wpf 如何在不锁定 GUI 的情况下将数千个项目添加到绑定集合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11958375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add thousands of items to a binded collection without locking GUI
提问by steveg89
I have a setup where potentially thousands of items (think 3000-5000) will be added to an ObservableCollectionthat is binded to some visual interface. Currently, the process of adding them is quite slow (approx. 4 seconds/1000 items), and of course the GUI is unresponsive during that time. What is a good method to handle moving that many items at once into a collection without worrying about the system locking up? I've looked at DispatcherTimerbut I'm not sure if it will provide everything I need it to.
我有一个设置,其中可能有数以千计的项目(想想 3000-5000)将被添加到ObservableCollection绑定到某个可视化界面的一个。目前,添加它们的过程非常缓慢(大约 4 秒/1000 个项目),当然在此期间 GUI 没有响应。处理一次将这么多项目移动到集合中而不担心系统锁定的好方法是什么?我已经看过了,DispatcherTimer但我不确定它是否会提供我需要的一切。
Another question - Is there something I can do to speed up the creation of these objects so that it doesn't take so long to add them to the collection? Currently I use them like so: Collection.Add(new Item(<params>))Would generating the items beforehand, in a background thread probably, decrease the time it takes to add them by a noticeable amount?
另一个问题 - 我可以做些什么来加速这些对象的创建,以便将它们添加到集合中不需要很长时间?目前我是这样使用它们的:是否Collection.Add(new Item(<params>))会在后台线程中预先生成项目,从而显着减少添加它们所需的时间?
Edit: Virtualization is not possible. The requirements specify a WrapPanellook, so the display is actually a ListBoxwhich has a templated ItemsPanel
编辑:虚拟化是不可能的。需求指定了一个WrapPanel外观,所以显示实际上是一个ListBox具有模板化的 ItemsPanel
Edit2: According to the stopwatch, the bottleneck is actually putting items into my ObservableCollection. I will try changing that collection type and doing my own notification to see if that speeds it up substantially.
Edit2:根据秒表,瓶颈实际上是将项目放入我的ObservableCollection. 我将尝试更改该集合类型并执行我自己的通知,以查看是否会显着加快速度。
Edit3: So the answer is in one place - I solved this issue (with help from below) by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add collections at one time, and added the ability to suppress the CollectionChangedEvent. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). Thislink details the implementation of these changes.
Edit3:所以答案就在一个地方 - 我通过创建一个继承自ObservableCollection. 这个类做了两件事 - 公开一个方法来一次添加集合,并添加了抑制CollectionChanged事件的能力。通过这些更改,添加 3000 个项目所需的时间大约为 0.4 秒(改进了 97%)。此链接详细介绍了这些更改的实施。
采纳答案by quetzalcoatl
You've said 1000, so I'll stick to that number just for instance.
你说的是 1000,所以我会坚持这个数字。
IIRC, the observable collection has a small drawback - if you add the items one by one, it raises notifies once per each item. That means that you have 1000 notifications for 1000 of items and the UI thread will run at deadly speed just to keep up with redrawing the screen.
IIRC,可观察集合有一个小缺点——如果你一个一个地添加项目,它会为每个项目引发一次通知。这意味着您有 1000 条通知,用于 1000 个项目,并且 UI 线程将以致命的速度运行,只是为了跟上重绘屏幕的速度。
Do you need to redraw ASAP? Maybe you can batch the additions? Split the 1000 of items into a few packed of 100 items, or a little more packets of 50 or 20 items. Then, instead of putting all items one by one, put them in packets. But beware: you have to use some methods like AddRange implemented by the collection it self, not by LINQ, or else you will again have one-by-one insertion. If you find such method, it should cut the number of events significantly, because the collection should raise the Changed event only once per AddRange call.
你需要尽快重新绘制吗?也许你可以批量添加?将 1000 件物品分成几包 100 件物品,或多一点的 50 件或 20 件物品。然后,不是将所有物品一个一个地放入,而是将它们放入包中。但请注意:您必须使用一些方法,例如由集合本身实现的 AddRange,而不是 LINQ,否则您将再次进行一对一插入。如果您找到这样的方法,它应该会显着减少事件数量,因为每次 AddRange 调用时,集合应该只引发一次 Changed 事件。
If observable collection does not have AddRange, either use different collection, or write your own, just a wrapper will probably be sufficient. The goal is to NOT raise Changed event at every single Add(), but after a reasonable count of them, or - maybe just skip raising Changed when items are added and raise Changed at some regular time intervals? This would be beneficial especially, if your data "flows in" indefinitely at a constant rate.
如果 observable 集合没有 AddRange,要么使用不同的集合,要么编写自己的集合,只需一个包装器就足够了。目标是不在每个 Add() 中引发 Changed 事件,而是在对它们进行合理计数之后,或者 - 可能只是在添加项目时跳过引发 Changed 并定期引发 Changed 事件?如果您的数据以恒定速率无限期地“流入”,这将尤其有益。
Of course, at that number of items coming onto the screen, you may just as well be held at the rendering it self. If your ItemTemplates are complicated, a 1000 of objects times 1000 of instances of visual layers/properties may simply kill the user experience. Have you simplified the ItemTemplates to the bare minimum?
当然,当出现在屏幕上的项目数量如此之多时,您也可能会被自己渲染。如果您的 ItemTemplate 很复杂,则 1000 个对象乘以 1000 个可视层/属性实例可能会扼杀用户体验。您是否已将 ItemTemplates 简化到最低限度?
Last thing: consider using virtualizing StackPanels as the ItemPanels in your ItemsControl/ListBoxes. It can greatly reduce the memory footprint and the number of items drawn at a single point of time. This will not necessarily help in the number or events raised, but it may help greatly when you have complex item templates!
最后一件事:考虑使用虚拟化 StackPanels 作为 ItemsControl/ListBoxes 中的 ItemPanels。它可以大大减少内存占用和单个时间点绘制的项目数量。这不一定对引发的数量或事件有帮助,但是当您拥有复杂的项目模板时,它可能会大有帮助!
Edit: you are using ObservableCollection, so I've assumed WPF/Silverlight.. update the question if this is not correct
编辑:您正在使用 ObservableCollection,所以我假设 WPF/Silverlight .. 如果这不正确,请更新问题
回答by David Anderson
WPF Bindingsupports concurrency for this reason. Try setting Binding.IsAsyncto true. In addition.
Binding由于这个原因,WPF支持并发。尝试设置Binding.IsAsync为true。此外。
- Don't use
ObservableCollection<T>, it is slow for this because each time an item is added it raises events. Use something faster likeList<T>and raise your property change notification afterall your items are added. - Pre-create your items in a background thread, then push them into your collection.
- Check other parts of code involved to see if there is bloat, and trim.
- 不要使用
ObservableCollection<T>,这样做很慢,因为每次添加项目时都会引发事件。使用更快的方法,List<T>并在添加所有项目后提高您的属性更改通知。 - 在后台线程中预先创建您的项目,然后将它们推送到您的收藏中。
- 检查涉及的其他代码部分,看看是否有膨胀,并进行修剪。
回答by Volma
Another thing you can try: subclass ObservableCollection and make it support bulk loading (AddRange). Here is an article: AddRange and ObservableCollection
您可以尝试的另一件事是:子类 ObservableCollection 并使其支持批量加载(AddRange)。这是一篇文章: AddRange 和 ObservableCollection
回答by steveg89
By request, here is how I solved this issue. I started by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add entire collections at once, and added the ability to suppress the CollectionChangedEvent. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). Thislink details the implementation of these changes.
根据要求,这是我解决此问题的方法。我首先创建了一个继承自ObservableCollection. 这个类做了两件事 - 公开一个方法来一次添加整个集合,并添加了抑制CollectionChanged事件的能力。通过这些更改,添加 3000 个项目所需的时间大约为 0.4 秒(改进了 97%)。此链接详细介绍了这些更改的实施。
回答by Hassan Boutougha
for second question if in your GUI you are using WPF technologie, you can increase performance using VirualizingStackPanel allowing you to create only visible items
对于第二个问题,如果在您的 GUI 中您使用的是 WPF 技术,您可以使用 VirualizingStackPanel 提高性能,允许您仅创建可见项目

