Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Tricksy Dispatcher & Running Tests in TDD.NET

Updated 11/07/2008

I recently ran into an interesting situation. I had an ObservableCollection bound to my UI in WPF, and I needed to updated this collection on something other than the UI thread. In other words, I was adding items to a UI-bound collection on a different thread, which triggered the change notification events on that non-UI thread. Unfortunately, you can't do that, the change notification events for ObservableCollection can only be execute on the UI thread.

Karl Hulme blogged about this and posted some custom collections he wrote to deal with the problem. Karl's solution was heavier (and more generic than I needed), so I distilled his code down to this:

public class BindableCollection<T> : ObservableCollection<T>
{
    private readonly Dispatcher _dispatcher;

    public BindableCollection(Dispatcher dispatcher)
    {
        if (dispatcher == null) throw new ArgumentNullException("dispatcher");

        _dispatcher = dispatcher;
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        _dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => base.OnPropertyChanged(e)));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        _dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => base.OnCollectionChanged(e)));
    }
}

This really a simplified version of what Karl did, but it was all I needed. When you instantiate this collection, you pass in the dispatcher for the UI. You can easily get that using System.Windows.Threading.Dispatcher.CurrentDispatcher. In case you are not familiar with it, Dispatcher is WPF class for managing the queue of work items for a thread.

It's a sweet trick. To sum up, the collection is now dependent on a Dispatcher. We pass into it the Dispatcher for the UI thread. Then we override the change notification methods, so that we marshal the actual underlying change notification back onto the UI thread.

The cast to an Action looks a bit weird, but unfortunately lambdas are not implicitly cast to Actions.

The Unexpected Bug

I guess all bugs are unexpected, so whatever.

Notice the similarity in colors between Flash and Ayende's blog? Coincidence? A few of our automated tests dealt with this new BindableCollection, and everything was green when I executed the tests through ReSharper's test runner. Likewise, everything ran as expected in the application itself. However, when running these test through TDD.NET the _dispatcher.Invoker call never returned.

Right now, it's important to give credit where it is due. Ayende solved this problem before I had time to fully grasp it even. If there is a Justice League of .NET, Ayende is Flash (and we think Justice Gray is probably Superman? I mean, the hair, right?).

Anyway, errors in this explanation are my own.

Diagnosis

When you call Invoke() the action is placed onto a queue. The  processing of the queue is triggered by the method WndProcHook(). This method is the hook for processing message from the OS.  In other words, the queue is only processed each time the window receives a message.

Now TDD.NET executes tests in a console, ReSharper's test execute in a GUI. So with TDD.NET, the queue is never processed and hence the Invoke method never returns.

The Revision

Ayende came up with the following revision to our collection. You can see some functional influence here:

public class BindableCollection<T> : ObservableCollection<T>
{
    private readonly Action<Action> _dispatcher;

    public BindableCollection(Action<Action> dispatcher)
    {
        if (dispatcher == null) throw new ArgumentNullException("dispatcher");

        _dispatcher = dispatcher;
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        _dispatcher(() => base.OnPropertyChanged(e));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        _dispatcher(() => base.OnCollectionChanged(e));
    }
}

Instantiating this, might look like this:

new BindableCollection<stuff>(action => dispatcher.Invoke(DispatcherPriority.Normal, action));

However, in my tests, I can avoid the dispatcher altogether and simply:

new BindableCollection<stuff>(action => action());

Hope someone else finds this useful.


Posted 10-23-2008 11:42 AM by Christopher Bennage
Filed under: , ,

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)