gpt4 book ai didi

c# - 如何可靠地检测项目何时滚动到 View 之外?

转载 作者:行者123 更新时间:2023-11-30 22:10:07 26 4
gpt4 key购买 nike

我有大量项目绑定(bind)到 ListBoxVirtualizingStackPanel 设置为它的 ItemsPanel。随着用户滚动和项目容器的创建,我做了一些工作来用数据填充项目(使用数据库查询)。如果用户滚动得非常快,它会建立大量的请求,这些请求往往会使事情陷入困境。我想做的是检测项目何时滚动到视口(viewport)之外,这样我就可以取消其相应的请求。

以下是我迄今为止尝试过的方法,以及它们为何不起作用:

  1. 覆盖 VirtualizingStackPanel.OnCleanUpVirtualizedItem .这问题是这个方法似乎在很久以后的某个时候被调用比当项目实际离开屏幕时。取消我的请求在这个方法中并没有多大用处,因为它发生得太晚了。

  2. 启用容器回收 VirtualizationMode.Recycling .此事件导致项目容器的 DataContext 改变但项目容器本身是重用。 DataContextChanged 事件作为一个事件立即发生项目超出视野,因此在这方面很好。问题容器回收会产生很多副作用吗?测试,总体来说有点问题。我不想使用它。

是否有良好的底层方法(例如 Hook 到布局事件)可以在项目超出 View 时给我一个确定性的答案?也许在 ScrollViewer 级别?

最佳答案

这是一个粗略的解决方案,我认为它可以满足您的需求。我通过监听 XAML 中的加载事件来获取虚拟化堆栈面板。如果我在生产代码中这样做,我可能会将其纳入可重用的附加行为中,而不是在代码隐藏中抛出一堆代码。

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;
}
}

请注意,我们实际上并没有真正可以在此处使用的属性来查找屏幕上的子项,因此我们通过比较父项与子项的布局边界来确定哪些子项在屏幕上。该代码使用扩展方法获取可视化树中项目的可视化子项,如下所示:

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>();
}
}

这段代码使用了一个非常基本的 View 模型层次结构,我创建它是为了测试它。我将包含它以防万一它有助于理解其他代码:

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>

关于c# - 如何可靠地检测项目何时滚动到 View 之外?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21081402/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com