【发布时间】:2016-12-23 10:40:37
【问题描述】:
向下滚动时,Groove 将标题向上移动到可视区域之外,就像常规 ListView 标题一样。向上滚动时,它会立即将标题向下移动到可视区域,而不管当前的垂直滚动偏移量如何。标题似乎是 ListView 内容的一部分,因为滚动条包含标题。
这如何在 Windows 10 UWP 应用中实现?
【问题讨论】:
标签: win-universal-app uwp uwp-xaml
向下滚动时,Groove 将标题向上移动到可视区域之外,就像常规 ListView 标题一样。向上滚动时,它会立即将标题向下移动到可视区域,而不管当前的垂直滚动偏移量如何。标题似乎是 ListView 内容的一部分,因为滚动条包含标题。
这如何在 Windows 10 UWP 应用中实现?
【问题讨论】:
标签: win-universal-app uwp uwp-xaml
您可以利用ListView's 内部ScrollViewer's ViewChanged 事件来做到这一点。
首先你要获得内部ScrollViewer。这是最简单的版本,但您可能希望使用众多 VisualTreeHelper 扩展之一来更安全、更轻松地完成它:
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;
}
}
这是基本思想。您可能不想添加一些流畅的动画,而只是更改可见性。
【讨论】:
环顾四周并进行实验后,我现在可以回答我自己的问题了。
可以使用基于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 变量将驱动 header 的 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>
结果如下:
【讨论】: