Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Overcoming the demands placed on the UI Thread by constantly reordered data in an ObservableCollection

0.00/5 (No votes)
20 Oct 2014 1  
Provides an alternative to an ObservableCollection that is less UI intensive

Introduction

If you have worked with WPF, then you've no doubt encountered a problem, where in order to update an ObservableCollection you have to spend long periods of execution on the UI thread. Generally for a small number of updates this is not an issue, but when dealing with frequently updated data that constantly varies in order (an example would be best price from list of brokers) this can be problematic. This article looks at an approach to overcoming it.

Background 

I have encountered this problem a few times in my working career and came up with the idea for this solution when faced with a particularly high volume incident.

Typically in most environments I have been in, data is acquired remotely via some form of service and in all instances it is common sense to process the data via a background worker thread (task pools, ect.). However at some point the data needs to be displayed and when it involves more than just property updates, we will typically need to call a BeginInvoke or an Invoke on the dispatcher thread. If you have done this, you will typically know how constantly invoking the dispatcher, can quickly render a UI unresponsive.

So when do we need to call BeginInvoke or an Invoke? Only when the collection needs to be changed, i.e. an item is add, removed, or its order changed. So how can we prevent that happening? The answer is we can't for items that need to be add or removed, but in the case of reordered data there is an alternative.

In a typical ObservableCollection, you may decide to handling reordering by either a CollectionViewSource or by swapping the items that need to change position as the value determining the sort index changes. Both approaches need to be performed on the UI thread and if called frequently it will degrade the UI's responsiveness. The approach that I have taken, is to rather than look at the properties of a class in the traditional OO approach (as belonging solely to one class), is to instead look at them as interchangeable. What that means in praticial terms, is instead of objects changing location, only their data is swapped. The obvious advantage to this is that property changes can be made on worker threads and so therefore have no impact on UI performance.

This is achieved this by wrapping an ObservableCollection and only updating it by adding or removing items, when the collection grows or shrinks. At all other times, the contents of the items are swapped, allowing the processing to be performed on background threads and thus freeing the UI thread to update the UI.

Although this approach works well for collections of a certain size, but as collections get larger, performance will quickly degrade as sorting an inserting will generally requires large number of iterations.

Using the Code

The zip file is divided into three projects;

ReorderableObservableCollection The ReorderableObservableCollection source

ReorderableObservableCollection.Tests A small unit test project

ReorderedCollectionExample An example of the performance differences and how to use the collection.

ReorderableObservableCollection

For the most part the collection can be treated like any other collection, but it is worth remembering there is one important thing to remember. You cannot rely on ReferencesEquals to determine if the objects contained in the collection are the same, as the contents will be swapped and so therefore you will need to implement IEquatable<>.

The differences

In order to achieve the performance, the observable collection is wrapped by the class and exposed as an IEnumerable collection. It is designed to work by allowing changes to be made in batches and then for the changes to be synchronized, which will then be performed on the UI thread.

To bind the collection to a control, you need to reference the ObservableCollection property.

//
// This property exposes the wrapped observable collection and should be bound
// to the control used to display the collection in your view
//
public IEnumerable<T> ObservableCollection
 
Example
<DataGrid ItemsSource="{Binding Path=ReorderableData.ObservableCollection}" />

Synchonrizing the underlying collection.

//
// Updates the observable collection with any changes made
//
public void Sync()


//
// Updates the observable collection from the maintained list and resets the changes.
// If a large number of changes have been made then this can be more efficient.
//
public void SyncReset()

Inserting items into the correct location to maintain sort order.

//
// Inserts an item into the correct location in the collection, based on its IComparable<>
// implementation
//
public void InsertInOrder(T item)
 
Example
ReorderableData.InsertInOrder(new ExampleClass());


//
// Inserts an item into the correct location in the collection, based on its 
// IComparer<T> implementation
//
public void InsertInOrder(IComparer<T> comparer, T item)
 
Example
public class SortOrder : IComparer<ExampleClass>
{
  public int Compare(ExampleClass x, ExampleClass y)
  {
     return x.Index.CompareTo(y.Index);
  }
}

ReorderableData.InsertInOrder(this, new ExampleClass());

Sorting the collection into order.

//
// Performs a quick sort on the collection using its IComparable<> implementation
//
public void Sort()
 
Example
ReorderableData.Sort();


//
// Performs a quick sort on the collection, using a supplied on its IComparer<T> implementation
//
public void Sort(IComparer<T> comparer)
 
Example
public class SortOrder : IComparer<ExampleClass>
{
  public int Compare(ExampleClass x, ExampleClass y)
  {
     return x.Index.CompareTo(y.Index);
  }
}

ReorderableData.Sort( new SortOrder());

The demo program

The demo program is designed to show the differences in load placed upon the UI thread when sorting ObservableCollection, against the ReorderableObservableCollection which does not touch it all when sorting.

Example

The top data grid is bound to the ReorderableObservableCollection and the lower grid to the ObservableCollection. You will notice a sleep value, which is the sleep given to the thread updating the ObservableCollection. Reduce this sleep and you will quick find the UI becomes unresponsive, increase and you will find the number of updates decreases. This could be optimised so that calling of BeginInvoke is throttled, but in a commercial application there will be other UI activities occurring, which are not simulated by the demo.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here