Sync Notify Pattern for WPF Cross Thread View Model

WPF has a powerful data binding mechanism, by which it is easy to implement MVVM and MVC pattern for UI application.
But thing gets not so beautiful once you introduced multi-thread into the app. Somehow, it means you have to manually call dispatcher method to synchronize the thread context, or else INotifyPropertyChanged contract causes cross thread violation, which is likely to drive UI component throw exception. But tons of calls to Dispatcher.Invoke or Dispatcher.BeginInvoke make you code ugly and hard to maintain. And it is boring to call dispatcher every time you try to write view model properties.

Is there any solution to this embarrassed situation?
The answer is Sync Notify pattern.

By analyzing the WPF data binding data flow, you can find the best point to behave the thread synchronization is at the boundary of your code and WPF implementation, which is the property changed event raiser. To sync the thread context here makes your code clean and effective.

Typically, you might implement the INotifyPropertyChanged in following way:

INofityPropertyChanged Implementation
1
2
3
4
5
6
7
8
9
10
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion

And you might implement the property of view model as following:

Notify Property Decalration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#region Notify Property ViewModelProperty
private string viewModelPropertyBackField;
public string ViewModelProperty
{
get { return viewModelPropertyBackField; }
set
{
if (viewModelPropertyBackField == value)
return;
viewModelPropertyBackField = value;
Notify("ViewModelProperty");
}
}
#endregion

This implementation works perfect in single thread context , but fails in multi-thread context. So we introduce a new event raiser implementation, which synchronize the thread context before raising the event:

SyncNotify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#region Sync INotifyPropertyChanged Members
protected void SyncNotify(string propertyName, bool wait = false, Dispatcher dispatcher = null)
{
if (PropertyChanged == null)
return;
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(PropertyChanged, this, new PropertyChangedEventArgs(propertyName));
}
}
}
#endregion

This method check the whether it is running on the same thread as dispatcher, it not it synchronize the thread. It works perfect in most common cases.
But this implementation requires you to add some new code to the view model, and requires the event works as a delegate. This restriction sometime hurt, such as when you try to derive your view model from ObservableCollection<T>.
ObservableCollection<T> declared PropertyChanged event, but implemented it explicitly, which means you won’t be able to raise the event by calling PropertyChanged directly. Instead, you need to call protected method OnPropertyChanged.

In such cases, you need the following implementation powered by extension method:

SyncNotifyExtension
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public static class SyncNotifyExtension
{
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, propertyName);
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, PropertyChangedEventArgs> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, new PropertyChangedEventArgs(propertyName));
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, new PropertyChangedEventArgs(propertyName));
}
}
}
public static void SyncNotify(this INotifyPropertyChanged host, string propertyName, Action<object, string> eventRaiser, bool wait = false, Dispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(host != null);
Contract.Requires<ArgumentException>(string.IsNullOrWhiteSpace(propertyName));
Contract.Requires<ArgumentNullException>(eventRaiser != null);
dispatcher = dispatcher ?? System.Windows.Threading.Dispatcher.CurrentDispatcher;
if (dispatcher.Thread == Thread.CurrentThread)
{
eventRaiser(host, propertyName);
}
else
{
if (wait)
{
dispatcher.Invoke(eventRaiser, host, propertyName);
}
else
{
dispatcher.BeginInvoke(eventRaiser, host, propertyName);
}
}
}
}

These extension methods can be called inside the view model, and it ensures that the event is raised on the dispatcher thread.
Since these extension methods uses delegate to invoke the event raiser, so it is unnecessary to worry about the accessibility of the event raiser, event it is declared as protected or private. And 4 overloads can handle most common event raisers.

Self Registration Pattern for Singleton View Models in WPF

Where to store the Singleton View Model in WPF application, there are 2 common options:

Store in Resource Dictionary.

UI Designers prefers to store the WPF View Model into the Resource Dictionary because the objects in Resource Dictionary can be easily referenced in XAML.
But Developers must hate that way very much!
To fetch the object in Resource Dictionary from code behind must call the “FindResource” method of DependencyObject. And codes with tons of calls to “FindResource” method are ugly and very low efficient. The situation is worse since the accessibility to object in resource dictionary is also constrained by the Resource Scope, which means it is almost impossible to fetch the object from business logic.

Store in the Static Class.

I preferred to store the View Model in static class which is available globally. Developer can fetch the object by calling the static method, and designer also can fetch the object by using {x:Static} psudo-tag.
But it is still inconvenient somehow for designer, and it is somehow hard to provide design-time mockup data in this way.

For the previous 2 solutions, the pros and cons are obvious. But is it possible to combine these 2 approaches together to gains all the advantages but all the disadvantages.
The answer is Self-Registration Pattern.

The basic idea for Self-Registration Pattern is simple. It is obvious that we prefers to store the view models in Resource Dictionary, but we also want to access that object from Code Behind by calling static method.

So I designed the ViewModel class as following:

Self-Register View-Model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ViewModel
{
#region Self Registration
public static ViewModel Default { get; private set; }
public ViewModel()
{
Default = this;
}
#endregion
}
View-Model Declaration
1
<vm:ViewModel x:Key="ViewModel"/>

You can see, the ViewModel has a globally visible static property named Default, and the class set the Default property to itself in its constructor.
Which means once the View Model is initialized in Resource Dictionary, it also set the instance reference to Default,
So designer can reference the view model easily with StaticResource Tag

Reference View-Model from XAML
1
<Control Property="{StaticResource ViewModel}"/>

And Developer also can access the view model by calling Static Property

Reference View-Model from C#
1
ViewModel.Default