views:

372

answers:

2

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?

+3  A: 

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
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
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
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
A: 
<ItemsControl IsAsync="True" ... />
Lisa
You probably meant `<Binding IsAsync="True"/>`. `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
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!
Lisa
If you are suggesting to do `<ItemsControl ItemsSource="{Binding IsAsync="True" .../>` 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