WPF - 滚动ScrollViewer时设置图像可见性

时间:2015-02-23 15:22:51

标签: wpf xaml

我在WPF用户控件上有一个ScrollViewer,而我想要实现的是在ScrollViewer的顶部和底部显示阴影图像,但是当隐藏时,隐藏顶部阴影滚动条位于顶部,当滚动条位于底部时隐藏底部阴影。

换句话说,我需要以某种方式将图像的Visibility属性绑定到ScrollViewer的偏移量。以下代码显然不对,但应该说明我正在尝试做什么。

非常感谢任何帮助。

<Grid>
    <Image Source="Shadow.png" VerticalAlignment="Top">
        <Image.Resources>
            <Style TargetType="Image">
                <Style.Triggers>
                    <Trigger SourceName="Scroller" Property="VerticalOffset" Value="GREATER THAN ZERO OR LESS THAN MAX">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Image.Resources>
    </Image>
    <ScrollViewer Height="200" x:Name="Scroller">
        <ContentControl />
    </ScrollViewer>
</Grid>

2 个答案:

答案 0 :(得分:2)

以下是我要做的事情:

首先,您需要一个IMultiValueConverter:

public class ScrollOffsetToVisibilityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null)
            throw new ArgumentException("Values cannot be null.");
        if (values.Count() != 2)
            throw new ArgumentException("Incorrect number of bindings (" + values.Count() + ")");
        if (parameter == null)
            throw new ArgumentException("Parameter cannot be null.");

        var top = parameter.ToString().ToUpper() == "TOP";

        var offset = Double.Parse(values[0].ToString());
        var maxHeight = Double.Parse(values[1].ToString());

        return (top && offset == 0) || (!top && offset == maxHeight) ? Visibility.Visible : Visibility.Collapsed;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后,您可以使用此转换器为图像的Visibility属性应用Setter。

<Image.Style Source="Shadow.png" VerticalAlignment="Top">
    <Style>
        <Setter Property="Image.Visibility">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource ScrollOffsetToVisibilityConverter}" ConverterParameter="Top">
                    <Binding ElementName="Scroller" Path="VerticalOffset"/>
                    <Binding ElementName="Scroller" Path="ScrollableHeight"/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>
</Image.Style>

如果滚动条位于顶部或底部,只需传入“顶部”或“底部”(或更准确地说,不是“顶部”),以使ConverterParameter返回“Visible”。

答案 1 :(得分:0)

您必须使用ScrollBar而不是ScrollViewer,因为ScrollViewer无法判断它何时到达其内容的末尾。 (或至少我没有看到这样做的可靠方法)。另一方面ScrollBar更适合这类操作。

我使用带有几个项目的ListBox来测试这种行为。你可以将它改成你需要的任何东西。

Xaml:

<Grid>
    <ListBox ScrollBar.Scroll="ListBox_Scroll">
        <!-- add many items here -->
        <TextBlock Text="something"/>
    </ListBox>

    <Image Source="Shadow.png" VerticalAlignment="Top">
        <Image.Style>
            <Style TargetType="Image">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ScrollerState}" 
                            Value="{x:Static enum:ScrollState.AtTheTop}">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Image.Style>
    </Image>
    <Image Source="Shadow.png" VerticalAlignment="Bottom">
        <Image.Style>
            <Style TargetType="Image">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ScrollerState}" 
                          Value="{x:Static enum:ScrollState.AtTheBottom}">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Image.Style>
    </Image>
</Grid>

正如您所看到的,我将触发器更改为DataTrigger并在DataContext中使用了一个可绑定属性(名为ScrollerState,类型为ScrollState,这是一个简单的枚举)来绑定。 / p>

枚举:

public enum ScrollState
{
    AtTheTop, AtTheBottom, InBetween
}

现在在Code中我们实现了Scroll事件,我们更改了ScrollerState的值:

public MainWindow()
{
    InitializeComponent();
    DataContext = new VM();
}

private void ListBox_Scroll(object sender, ScrollEventArgs e)
{
    ScrollBar sb = e.OriginalSource as ScrollBar;

    if (sb.Orientation == Orientation.Horizontal)
        return;

    if (sb.Value == 0)
        (DataContext as VM).ScrollerState = ScrollState.AtTheTop;
    else if (sb.Value == sb.Maximum)
        (DataContext as VM).ScrollerState = ScrollState.AtTheBottom;
    else
        (DataContext as VM).ScrollerState = ScrollState.InBetween;
}

VM(此实例设置为窗口的DataContext):

public class VM : DependencyObject
{
    public ScrollState ScrollerState
    {
        get { return (ScrollState)GetValue(ScrollerStateProperty); }
        set { SetValue(ScrollerStateProperty, value); }
    }
    public static readonly DependencyProperty ScrollerStateProperty =
        DependencyProperty.Register("ScrollerState", typeof(ScrollState), typeof(VM), 
        new PropertyMetadata(ScrollState.AtTheTop));
}