WPF 4.5: Observable Collection Cross-Thread Change Notification

Pete Brown - 16January2012

WPF 4.5 is available as part of the Visual Studio 11 Developer Preview released at
the Build 2011 conference, and is part of the .NET Framework
version 4.5. WPF 4.5 addresses several important customer requests
including the ability to have cross-thread change notification for
collections - the topic of this post.

Update 1/20/2012: I have
posted a set of updates to this post with additional
information about the details, as well as a few code changes. Be
sure to read that post as well as this one.

The Problem

As I explained in my post on cross-thread change notification, Silverlight
and WPF have the concept of the UI thread and background (or
secondary) threads. Code on a background thread is not allowed to
interact with UI elements (controls, etc.) on the foreground
thread. You can find more information about the general problem in
that post.

What happens when you need to add and remove items from a
collection from that background thread? That's pretty common in
applications which must keep a list updated (stock trading is an
obvious example). When the collection raises the change
notification events, the binding system and listeners, on the UI
thread, have code executed. Remember, an event is just a delegate,
or really, just a function pointer. So, essentially, the collection
is calling a function on the UI thread. That's not allowed, and it
throws an exception.

The approach pre-4.5 was to dispatch all calls to add items to
the collection. The diagram from the Silverlight post applies
here.

If code on the background thread needs to access elements on the
UI thread, set a property for example, it needs to dispatch the
call to the UI thread using the dispatcher or synchronization
context. Get enough of this happening, such as when constantly
updating a collection, and you can run into some real performance
problems.

The 4.5 Solution

We actually have a good solution for this in WPF 4.5. The
BindingOperations class includes the
EnableCollectionSynchronization function. This function helps the
binding system and the collection intelligently handle change
notification.

Let's look at an example. The application, when run, will look
like this:

The stocks in the ListBox are loaded by clicking the "Load"
button. To test the change notification, click the Keep Loading
button. For this example, the Keep Updating button doesn't do
anything (more on that when I cover in-place sorting and updating
in another post)

XAML

Just for grins, and to show its inclusion in 4.5, I've put the
functionality into another new addition to WPF proper: the Ribbon.
I didn't use commands although they are supported.

For this example, the primary pieces to pay attention to are the
ListBox which is bound to the stocks, and the ribbon buttons to
Load and Update the stocks collection. Those use the Stock model
item via the ViewModel.

When the MainViewModel is instantiated, the collection is
created. Next, I call the EnableCollectionSynchronization method,
providing it the collection and an object to use as a lock. The
lock object is nothing special, just an instance of the CLR object
type. This single method makes cross-thread collection change
notification possible.

The StartAddingItems method spins up another thread, using the
Task parallel library, which then loads items into the collection.
The data is randomly generated, but includes a large number of
updates. When you run the application, you'll see that the ListBox
is able to be quickly updated and that there are no cross-thread
exceptions reported.

If you want to cause the exceptions to happen as normal, comment
out the BindingOperations.EnableCollectionSynchronization call.

Summary

WPF 4.5 includes a number of key targeted performance and
capability features, one of which is cross-thread collection change
notification. Enabling the change notification is as simple as the
inclusion of a lock object and a single function call. Versus the
manual dispatching approach, this can be a real performance win,
not to mention save you some coding time.

Nice post and great that this will be in WPF 4.5 now we can remove a lot of those Dispatcher.BeginInvoke :-)

It would be nice if AddRange was added to ObservableCollection but will it not be less important if we use EnableCollectionSynchronization ?
In 4.0 we have to change to the UI thread and then call Add repeatedly to add several items which in turn updates the UI.
But in 4.5 each call to add (yes AddRange would make our life easier here) will affect the UI thread whenever there is a switch between the threads. Does anyone agree that this makes AddRange a little less important or am I completely lost? :-)

I do not know what the "in-place sorting and updating" post might be about but adding Sort to ObservableCollection would also be nice (if possible?).

This approach only takes care of the cross-thread notification problem but doesn't handle the thread-safety issues that arise. Try this collection which takes care of this problem as well as other multi-threaded problems that will inevitably crop up with other approaches : http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection

Comment on this Post

Your comment has been posted, thank you. (If your comment does not appear shortly, it was marked as spam.)

Pete Brown is a XAML and Blinky lights guy at Microsoft who focuses on Windows XAML (WinRT), WPF, Silverlight, .NET Micro Framework and other "code on the client" and "code on a device" technologies. This is his personal blog.
About Pete/Full Bio | Contact | About 10rem.net