许多先前的问题已经出现了相关问题(见下文),但所讨论的解决方案似乎都不适用于这种情况。有问题的应用程序使用ObservableCollection<T>
通过数据绑定驱动常规WPF ListBox
(带有一些自定义样式)。 ObservableCollection可能包含大量项目,但由于ListBox默认以虚拟方式运行,因此这不是问题。 (具有数十万件物品的“静态”表现非常值得尊敬。)
但是,如果集合已经包含大量项目,然后添加了更多的项目(通过ObservableCollection<T>.Add()
),由于所有NotifyCollectionChanged
事件被触发,UI不可避免地暂停一段时间。这里推荐的解决方法是对ObservableCollection进行子类化并实现一个AddRange()
方法,该方法将新元素添加到(protected)Items
集合中,然后使用NotifyCollectionChanged
调用NotifyCollectionChangedAction.Reset
。
根据我的经验,这解决了一个问题,但创造了另一个问题。虽然不再有大量的Add
事件被引发,但Reset
通知会导致ListBox重新评估整个集合,如果它包含数万个项目,则可能需要相当长的时间。这与在目标应用程序中可能经常添加大量项目的事实相结合,意味着应用程序长时间咀嚼大量CPU,同时ListBox消化所有新信息。
事后可以说,ObservableCollection<T>
不是向用户提供这么大的数据集的正确工具,但是到目前为止,很难看到一种简单的方法来重新架构应用程序来处理这个问题。
感谢收到的建议;提前谢谢了。
相关问题:
尽量不要直接绑定到你的ObservableCollection
,而是绑定到ICollectionView。
这样,在向OC添加项目时,您的UI不会受到影响,因为您的Items Control未绑定到您的OC,当您完成向OC添加项目时,您只需通过调用ICollectionView
更新您的CollectionViewSource.GetDefatultView()
,并使用新的UI更新您的UI OC州......
这是我如何做到这一点,我从来没有注意到任何UI问题(授予我从未尝试过数十万项,但我确实尝试了数千项,并没有注意到任何UI问题所以我很确定这将解决你的问题问题:
Task.Factory.StartNew(() => SessionList = serviceAgent.GetSessions(someId, someStartDate)).
ContinueWith(t => Sessions.Add(SessionList), TaskScheduler.FromCurrentSynchronizationContext()).
ContinueWith(t => SessionsView = CollectionViewSource.GetDefaultView(Sessions),
TaskScheduler.FromCurrentSynchronizationContext());
qazxsw poi是OC和qazxsw poi是qazxsw poi ...
这也带来了额外的好处。这将使用Sessions
在后台线程上获取记录,它将允许您以多种方式操作View,而不会影响实际的底层集合或必须对集合运行LINQ查询并显示生成的集合...
虽然这只是字符串,但我没有看到ListBox花费很长时间来消化新信息。不能只使用Notify或集合将脱离sych
此单击在大约0.4秒内添加100万行到100万行。 不只是添加行的时间 - UI在0.4秒内刷新。
如果在添加之前向下滚动,甚至可以将正确的项目放入视图中。
SessionsView
进一步的研究以及对启动行为的一些思考为这个问题找到了答案。
在启动时,应用程序能够在几秒钟内将200,000个项目读入ListBox,因此它应该能够轻松维持每秒100个附加项的更新速率。根本问题是Dispatcher的参与,因为新项目的创建是在一个单独的线程中进行的。所以改变这个代码:
ICollectionView
对此:
TPL
即迭代在调度程序中添加的项目而不是外部对性能产生巨大差异,这意味着我根本不需要调用重置操作。