DCSIMG
WPF Model Data Binding - Part II - Essential WPF

WPF Model Data Binding - Part II

In my previous post I showed how to design a data model which provides the following characteristics:

  1. Property notification changes
  2. In-memory state persistency
  3. Commit and Rollback mechanism
  4. Dirty flag

In this post I will show how to handle multi-threading scenarios with WPF data binding.

You can download the complete code from here.

Before I will start, I want to give a little background of the WPF threading model, and to explain why you shouldn't change any data-bind object from a different thread.

The WPF threading model is very simple: STA (Single Thread Affinity). This means that like other UI technologies such as Windows Forms, only the thread that created the UI is allowed to access it.

In Windows Forms unfortunately, an access from a different thread is not restricted. So changing a UI element from a different thread in Windows Forms most of the time works, but sometimes fails (usually after deployed :).

In contrary to Windows Forms, WPF restricts access to UI elements from a different thread, by throwing an exception most of the time. I will not get into details but you can read all about it here.

Based on the WPF treading model described above, if you will try to change a data-bind object property value from a different thread (other than the bind UI element), you will have an exception.

To workaround this problem, you should use the Dispatcher property of the UI element, to invoke a change in the correct thread.

The code snippet bellow shows how to do that:

private delegate void PropertyChangedInvoker(PropertyChangedEventArgs args);

/// <summary>
/// Notify property change
/// </summary>
private void OnPropertyChanged(PropertyChangedEventArgs args)
{
    if (PropertyChanged != null)
    {
        if (Dispatcher.Thread == Thread.CurrentThread)
        {
            PropertyChanged(this, args);
        }
        else
        {
            Dispatcher.Invoke(
                DispatcherPriority.DataBind,
                new PropertyChangedInvoker(OnPropertyChanged),
                args);
        }
    }
}

As you can see, I have upgraded the OnPropertyChanged method to raise the PropertyChanged event in the context of the UI thread, by calling the Dispatcher, Invoke method in case that the caller thread is not the one that created the data object.

Where the Dispatcher property comes from?

There are several ways to invoke a method in the context of the UI thread. For the simplicity I derived the DataEntity type from the DispatcherObject class (introduced with the .NET 3.0 under the WindowsBase.dll). Deriving a type from the DispatcherObject class, provides an instance Dispatcher property of type Dispatcher. The dispatcher instance is a Singleton per Thread. Calling the Dispatcher instance Invoke method, ensures that the delegate passed to it is invoke in the context of the same thread that created the DispatcherObject derived type (DataEntity).

One disadvantage of this method is that you should create the data entity with the same thread as the UI.

To overcome this disadvantage, you can always pass the DataEntity a UI element Dispatcher instance, only after you have created the entity.

Another alternative for using the Dispatcher type, is the System.Threading.SynchronizationContext class, which is beyond the scope of this post. You can read about it here (it is the same type that Dispatcher and BackgroundWorker are using internally).

The code snippet bellow demonstrates how to change the DataEntity instance from a different thread:

void buttonSetData_Click(object sender, RoutedEventArgs e)
{
    // Do not access DataContext from other thread!
    Person person = DataContext as Person;
    new Thread(delegate()
    {
        person.FirstName    = "John";
        person.LastName     = "Doe";
        person.Blog         = "http://www.blogs.com/JohnDoe";

    }).Start();            
}
You can download the complete code from here.

(I have upgraded to VS2008 Beta2, so if you still working with VS2005, create a new WPF Window project and drop the files there. I didn't use C# 3.5 syntax so it should work fine)

Published Friday, September 14, 2007 4:54 PM by Tomer Shamam
תגים:, , ,

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above:
Powered by Community Server (Commercial Edition), by Telligent Systems