Posts Tagged ‘observablecollection’

Faster Databinding in WPF and Silverlight using OptimizedObservableCollection

Written by Simon Guindon. Posted in Coding

ObservableCollection<T> is a core piece to most WPF or Silverlight applications. It aligns perfectly with the MVVM pattern that is very popular in XAML based applications these days. For example binding a ListBox’s ItemsSource to an ObservableCollection give’s the WPF or Silverlight databinding engine the ability to listen to the change events that ObservableCollection offers whenever an item is added or removed to the list. This makes it very simple for the developer, in their ViewModel they just add or remove items as they wish and the UI updates itself. Magical!

There’s a catch though. Although this is automatic, it’s far from fast. If you are removing or adding 1 item at a time, you likely won’t see any issues but we must keep in mind when we call ObservableCollection.Add() or ObservableCollection.Remove() an event is being fired per call and the layout engine is calculating layout on the items. If you load 100 users, and you add them one by one as ObservableCollection requires you to, you will be hitting layout calculations on each call.

What we really want is to fire one change event when a batch of add or removes has executed so that the layout engine recalculates the visuals once. There is no need to recalculate layout on each item add. I’m not sure why this was left out of ObservableCollection but it’s a pretty simple addition and offers some great performance increases.

Adding an AddRange() method to ObservableCollection will give us the flexibility we need when we want to add a batch of items or we can still call Add() when we need more fine grained updates.

public class OptimizedObservableCollection 
    : ObservableCollection
{
    private bool suppressOnCollectionChanged;

    public void AddRange(IEnumerable items)
    {
        if (null == items)
        {
            throw new ArgumentNullException("items");
        }

        if (items.Any())
        {
            try
            {
                suppressOnCollectionChanged = true;
                foreach (var item in items)
                {
                    Add(item);
                }
            }
            finally
            {
                suppressOnCollectionChanged = false;
                OnCollectionChanged(
                    new NotifyCollectionChangedEventArgs(
                        NotifyCollectionChangedAction.Reset));
            }
        }
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!suppressOnCollectionChanged)
        {
            base.OnCollectionChanged(e);
        }
    }
}

As you can see in the AddRange() we are supressing the event from firing until the batch is completed so that the event only fires once.

I created a sample which uses two ListBox’s databinding against ObservableCollection and OptimizedObservableCollection that adds 1,000,000 items to these collections. Keep in mind that 1,000,000 is a rather unrealistic number but I needed to crank the volume in this simplistic of a sample to see the difference. When you use DataTemplate’s and create custom visuals (which is very common) this limitation of ObservableCollection can start showing to the user in the amount of seconds with only 100 items databound. Remember the impact of the layout calls per Add() will be based on the size and complexity of your visual tree. In the case of this sample ListBox is only using one control so the layout cost is very small.

Once you start building richer UI’s you will hit views that take a few seconds to databind when loading data but you can avoid this with OptimizedObservableCollection that I will show you.

image

From the screenshot of the sample application we can see there is only 1 layout calculation even after we added 1,000,000 items (the count says 2 because the initial application loading also requires a layout calculation). The real shocker is the amount of time spent on the UI thread databinding with the regular ObservableCollection.

Whenever possible we should be spending as little time in the UI thread as possible so that the application rendering doesn’t get interfered with. The more time we spend in the UI thread crunching CPU the less is available to the WPF or Silverlight rendering engine for updating UI in other places in our application such as changes in other areas or animations we may have running.

Here’s a copy of the sample code and OptimizedObservableCollection<T>

Download

Startups