代码之家  ›  专栏  ›  技术社区  ›  Jordan

如何将数据触发器链接到ScrollViewer是否可以向上滚动?

  •  2
  • Jordan  · 技术社区  · 14 年前

    我想在我的ScrollViewer的顶部和底部显示一个半不透明的箭头,如果它可以分别向上或向下滚动的话。我认为最好的选择是一个数据触发器,但我不确定我能把它也绑起来。我试图避免对ScrollViewer子类化,但如果必须的话,我会的。有什么想法吗?

    我正在使用.Net Framework 3.5(我希望我能升级!)。

    谢谢。:)

    1 回复  |  直到 8 年前
        1
  •  3
  •   Athari    14 年前

    可能的解决方案之一。它使用两个转换器来计算是否可以滚动。模板基于标准 ScrollViewer 模板,但有两个额外的文本块来显示信息(“箭头”)。

    窗口1.xaml

    <Window x:Class="WpfApplication1.Window1"
            Title="Window1" Height="300" Width="300"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:WpfApplication1="clr-namespace:WpfApplication1">
        <Window.Resources>
            <ResourceDictionary>
                <WpfApplication1:ScrollViewerCanScrollUpConverter x:Key="ScrollViewerCanScrollUpConverter" />
                <WpfApplication1:ScrollViewerCanScrollDownConverter x:Key="ScrollViewerCanScrollDownConverter" />
            </ResourceDictionary>
        </Window.Resources>
        <Grid>
            <ScrollViewer Background="Transparent">
                <ScrollViewer.Template>
                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                        <Grid x:Name="Grid" Background="{TemplateBinding Background}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Rectangle x:Name="Corner" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Column="1" Grid.Row="1"/>
                            <ScrollContentPresenter Margin="{TemplateBinding Padding}" x:Name="PART_ScrollContentPresenter" Grid.Column="0" Grid.Row="0" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/>
                            <ScrollBar Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_VerticalScrollBar" ViewportSize="{TemplateBinding ViewportHeight}" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="1" Grid.Row="0" AutomationProperties.AutomationId="VerticalScrollBar"/>
                            <ScrollBar Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_HorizontalScrollBar" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="0" Grid.Row="1" AutomationProperties.AutomationId="HorizontalScrollBar"/>
                            <TextBlock x:Name="PART_UpTextBlock" VerticalAlignment="Top" Text="Can scroll up" Visibility="Collapsed" />
                            <TextBlock x:Name="PART_DownTextBlock" VerticalAlignment="Bottom" Text="Can scroll down" Visibility="Collapsed" />
                        </Grid>
                        <ControlTemplate.Triggers>
                            <DataTrigger Value="True" Binding="{Binding Path=VerticalOffset, RelativeSource={RelativeSource Self}, Converter={StaticResource ScrollViewerCanScrollUpConverter}}">
                                <Setter TargetName="PART_UpTextBlock" Property="Visibility" Value="Visible" />
                            </DataTrigger>
                            <DataTrigger Value="True">
                                <DataTrigger.Binding>
                                    <MultiBinding Converter="{StaticResource ScrollViewerCanScrollDownConverter}">
                                        <Binding Path="VerticalOffset" RelativeSource="{RelativeSource Self}" />
                                        <Binding Path="ExtentHeight" RelativeSource="{RelativeSource Self}" />
                                        <Binding Path="ViewportHeight" RelativeSource="{RelativeSource Self}" />
                                    </MultiBinding>
                                </DataTrigger.Binding>
                                <Setter TargetName="PART_DownTextBlock" Property="Visibility" Value="Visible" />
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </ScrollViewer.Template>
                <Border Margin="10" Height="400" Background="Yellow">
                    <TextBlock Text="Content" />
                </Border>
            </ScrollViewer>
        </Grid>
    </Window>
    

    using System;
    using System.Globalization;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
        public partial class Window1
        {
            public const double Epsilon = 0.001;
    
            public Window1()
            {
                InitializeComponent();
            }
        }
    
        public class ScrollViewerCanScrollUpConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Math.Abs((double)value) > Window1.Epsilon;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
        public class ScrollViewerCanScrollDownConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                double verticalOffset = (double)values[0];
                double extentHeight = (double)values[1];
                double viewportHeight = (double)values[2];
                double maxOffset = Math.Max(0.0, extentHeight - viewportHeight);
                return verticalOffset < maxOffset - Window1.Epsilon;
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }