Property change notifications for multithreaded Silverlight applications

As I’ve been developing more complex Silverlight business applications, I’ve been increasingly relying on BackgroundWorker to offload complex calculations and operations to the background thread.

Something I didn’t have to worry about in the typical user interface thread-only implementation of my app was which thread change notifications fire on.

Here’s a typical scenario:

  1. You have a CLR property on a data/model object in your app. In a background thread, the property is updated.
  2. As all data and model objects should be, the type implements INotifyPropertyChanged (the data binding system relies on this to know when to update bindings).
  3. The property changed event is fired, and listeners react to the change, including the data binding system.
  4. The data binding system throws an invalid cross thread exception, since all UI operations, including handing off the binding changes, need to happen on the UI thread (but it’s happening on a background thread)

BackgroundThreadWithoutDispatcher

So what you really need to do is funnel those change notifications back to always happen on the user interface thread – that’s what makes the most sense given the data binding requirement.

By using a dispatcher, which can accept a BeginInvoke for that other thread, it will all just work and binding will move along happily:

BackgroundThreadWithDispatcher

This scenario is ripe for a few helper classes that I’m open to receiving feedback on. Hope you agree with my approach of using a ‘smart dispatcher’.

One Dispatcher to rule them all

The only thing you need to fire an Action on the primary UI thread is a reference to a Dispatcher instance. Unfortunately this isn’t something you can request from a background thread – you need to have the instance already stored away in most cases.

As a result, I’ve a static helper class and in it, I try and make sure that there is an instance stored before any of my data binding or model code is used.

Though I could have used SynchronizationContext instead, Dispatcher is a little easier to use, and better suited to WPF and Silverlight apps.

In my App.xaml.cs, I add a line that calls Initialize on my class. This is to make sure that there is always an available dispatcher for other threads to get access to:

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    SmartDispatcher.Initialize(Deployment.Current.Dispatcher);

    InitializeComponent();
}

If you didn’t want to have to add this code to initialization, my implementation of a dispatcher helper class falls back to trying to grab the dispatcher from the root visual – but this won’t work in all scenarios, so I prefer the above. One more thing to remember however.

Being smart about using the dispatcher

It’s easy enough to replace all event firings for property changes to go through the dispatcher. However, this will reduce performance some; instead of the call being made immediately, it’s made at a later time.

There is a hidden method called CheckAccess on Dispatcher that returns true if you are on the same thread that the Dispatcher was first created in. By checking with it before making a call, we can make a better perf choice:

  • If CheckAccess is true and we’re on the UI thread, just go ahead and directly invoke the Action.
  • Else, BeginInvoke with the Dispatcher that will take care of getting it done on the UI thread later.

I’ve also done some optimization to try and ensure that the design-time experience at least stays consistent. In general you shouldn’t have background operations ever occurring in a design-time scenario.

The static helper class I’ve created is called SmartDispatcher and simplifies all of this logic.

So here’s my smart dispatcher implementation:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System.ComponentModel;

namespace System.Windows.Threading
{
    /// <summary>
    /// A smart dispatcher system for routing actions to the user interface
    /// thread.
    /// </summary>
    public static class SmartDispatcher
    {
        /// <summary>
        /// A single Dispatcher instance to marshall actions to the user
        /// interface thread.
        /// </summary>
        private static Dispatcher _instance;

        /// <summary>
        /// Backing field for a value indicating whether this is a design-time
        /// environment.
        /// </summary>
        private static bool? _designer;

        /// <summary>
        /// Requires an instance and attempts to find a Dispatcher if one has
        /// not yet been set.
        /// </summary>
        private static void RequireInstance()
        {
            if (_designer == null)
            {
                _designer = DesignerProperties.IsInDesignTool;
            }

            // Design-time is more of a no-op, won't be able to resolve the
            // dispatcher if it isn't already set in these situations.
            if (_designer == true)
            {
                return;
            }

            // Attempt to use the RootVisual of the plugin to retrieve a
            // dispatcher instance. This call will only succeed if the current
            // thread is the UI thread.
            try
            {
                _instance = Application.Current.RootVisual.Dispatcher;
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("The first time SmartDispatcher is used must be from a user interface thread. Consider having the application call Initialize, with or without an instance.", e);
            }

            if (_instance == null)
            {
                throw new InvalidOperationException("Unable to find a suitable Dispatcher instance.");
            }
        }

        /// <summary>
        /// Initializes the SmartDispatcher system, attempting to use the
        /// RootVisual of the plugin to retrieve a Dispatcher instance.
        /// </summary>
        public static void Initialize()
        {
            if (_instance == null)
            {
                RequireInstance();
            }
        }

        /// <summary>
        /// Initializes the SmartDispatcher system with the dispatcher
        /// instance.
        /// </summary>
        /// <param name="dispatcher">The dispatcher instance.</param>
        public static void Initialize(Dispatcher dispatcher)
        {
            if (dispatcher == null)
            {
                throw new ArgumentNullException("dispatcher");
            }

            _instance = dispatcher;

            if (_designer == null)
            {
                _designer = DesignerProperties.IsInDesignTool;
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public static bool CheckAccess()
        {
            if (_instance == null)
            {
                RequireInstance();
            }

            return _instance.CheckAccess();
        }

        /// <summary>
        /// Executes the specified delegate asynchronously on the user interface
        /// thread. If the current thread is the user interface thread, the
        /// dispatcher if not used and the operation happens immediately.
        /// </summary>
        /// <param name="a">A delegate to a method that takes no arguments and
        /// does not return a value, which is either pushed onto the Dispatcher
        /// event queue or immediately run, depending on the current thread.</param>
        public static void BeginInvoke(Action a)
        {
            if (_instance == null)
            {
                RequireInstance();
            }

            // If the current thread is the user interface thread, skip the
            // dispatcher and directly invoke the Action.
            if (_instance.CheckAccess() || _designer == true)
            {
                a();
            }
            else
            {
                _instance.BeginInvoke(a);
            }
        }
    }
}

A property change base class

Next up, instead of having to manually wire up the INotifyPropertyChanged interface in all my data classes, I prefer to derive from a common base class, PropertyChangedBase, that has logic in it to use my smart dispatcher.

This base class:

  • Has a protected NotifyPropertyChanged method that does all the real work
  • Statically stores created event arguments to improve performance over time (only one PropertyChangedEventArgs instance is required per property name in the application’s lifetime)
  • Uses SmartDispatcher to use a dispatcher only if not running on the user interface thread, otherwise directly invokes the property change handler.
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Windows.Threading;

namespace System.ComponentModel
{
    /// <summary>
    /// A base class for data objects that implement the property changed
    /// interface, offering data binding and change notifications.
    /// </summary>
    public class PropertyChangedBase : INotifyPropertyChanged
    {
        /// <summary>
        /// A static set of argument instances, one per property name.
        /// </summary>
        private static Dictionary<string, PropertyChangedEventArgs> _argumentInstances = new Dictionary<string, PropertyChangedEventArgs>();

        /// <summary>
        /// The property changed event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify any listeners that the property value has changed.
        /// </summary>
        /// <param name="propertyName">The property name.</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentException("PropertyName cannot be empty or null.");
            }

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                PropertyChangedEventArgs args;
                if (!_argumentInstances.TryGetValue(propertyName, out args))
                {
                    args = new PropertyChangedEventArgs(propertyName);
                    _argumentInstances[propertyName] = args;
                }

                // Fire the change event. The smart dispatcher will directly
                // invoke the handler if this change happened on the UI thread,
                // otherwise it is sent to the proper dispatcher.
                SmartDispatcher.BeginInvoke(delegate
                {
                    handler(this, args);
                });
            }
        }
    }
}

Putting it all together

To use this in your own application, you take your model/data classes and derive from PropertyChangedBase. Then, in CLR setters, always call NotifyPropertyChanged:

public class SampleData : PropertyChangedBase
{
    private string _someText;
    public string SomeText
    {
        get { return _someText; }
        set
        {
            _someText = value;
            NotifyPropertyChanged("SomeText");
        }
    }
}

Grab the code

I’ve decided to place these classes inside their appropriate system namespaces: System.Windows.Threading for the smart dispatcher, and System.ComponentModel for the PropertyChangedBase. Hope that isn’t too offensive.

Here are the files for download as well:

Hope this helps. Let me know what you think.

Comments

  1. April 2nd, 2010 | 11:08 am

    Aren’t you assuming that other objects are listening to the propertychanged event on the UI Thread only? What if the background thread was spun up from another background thread. I would want my events to fire on the other backgronud thread, and not suddenly end up over in a UI thread.
    I try only a use dispatcher when I know I’m dealing with UI Controls (ie I think only UI Controls should be using the dispatcher). If I’m just dealing with DependencyObjects, that’s a whole different story, since there’s no guarantee that these have been created on the UI Thread.

  2. April 2nd, 2010 | 12:46 pm

    @Morten,
    Yeah, so I am making the assumption that the listening is only on the main UI thread. With the exception of MVVM zealots, who will already have solutions in place for this sort of thing, property change notifications are a majority (“80% is the number I randomly pick”) of what’s going to be done with those values for most people who aren’t taking this into account today.

  3. Joe
    April 2nd, 2010 | 6:47 pm

    Jeff,

    Is there any hard and fast rule about not allowing the property name to be null when calling NotifyPropertyChanged()?

    It has been my experience that you can use ‘null’ as a parameter and it has the effect of providing notification for all the properties, not just one specifically.

    Regards,

    Joe

  4. Haathen
    April 2nd, 2010 | 8:41 pm

    @Morten:
    This article is about Silverlight and -unfortunately- here they can only be created on the UI thread.

    See:
    http://forums.silverlight.net/forums/p/164744/371088.aspx#371088

    This has not been changed in RC.

  5. April 3rd, 2010 | 7:00 am

    [...] Property change notifications for multithreaded Silverlight applications (Jeff Wilcox) [...]

  6. April 3rd, 2010 | 12:43 pm

    It is a sticky problem to solve.

    It does smell all wrong to load the solution of this problem on to the data classes. The problem is a result of the way the UI works so it seems only fair that code in the UI should solve the problem. Instead what we have here is the data classes jump through hoops (including the introduction of base class) just in case it might make the UI code barf.

    That said it is a pragmatic solution based on reasonable assumptions.

    I’m curious about a couple of things though:-

    Why not simply acquire a value for _instance from Deployment.Current during a static constructor on SmartDispatcher? It seems to me that would simplify some of the code.

    Also are we sure that when running in a designer CheckAccess won’t be called? It may throw if it is since _instance may remain null.

  7. April 4th, 2010 | 8:00 am

    Thanks for providing the code. Im surprised this wasnt addressed in SL 4.

  8. April 4th, 2010 | 8:56 am

    Sure thing, glad to help.

    On the Silverlight 4 thing, you will find that even my solution has some issues that would not work if this was baked into the platform, so its best addressed as a one-off thing for now in my opinion.

  9. April 12th, 2010 | 6:06 pm

    [...] Property change notifications for multithreaded Silverlight applications [...]

  10. April 16th, 2010 | 5:56 pm

    Um, so this appears to be broken in VS2010 RTM. Anything using it built fine in the RC. There is no longer a type System.Action that does not require a generic (or more than one for that matter). Is is just broken on my machine (BOMM lol)?

  11. April 16th, 2010 | 6:07 pm

    Hmm. Might be Resharper 5.0. I suspended it, and all error indications went away. Resharper seems to think there is no non-generic Action delegate.