【问题标题】:WPF data virtualization issuesWPF 数据虚拟化问题
【发布时间】:2011-10-20 00:48:03
【问题描述】:

我需要在 WPF ListBox 中显示大约几百万个项目(我知道这是多么糟糕的主意,但我必须这样做)。新项目以非常快的速度到达,每秒数千个,但除此之外没有对列表进行任何更改:项目永远不会被删除或修改。 我需要每秒至少刷新一次 LisBox 控件。

我知道我在 WPF 中“免费”获得 UI 虚拟化(我在 Recycle 模式和延迟滚动中使用 VirtualizedStackPanel),但我必须虚拟化数据。我对 WPF 完全陌生,所以我尝试利用我发现的最好的 existing solution。它可以工作,但每次刷新计数时都会闪烁,并且每次重新加载时都会丢失 SelectedItem(我认为这是因为它触发了重新加载整个集合的“重置”类型的 CollectionChanged)。我尝试使用“添加”事件而不是“重置”,但它需要实际添加到集合中的项目列表,并且每秒获取数千个对象只是为了将它们传递给事件并没有任何意义并扔掉它们,因为它们无论如何都是虚拟化的。我还尝试为Count 属性触发PropertyChanged,以便ListBox 更新它的索引范围/调整滚动条,奇怪的事情开始发生:滚动条会调整到新的计数,尽管列表项不会显示并且仍然有很多闪烁。

换句话说:我如何通知 ListBox 控件 N 新项目已添加到绑定集合中,以便 ListBox 只会调整滚动条范围(并且不会问我对于添加的项目,直到它们实际显示)。

我在这个项目中使用 .NET 4.0 和 Caliburn.Micro,但我怀疑这会影响潜在的解决方案。

【问题讨论】:

    标签: .net wpf mvvm .net-4.0


    【解决方案1】:

    您需要创建一个自定义集合来实现非泛型 IList 接口以及泛型 IList。如果你这样做,那么列表框控件不会枚举集合,而是使用 this[] 接口,它让你有机会在用户滚动时以分页样式自动加载行。

    这是我在博客上发布的类似解决方案: http://www.deanchalk.me.uk/post/WPF-e28093-DataContext-Virtualization-With-Paged-Services.aspx

    【讨论】:

    • 此解决方案与我之前尝试过的问题类似。滚动范围扩大,但项目仅被枚举(通过GetEnumerator)一次,并且在第一批加载的项目下方滚动只会将滚动恢复到第一个项目(而不是获取新项目)。我可以以某种方式强制重新枚举我的收藏吗?
    • 如果您实现 IList(非泛型),Listbox 将仅调用 GetEnumerator 来枚举整个集合中的第一项。它将专门使用 Count 和 this[] 索引器,这使您能够实现简单的虚拟化
    • 确实! (虽然当我的列表同时实现IListIList<T> 时它的工作方式有所不同——太奇怪了。)但我仍然对Count 有问题(和以前一样),我正在实现INotifyPropertyChanged 但我的PropertyChanged事件总是null。 :
    • 再看看你的实现,这是一个绝对有效的沼泽标准解决方案。你的列表框在你的集合类上喊叫 Count()。
    【解决方案2】:

    我遇到过类似的情况,其中我的缓存对象列表(以百万计)被绑定到跨应用程序的多个 ListBox(加载在各个页面上),并且当缓存项目将被添加、删除或编辑时,我必须维护选择并在不滚动或选择闪烁的情况下更新整个应用程序的更改。

    我是这样实现的……

    1. 我有一个线程安全的ObservableCollection,名为FastObservableCollection ... Updating an ObservableCollection in a separate thread

    2. 我在上面的 FastObservaleCollection 中添加了对 AddRange() 的支持,用于在没有 CollectionChanged 通知的情况下进行批量插入。 CollectionChanged 通知只会针对从批量添加的最后一项触发一次.... http://peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/

    3. 使用线程安全CollectionViews。您可以自定义集合视图并通过引发自定义“刷新”事件在正确的线程上调度 SourceCollection.CollectionChanged 事件。

    此链接提供已调度集合更改通知的指南...Where do I get a thread-safe CollectionView?

    1. 我将 ListBoxes 更改为使用 SelectedValue 绑定而不是 SelectedItem 绑定,并确保 SelectedValue 是原始类型(值类型)。这保持了按值选择。

    2. 当 ListBox ItemsSource(即从 CollectionView 引发的自定义刷新事件)通过附加行为处理时,我会调用 ListBox.SelectedValue 的绑定表达式 UpdateSource()UpdateTarget()

    现在它像魅力一样工作,没有任何闪烁,并且还保持选择。

    【讨论】:

    • 您能否详细说明第 2 点和第 3 点?我的收藏被另一个进程更新,我只有两个方法fetchItems(begin, end)getCount() 的接口,所以我看不到AddRange 可以如何帮助我,或者我可能误解了一些东西。关于“SourceCollection.CollectionChanged”——您所说的正确是指哪个线程?我的自定义“刷新”事件将在哪里处理?我不确定你在开什么车。
    猜你喜欢
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    相关资源
    最近更新 更多