How to add thousands of items to a binded collection without locking GUI

前端 未结 5 1822
深忆病人
深忆病人 2021-01-31 10:25

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, t

相关标签:
5条回答
  • 2021-01-31 11:11

    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.

    0 讨论(0)
  • 2021-01-31 11:13

    You've said 1000, so I'll stick to that number just for instance.

    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.

    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.

    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.

    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?

    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!

    Edit: you are using ObservableCollection, so I've assumed WPF/Silverlight.. update the question if this is not correct

    0 讨论(0)
  • 2021-01-31 11:15

    Another thing you can try: subclass ObservableCollection and make it support bulk loading (AddRange). Here is an article: AddRange and ObservableCollection

    0 讨论(0)
  • 2021-01-31 11:18

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

    • 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.
    • 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.
    0 讨论(0)
  • 2021-01-31 11:20

    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

    0 讨论(0)
提交回复
热议问题