带有Groove的ListView,如快速返回标头

时间:2016-08-16 22:16:07

标签: win-universal-app uwp uwp-xaml

当向下滚动时,Groove将标题向上移动到可视区域之外,就像常规ListView标题一样。向后滚动时,无论当前的垂直滚动偏移如何,它都会立即将标题向下移回可视区域。标题似乎是ListView内容的一部分,因为滚动条包含标题。

如何在Windows 10 UWP应用程序中实现?

Quick return header demo

2 个答案:

答案 0 :(得分:1)

您可以使用ListView's内部ScrollViewer's ViewChanged事件来执行此操作。

首先,你必须获得内部ScrollViewer。这是最简单的版本,但您可能希望使用众多VisualTreeHelper Extensions中的一个来更安全,更轻松地使用它:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    var border = VisualTreeHelper.GetChild(MyListView, 0);
    var scrollviewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;

    scrollviewer.ViewChanged += Scrollviewer_ViewChanged;
}

在EventHandler中,您可以根据滚动方向更改标题的可见性。

private void Scrollviewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    var sv = sender as ScrollViewer;

    if (sv.VerticalOffset > _lastVerticalOffset)
    {
        MyHeader.Visibility = Visibility.Collapsed;
    }
    else
    {
        MyHeader.Visibility = Visibility.Visible;
    }
}

这是基本的想法。您可能无法添加一些流畅的动画,而不仅仅是更改可见性。

答案 1 :(得分:0)

看了一下实验后,我现在可以回答我自己的问题了。

可以使用基于composition animation的表达式来调整与滚动相关的标题的Y偏移量。这个想法是基于这个answer。我在GitHub上准备了一个完整的工作示例。

动画是在ListView的SizeChanged事件中准备的:

ScrollViewer scrollViewer = null;
private double previousVerticalScrollOffset = 0.0;
private CompositionPropertySet scrollProperties;
private CompositionPropertySet animationProperties;

SizeChanged += (sender, args) =>
{
    if (scrollProperties == null)
        scrollProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);

    var compositor = scrollProperties.Compositor;

    if (animationProperties == null)
    {
        animationProperties = compositor.CreatePropertySet();
        animationProperties.InsertScalar("OffsetY", 0.0f);
    }

    var expressionAnimation = compositor.CreateExpressionAnimation("animationProperties.OffsetY - ScrollingProperties.Translation.Y");

    expressionAnimation.SetReferenceParameter("ScrollingProperties", scrollProperties);
    expressionAnimation.SetReferenceParameter("animationProperties", animationProperties);

    var headerVisual = ElementCompositionPreview.GetElementVisual((UIElement)Header);
    headerVisual.StartAnimation("Offset.Y", expressionAnimation);
};

animationProperties中的OffsetY变量将驱动标题的OffsetY属性的动画。 OffsetY变量在ScrollViewer的ViewChanged事件中更新:

scrollViewer.ViewChanged += (sender, args) =>
{
    float oldOffsetY = 0.0f;
    animationProperties.TryGetScalar("OffsetY", out oldOffsetY);

    var delta = scrollViewer.VerticalOffset - previousVerticalScrollOffset;
    previousVerticalScrollOffset = scrollViewer.VerticalOffset;

    var newOffsetY = oldOffsetY - (float)delta;

    // Keep values within negativ header size and 0
    FrameworkElement header = (FrameworkElement)Header;
    newOffsetY = Math.Max((float)-header.ActualHeight, newOffsetY);
    newOffsetY = Math.Min(0, newOffsetY);

    if (oldOffsetY != newOffsetY)
        animationProperties.InsertScalar("OffsetY", newOffsetY);
};

虽然这样可以正确设置动画,但标题不会堆叠在ListView项目的顶部。因此,拼图的最后一部分是减少ListView的ItemsPanelTemplate的ZIndex:

<ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <ItemsStackPanel Canvas.ZIndex="-1" />
    </ItemsPanelTemplate>
</ListView.ItemsPanel>

结果就是这样:

Quick return header demo