|
In this previous article, I mentioned how amazingly cool the monostate pattern was. Unfortunately, that coolness is limited, and the monostate pattern actually breaks and falls to pieces in several key scenarios.
When I wrote the code for that article, what was happening was that all the root properties on that model object (ModelRoot class) were of type ObservableCollection<T>. What I didn't realize is that ObservableCollection<T> has intimate knowledge of how WPF bubbles property changed events to the surface of the UI. In other words, when you fill an ObservableCollection<T> with instances of a class that implements INotifyPropertyChanged, the ObservableCollection<T> instance will actually subscribe to those changes, and then manually bubble those changes out to the WPF event dispatcher for you. While this is handy behavior, it can lull you into a false sense of security when implementing the monostate pattern.
Case in point: Implement the monostate pattern to have a single class with a single property called Name. Next, bind your XAML to an instance of that property. Then, create a button that has a handler. In that click handler, create an instance of your model root class and set the Name property to something fancy like "modified". What you'll notice is that your freaking GUI has not changed!! Even though there is but a single subscriber to your PropertyChanged event handler (the WPF event dispatcher), and there is a single source for your data (static member variables), and when you go through the debugger you will see the event fire and you will see the data change, the GUI is never updated!!
Even when you use the dispatcher to forward your event firing to the dispatcher thread, it still won't work. In other words, unless your model root is composed entirely of observable collections, this pattern simply isn't going to work. So, I decided to revisit the singleton pattern (the one I had abandoned in favor of the monostate pattern) and am pleased to report that it actually works.
First, take a look at my model class:
public class ModelRoot : INotifyPropertyChanged
{
...
// member variables .. private, instance-scope...
private static ModelRoot _instance;
private ModelRoot() { // initialize member variables }
public static ModelRoot GetInstance()
{
if (_instance != null) return _instance;
else
{
_instance = new ModelRoot();
return _instance;
}
}
public string Name
{
get { return _name; }
set {
if (_name != value) {
_name = value;
NotifyChanged("Name");
}
}
private void NotifyChanged(string propName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
That's the model class (or at least, the bare bones beginnings of a model class with only a single piece of data (the "Name" property). To create an object provider in your App.xaml file that hosts the new singleton object model, use some XAML that looks like this:
<Application x:Class="modeltest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
xmlns:model="clr-namespace:modeltest"
>
<Application.Resources>
<ObjectDataProvider MethodName="GetInstance" ObjectType="{x:Type model:MyModel}" x:Key="ModelRoot"/>
</Application.Resources>
</Application>And of course, to bind a control to this object you use the same syntax you would with a regular object (or a monostate object):
<TextBlock Text="{Binding Path=Name, Source={StaticResource ModelRoot}}"/>And finally, to make changes to the model from background threads (for example, if you have data streaming in from a background process that you want reflected in your GUI, and your fabolous monostate pattern model didn't work) you can simply obtain a reference to the singleton instance (the one to which the WPF dispatcher is subscribed!!!) as follows:
MyModel m = MyModel.GetInstance();
m.Name = "Modified";
In short, unless you know you're going to be using observable collections, I'd suggest using the singleton model instance pattern described in this post. It is more reliable, it gets around the instance problem and "event cylo" problems, and its actually pretty easy to refactor your monostate model root class into a singleton model root class. So, I am recanting - don't use monostate as your standard data binding modus operandi, because you will find that it works for a while and then all of a sudden breaks when you do something that is vulnerable to event cylo separation.
p.s. I will be comparing WPF binding to Cocoa binding in an upcoming post. My frustration encountered while debugging the monostate pattern stuff and settling on the singleton pattern plays a pretty big part in my overall opinion of WPF and Cocoa bindings.
I look forward to the comparison.
I've been using Cocoa bindings for while, and was looking forward to see
how MS implements it with WPF.