2014-01-12 20 views
1

我有大量的项目绑定到ListBox,其中VirtualizingStackPanel设置为ItemsPanel。当用户滚动并创建项目容器时,我会做一些工作来使用数据填充项目(使用数据库查询)。如果用户滚动速度非常快,则会产生大量请求,这些请求往往会让事情陷入瘫痪。我想要做的是检测项目何时在视口外滚动,以便我可以取消其相应的请求。如何可靠地检测项目何时在视图外滚动?

这里是迄今为止我已经试过的办法,为什么他们都没有奏效:

  1. 覆盖VirtualizingStackPanel.OnCleanUpVirtualizedItem。 的问题是,这个方法似乎比某个物品实际上离开屏幕时晚得多。在此方法中取消我的请求 并没有太大的好处,因为它发生得太晚了。

  2. 开启集装箱回收利用 VirtualizationMode.Recycling。此事件导致项目 容器的DataContext发生更改,但项目容器本身重复使用 。 DataContextChanged事件立即发生,因为其中一个 项目不在视图中,所以它在这方面很好。问题 是容器回收创造了很多副作用,并且在我的 测试中,总体上有点小问题。我宁愿不使用它。

是,有一个良好的较低水平的方法,如挂钩到布局事件,可以给我当一个项目开始的视野之外的一个确定性的答案吗?也许在ScrollViewer的水平?

回答

2

这里有一个粗略的解决方案,我想实现你在找什么。我通过监听XAML中加载的事件来获取虚拟化堆栈面板。如果我在生产代码中这样做,我可能会将其分解为可重用的附加行为,而不是在代码隐藏中投入大量代码。

注意,确实没有我们可以用在这里找到屏幕上的孩子们的属性,所以我们来看看比较给孩子确定哪些孩子是在屏幕上父母的布局界限。 代码使用为获得一个项目的视觉孩子在视觉树的扩展方法,包含如下:

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

此代码是用我测试了这一点的目的,创造了一个非常基本的视图模型的层次结构。我会将它只是在情况下,它有助于理解其他代码:

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> 
+0

谢谢Tim,这是一个很好的答案。我其实只是从VirtualizingStackPanel继承而来,并将代码移到那里。然后浇铸孩子以键入IDisposable,所以现在我需要做的是在我的虚拟机上实现IDisposable以使其工作。 – Charlie

-1

您可以尝试添加。
scrollviewer.veriticalscroll =“自动” 这将导致其只对项目的金额已滚动在列表框中

+0

我想你已经误解了我的问题。 – Charlie

0

在视图模型方面,你可以看连接,并从INotifyPropertyChanged的事件分离:

public event PropertyChangedEventHandler PropertyChanged 
{ 
    add 
    { 
     if(this.InternalPropertyChanged == null) 
      Console.WriteLine("COMING INTO VIEW"); 
     this.InternalPropertyChanged += value; 
    } 

    remove 
    { 
     this.InternalPropertyChanged -= value; 
     if(this.InternalPropertyChanged == null) 
      Console.WriteLine("OUT OF VIEW"); 
    } 
} 

private event PropertyChangedEventHandler InternalPropertyChanged; 

注意:如果没有VirtualizationMode.Recycling,ListBox可能会推迟容器的销毁(并因此分离),直到用户停止滚动。这可能会扩大内存消耗,尤其是如果ItemTemplate很复杂(并且不会取消数据库查询)。

相关问题