如何可靠地检测何时将项目滚动到视图之外? [英] How to reliably detect when an item is scrolled outside of view?

查看:93
本文介绍了如何可靠地检测何时将项目滚动到视图之外?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有很多绑定到ListBox的项目,并且VirtualizingStackPanel被设置为其ItemsPanel.当用户滚动并创建项目容器时,我做了一些工作以使用数据填充项目(使用数据库查询).如果用户滚动非常快,则会建立大量的请求,这些请求往往会使事情陷入困境.我想做的是检测何时将项目滚动到视口之外,以便取消其相应的请求.

I have a large collection of items bound to a ListBox, with a VirtualizingStackPanel set as its ItemsPanel. As the user scrolls and item containers are created, I do some work to populate the item with data (using a database query). If the user scrolls very rapidly, it builds up a large number of requests that tend to bog things down. What I would like to do is detect when the item is scrolled outside of the viewport, so I can cancel its corresponding request.

以下是我到目前为止尝试过的方法,以及为什么它们没有起作用:

Here are the approaches I've tried thus far, and why they have not worked:

  1. 覆盖 VirtualizingStackPanel.OnCleanUpVirtualizedItem .这 问题是此方法似乎在以后的某个时间被调用 比该项目实际不在屏幕上的时间要多.取消我的要求 在这种方法中效果不佳,因为它发生得太晚了.

  1. Override VirtualizingStackPanel.OnCleanUpVirtualizedItem. The problem is that this method seems to be called sometime much later than when the item actually goes off-screen. Cancelling my request within this method doesn't do much good because it occurs so late.

使用以下命令打开容器回收 VirtualizationMode.Recycling .此事件导致该项目 容器的DataContext进行更改,但项目容器本身是 重用. DataContextChanged事件作为一个立即发生 项目不在视野范围内,因此在这方面很好.问题 是容器回收会产生很多副作用,在我看来 测试,总体来说有点麻烦.我不希望使用它.

Turn on container recycling with VirtualizationMode.Recycling. This event causes the item container's DataContext to change but the item container itself is reused. The DataContextChanged event occurs immediately as one item goes outside of view, so it is good in that regard. The problem is that container recycling creates a lot of side-effects and, in my testing, is a little buggy overall. I would prefer not to use it.

是否有一种很好的底层方法,例如挂接到布局事件,该方法可以为我确定项目何时超出视野的答案?也许是在ScrollViewer级别?

Is there are a good lower-level approach, such as hooking into layout events, that can give me a deterministic answer on when an item goes outside of view? Perhaps at the ScrollViewer level?

推荐答案

我认为这是一个粗略的解决方案,可以满足您的需求.我通过侦听XAML中的已加载事件来获取虚拟化堆栈面板.如果我在生产代码中执行此操作,则可能会将其分解为可重用的附加行为,而不是在后面的代码中抛出一堆代码.

Here's a rough solution that I think accomplishes what you're looking for. I'm getting the virtualizing stack panel by listening to the loaded event in the XAML. If I were doing this in production code, I might factor this into a reusable attached behavior rather than throwing a bunch of code in the code-behind.

public partial class MainWindow
{
    private VirtualizingStackPanel _panel;
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MyViewModel();
    }

    private IList<ChildViewModel> _snapshot = new List<ChildViewModel>();

    private void OnPanelLoaded(object sender, RoutedEventArgs eventArgs)
    {
        _panel = (VirtualizingStackPanel)sender;
        UpdateSnapshot();
        _panel.ScrollOwner.ScrollChanged += (s,e) => UpdateSnapshot();
    }

    private void UpdateSnapshot()
    {
        var layoutBounds = LayoutInformation.GetLayoutSlot(_panel);

        var onScreenChildren = 
            (from visualChild in _panel.GetChildren()
             let childBounds = LayoutInformation.GetLayoutSlot(visualChild)
             where layoutBounds.Contains(childBounds) || layoutBounds.IntersectsWith(childBounds)
             select visualChild.DataContext).Cast<ChildViewModel>().ToList();            

        foreach (var removed in _snapshot.Except(onScreenChildren))
        {
            // TODO: Cancel pending calculations.
            Console.WriteLine("{0} was removed.", removed.Value);
        }
        _snapshot = onScreenChildren;
    }
}

请注意,这里并没有真正可以使用的属性来查找屏幕上的子项,因此我们比较父项与子项的布局范围,以确定哪些子项在屏幕上. 该代码使用一种扩展方法来获取可视化树中某个项目的可视化子级,如下所示:

Notice that there isn't really a property we can use here to find the on-screen children, so we look at the layout bounds of the parent compared to the children to determine which children are on screen. The code uses an extension method for getting the visual children of an item in the visual tree, included below:

public static class MyVisualTreeHelpers
{
    public static IEnumerable<FrameworkElement> GetChildren(this DependencyObject dependencyObject)
    {
        var numberOfChildren = VisualTreeHelper.GetChildrenCount(dependencyObject);
        return (from index in Enumerable.Range(0, numberOfChildren)
                select VisualTreeHelper.GetChild(dependencyObject, index)).Cast<FrameworkElement>();
    }
}

此代码使用的是我创建的非常基本的视图模型层次结构,用于进行测试.我将包括它,以防它有助于理解其他代码:

This code is using a very basic view model hierarchy I created for the purposes of testing this out. I'll include it just in case it's helpful in understanding the other code:

public class MyViewModel
{
    public MyViewModel()
    {
        Children = new ObservableCollection<ChildViewModel>(GenerateChildren());
    }

    public ObservableCollection<ChildViewModel> Children { get; set; }

    private static IEnumerable<ChildViewModel> GenerateChildren()
    {
        return from value in Enumerable.Range(1, 1000)
               select new ChildViewModel {Value = value};
    }
}

public class ChildViewModel
{
    public int Value { get; set; }
}

XAML:

<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfTest="clr-namespace:WpfTest"
        Title="MainWindow" Height="500" Width="500">
    <ListBox ItemsSource="{Binding Children}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel Loaded="OnPanelLoaded" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>

        <ListBox.ItemTemplate>
            <DataTemplate DataType="wpfTest:ChildViewModel">
                <TextBlock Text="{Binding Value}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

这篇关于如何可靠地检测何时将项目滚动到视图之外?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆