Wednesday, January 24, 2007

How to propagate changes across threads in WPF?

If you are reading this blog, I assume you already knew that in WPF,
it doesn't not support collection change notifications across threads.

Beatriz Costa has a great post about this (see http://www.beacosta.com/Archive/2006_09_01_bcosta_archive.html).

Kent (http://www.newsalloy.c8/681/) has written a nice wrap class DispatchingNotifyingCollection to solve this problem. But unfortunately, his solution didn't work when used together with CollectionView because of a bug in CollectionView as mentioned in the following post http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1144125&SiteID=1. Also, his solution seems make it harder to solve the multi thread problems since different threads can change both the wrapped collection and DispatchingNotifyingCollection at the same time.

So how to fix the filter problems?
Inspired by Kent's code, here is my solution. I called it DispatchingObservableCollection.
It is simpler than Kent's solution and also works with the CollectionView filter.

This is my first post and hopefully someone will read it and find it useful. :)

public class DispatchingObservableCollection<Titem> : ObservableCollection<Titem>

{

private readonly Dispatcher _dispatcher;

public Dispatcher Dispatcher

{

get

{

return _dispatcher;

}

}

public DispatchingObservableCollection(Dispatcher dispatcher)

{

if (dispatcher == null)

{

throw new ArgumentNullException("dispatcher can not be null");

}

_dispatcher = dispatcher;

}

private delegate void OnCollectionChangedHandler(NotifyCollectionChangedEventArgs e);

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)

{

if (_dispatcher.CheckAccess())

{

base.OnCollectionChanged(e);

}

else

{

_dispatcher.Invoke(DispatcherPriority.DataBind, new OnCollectionChangedHandler(base.OnCollectionChanged), e);

}

}

}


2 comments:

Beatriz Costa said...

Hi Chong,

I see at least one serious bug in the solution from your blog (also in the solution from Kent's blog). It applies to all operations, but let's take Insert as an example. Your method inserts the new item into the data structure on the worker thread, then raises the change notifcation on the dispatcher thread (blocking the worker thread until the dispatcher thread is available). So there's an interval of time where the item has been added, but the dispatcher thread doesn't know it yet. If the dispatcher thread reads the collection during this interval, it will get the wrong information. This can lead to incorrect display, or even a crash.

The solution in Bea Costa's blog does both the actual insertion and the notification on the dispatcher thread, so it doesn't have the problem.

Sam Bent and Bea Costa

肖重庆 said...

Hi, Sam and Bea:

Thanks for your comment. I am surprised your guys actually take time to read my post.

As I mentioned in my post, my solution is not thread safe so I am aware of the problems you have mentioned.

My solution will work only under the following conditions.
1. A worker thread is updating the collection.
2. The UI thread is only using data binding to display the collection.
(This is what is required in my current project and that is why I come up with the siimple solution)

For more complex condition, say multi threads can update the collection at the same time, my solution won't work, more work need to be done to make it thread safe.

Once again, thanks for your comments.
(Especially thanks Bea since your blogs have helped me a lot to understand data binding in WPF.)