6

I have an (non-virtualized) ItemsControl that binds its ItemsSource to a ObeservableCollection of ViewModel instances. Now once the large amount Model instances is loaded all the ViewModel complemnents needs to be added to that ObservableCollection. How can I add a large amount of ViewModels without making the UI Thread hang?

I suppose the UI Thread hangs because each time a new item is added the ItemsControl needs to update itself and does layout etc. over and over again.

  • Should I suspend the binding add all items and then resume? If so, how?
  • Should I override the ObservableCollection to implement an AddRange so only 1 CollectionChanged Event is fired for adding multiple items? Or alternatively just replace the whole collection?
  • Or is it better to add each items separately and call Dispatcher.Invoke for each item separately? So I would unblock frequently.

How do you handle large dynamic lists that can not be virtualized?

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
bitbonk
  • 48,890
  • 37
  • 186
  • 278

2 Answers2

10

You can create a a class derived from ObservableCollection which allows you to temporarily suspend CollectionChanged events like this:

public class SuspendableObservableCollection : ObservableCollection
{
    private bool suspended;

    public bool Suspended 
    {
        get
        {
            return this.suspended;
        }
        set
        {
            this.suspended = value;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Reset));
        }
    }

    protected override void OnCollectionChanged(
        NotifyCollectionChangedEventArgs args)
    {
       if (!Suspended)
       {
           base.OnCollectionChanged(args);
       }
    }
}
Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • My question is more towards how I can improve perfomance not how to implement such a collection. Also if I temporarily suspend events like you suggest the ItemsControl will display the wrong state after suspension is resumed. And if then a new CollectionChanged event is raised it will still display the wrong state because it didn't get all the modifications that have been made to the collection while it was suspended. – bitbonk Mar 17 '10 at 12:01
  • 1) Doesn't implementing such a collection enable you to improve performance? 2) I added a reset event to fix the issue you described. – Wim Coenen Mar 17 '10 at 12:18
  • It seems that this doesn't improve performance. Probably because tze ItemsControl needs to generate a lot of Item Controls at once. So I guess the better approach would be to add some items separately using (Begin)Invoke multiple times. – bitbonk Mar 17 '10 at 12:49
0
<ItemsControl IsAsync="True" ... />
Elisabeth
  • 20,496
  • 52
  • 200
  • 321
  • You probably meant ``. `ItemsControl` itself does not have that property. This property should be used sparely: "there should not be many scenarios where you need to use the IsAsync property. The .NET guidelines recommend against defining properties that are orders of magnitude slower than a field set would be." – bitbonk Oct 06 '10 at 21:37
  • ah I thought every Selector has IsAsync property. When you load a window which is loading much data in a not async way you get a black window or it hangs (such It did in my case using IsAsync=True now). So IsAsync is advised to use! – Elisabeth Oct 07 '10 at 18:07
  • If you are suggesting to do `` that doesn't do the trick. `IsAsnyc` only works when the DataSource itself is not available right away or takes a while. In my case the DataSource is available right away but just contains a lot of items. Read here for more details about the problem: http://goo.gl/BwA1 – bitbonk Oct 07 '10 at 18:26