如何在不锁定GUI的情况下将数千个项目添加到绑定集合中

时间:2022-09-12 08:44:16

I have a setup where potentially thousands of items (think 3000-5000) will be added to an ObservableCollection that 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 DispatcherTimer but 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( ))预先生成项目,在后台线程中,可能会减少添加它们所需的时间吗?

Edit: Virtualization is not possible. The requirements specify a WrapPanel look, so the display is actually a ListBox which has a templated ItemsPanel

编辑:虚拟化是不可能的。要求指定了WrapPanel外观,因此显示实际上是一个具有模板化ItemsPanel的ListBox

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 CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.

编辑3:所以答案在一个地方 - 我通过创建一个继承自ObservableCollection的类来解决这个问题(在下面的帮助下)。这个类做了两件事 - 公开了一次添加集合的方法,并添加了抑制CollectionChanged事件的能力。通过这些更改,添加3000个项目所需的时间大约为.4秒(97%的改进)。此链接详细说明了这些更改的实现。

5 个解决方案

#1


7  

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 collection没有AddRange,要么使用不同的集合,要么编写自己的集合,只需一个包装器即可。目标是不要在每一个Add()中提出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?

当然,在屏幕上显示的那些项目中,您也可以自己进行渲染。如果您的ItemTemplates很复杂,那么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 ..如果这不正确则更新问题

#2


8  

WPF Binding supports concurrency for this reason. Try setting Binding.IsAsync to true. In addition.

由于这个原因,WPF Binding支持并发。尝试将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 like List<T> and raise your property change notification after all your items are added.
  • 不要使用ObservableCollection ,它的速度很慢,因为每次添加一个项目都会引发事件。使用比List 更快的东西,并在添加所有项目后提出属性更改通知。
  • 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.
  • 检查所涉及的代码的其他部分,看看是否有膨胀和修剪。

#3


4  

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

#4


4  

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 CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.

根据要求,这是我解决这个问题的方法。我开始创建一个继承自ObservableCollection的类。这个类做了两件事 - 公开一个方法来一次添加整个集合,并添加了抑制CollectionChanged事件的能力。通过这些更改,添加3000个项目所需的时间大约为.4秒(97%的改进)。此链接详细说明了这些更改的实现。

#5


0  

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提高性能,从而只创建可见项

#1


7  

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 collection没有AddRange,要么使用不同的集合,要么编写自己的集合,只需一个包装器即可。目标是不要在每一个Add()中提出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?

当然,在屏幕上显示的那些项目中,您也可以自己进行渲染。如果您的ItemTemplates很复杂,那么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 ..如果这不正确则更新问题

#2


8  

WPF Binding supports concurrency for this reason. Try setting Binding.IsAsync to true. In addition.

由于这个原因,WPF Binding支持并发。尝试将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 like List<T> and raise your property change notification after all your items are added.
  • 不要使用ObservableCollection ,它的速度很慢,因为每次添加一个项目都会引发事件。使用比List 更快的东西,并在添加所有项目后提出属性更改通知。
  • 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.
  • 检查所涉及的代码的其他部分,看看是否有膨胀和修剪。

#3


4  

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

#4


4  

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 CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.

根据要求,这是我解决这个问题的方法。我开始创建一个继承自ObservableCollection的类。这个类做了两件事 - 公开一个方法来一次添加整个集合,并添加了抑制CollectionChanged事件的能力。通过这些更改,添加3000个项目所需的时间大约为.4秒(97%的改进)。此链接详细说明了这些更改的实现。

#5


0  

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提高性能,从而只创建可见项