检测 ListView 中滚动的结束

Detecting the end of scrolling in a ListView

通用Windows 8.1 在此处存储项目。

我想知道,当 ListView 在用户交互后停止滚动时。我在网上找到了很多信息,但没有一个在 WP 8.1 上可靠地工作的例子(WPF/WP8 例子没有多大帮助,而且有很多)。

这是我现在所做的。

1。列表视图

<ListView  
    x:Name="MessageList"
    ItemsSource="{Binding Messages}"  
    VerticalAlignment="Bottom"                
    ItemContainerStyle="{StaticResource ChatListViewItemStyle}" 
    PointerEntered="MessageList_OnPointerEntered"
    >
    <ListView.ItemTemplate>
        <DataTemplate>
            <messages:MessageContainer />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

2。滚动查看器

我在后面的代码中从 ListView 获得了一个 ScrollViewer 引用。

// GetChildElement<T>(this DependencyObject root) is a simple extension method of mine
Scroll = MessageList.GetChildElement<ScrollViewer>();

3。 ListViewer.PointerEntered 和 ScrollViewer.ViewChanged

PointerEntered 处理程序用于检测用户交互的开始。当检测到交互时,我订阅 Scroll.ViewChanged 并使用事件的 IsIntermediate 标志来检测列表何时停止滚动(包括惯性)。

void MessageList_OnPointerEntered(object sender, PointerRoutedEventArgs e)
{
    Debug.WriteLine("START MONITORING INTERACTION");
    Scroll.ViewChanged += OnViewChangedByUser;
}

void OnViewChangedByUser(object sender, ScrollViewerViewChangedEventArgs e)
{
  Debug.WriteLine("WAITING FOR INTERACTION TO END");
  if (!e.IsIntermediate) {
    Debug.WriteLine("INTERACTION ENDED");
    Scroll.ViewChanged -= OnViewChangedByUser;
  }
}

这在某种程度上确实有效。

问题

问题是,当列表滚动到 end/start 并且用户将其拉出边界并释放它时,ViewChanged 不会触发,导致它返回 return与惯性。因此,检测到交互开始,但未检测到结束。 ViewChanged 根本不会被触发——无论是 IsIntermediate=True 还是 False。

做我想做的事情的更好方法是什么?

遗憾的是,除了重复轮询和检查 ScrollOffset 之外,在 Windows 8.1 上没有什么好的方法可以做到这一点。

我只得到一个包含 10 个双精度数的数组,并且每秒 10 次我将移入当前的滚动偏移量。比在同一个处理程序中检查最后 5 个是否等于列表的末尾而不是引发事件。

正如 Tamás Deme 所说,没有什么好的方法可以完成所需的工作。但是,我找到了一种适用于我的情况的解决方法(虽然没有什么好处)。

事实上,我正在检测列表是否滚动到底部,当滚动停止时。它正在检测滚动结束是造成这么多麻烦的原因。

问题分为两部分:1 - 检测用户交互结束,2 - 检测惯性结束。令人惊讶的是,没有解决它们中任何一个的好方法。值得庆幸的是,我真正需要的只是知道滚动(用户驱动或惯性动画)停止时 VerticalOffset 的值。我实际上不必知道用户是否仍然持有该列表。

void MessageList_OnPointerEntered(object sender, PointerRoutedEventArgs e)
{
    IsScrolledToLastLine = false; // this is to signal, that the user is 
                                  // holding the list, and there must be no 
                                  // automatic scrolling, when content is 
                                  // added to it.
    Debug.WriteLine("[*]START MONITORING INTERACTION");
    Scroll.ViewChanged += OnViewChangedByUser;
    Scroll.LayoutUpdated += OnScrollLayoutUpdated;
}

void OnScrollLayoutUpdated(object sender, object e)
{
  // will trigger multiple times during scrolling 
  // AND  
  // will trigger when inertia finally stops 
  // (regardless of the changes of VerticalOffset)
  IsScrolledToLastLine = Scroll.ScrollableHeight == Scroll.VerticalOffset;
  Debug.WriteLine("Interaction progress: {0}", IsScrolledToLastLine);
}

void OnViewChangedByUser(object sender, ScrollViewerViewChangedEventArgs e)
{
  if (!e.IsIntermediate) {
    IsScrolledToLastLine = Scroll.ScrollableHeight == Scroll.VerticalOffset;
    Debug.WriteLine("Interaction end: {0}", IsScrolledToLastLine);
    Scroll.LayoutUpdated -= OnScrollLayoutUpdated;
    Scroll.ViewChanged -= OnViewChangedByUser;
  }
}

Scroll.LayoutUpdated

LayoutUpdated 在滚动期间被触发多次。与 ViewChanged 不同的是,在 post 图片中惯性停止时也会触发此事件。遗憾的是,在 LayoutUpdated 中无法确定列表是否完全停止滚动。

ViewChanged 当您实际通过滚动更改 VerticalOffset 时工作正常; LayoutUpdated涵盖过度滚动的情况。

但还有另一个问题:OnScrollLayoutUpdated 可能会在列表边缘滚动时保持订阅状态,因为 ViewChanged 不会触发。幸运的是,我可以忽略它,这不会破坏任何东西。