Coroutines - Loader and UI Thread Marshaling

May 2, 2011 at 8:33 PM


I'm using Caliburn Micro with WPF, and have run into an issue with the Loader as described in the (excellent) Soup To Nuts series of posts. Hopefully it's something straightforward that I'm missing!

I have a viewmodel consisting only of a BusyIndicator (from with a button as content. The button calls a method similar to this:

        public IEnumerable<IResult> StartLongProcess()
            yield return Loader.Show();
            yield return new WaitResult();
            yield return Loader.Hide();

WaitResult is implemented like this:

    public class WaitResult : IResult
        public void Execute(ActionExecutionContext context)
            var task = Task.Factory.StartNew(() => Thread.Sleep(3000));
            task.ContinueWith(t => Completed(this, new ResultCompletionEventArgs()));

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

The issue I see is that after WaitResult has completed, and the Loader.Hide result is enumerated, I get a first chance InvalidOperationException. Forcing the debugger to stop on these shows that it is caused by the loader implementation accessing the UI from the wrong thread. Using:

    busyIndicator.Dispatcher.Invoke(new Action(() => { .... code interacting with busy indicator ... }));

works correctly however.

Is my assumption from reading the Loader code that the results are executed on the UI thread incorrect, or am I doing something wrong here?




May 2, 2011 at 9:04 PM

Each implementation of IResult is individually responsible for thread marshalling (for better or worse). To your problem, have the WaitResult raise the completed event on the UI thread. Something like this:

task.ContinueWith(t => Execute.OnUIThread(() => Completed(this, new ResultCompletionEventArgs())));

May 2, 2011 at 9:15 PM

Ah, that explains it. Thanks for the quick response. Seems to work properly with that modification!