SimplePropertyPath: A Poor Man's Binding
• CommentsThis post illustrates one of several utility classes that are in the Nito.MVVM library: SimplePropertyPath.
SimplePropertyPath is used to create a very simple binding using only INotifyPropertyChanged and not using DependencyProperty or DependencyObject. The System.Windows.Data.Binding class is much more powerful, but is dependent on the WPF-specific dependency property/object system. SimplePropertyPath only uses INotifyPropertyChanged, which gives it two advantages:
- It can be used in non-WPF environments with only minor changes, e.g., using the MVVM pattern on a compact device.
- It allows all ViewModel classes to be POCO (utilizing INotifyPropertyChanged) instead of forcing them to be derived from DependencyObject. (In my mind, at least, DependencyObject or FrameworkElement-derived classes are more of a View class).
SimplePropertyPath merely propagates an existing INotifyPropertyChanged implementation “forward” to other listeners, and propogates writes “back” to the original property. It is only capable of understanding a “simple” property path: one comprised entirely of member accessors.
Some examples will help clarify how this class can be used; the following “fake ViewModel” class is used by these examples. It just has two properties: “int Value” and “FakeVM Child”, and implements INotifyPropertyChanged:
private sealed class FakeVM : INotifyPropertyChanged
{
private int value;
private FakeVM child;
public int Value
{
get { return this.value; }
set
{
this.value = value;
this.OnPropertyChanged("Value");
}
}
public FakeVM Child
{
get { return this.child; }
set
{
this.child = value;
this.OnPropertyChanged("Child");
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
The API of SimplePropertyPath is rather simple:
The two properties “Root” and “Path” are used to define the SimplePropertyPath. The “Value” property is used to read or write the nested property.
A simple example to start off with; to read or write the “Value” property on a FakeVM object:
FakeVM obj = new FakeVM { Value = 13 };
SimplePropertyPath path = new SimplePropertyPath { Root = obj, Path = "Value" };
Assert.AreEqual(13, path.Value);
path.Value = 17;
Assert.AreEqual(17, obj.Value);
Nothing too difficult there. The next example exercises reading and writing to a longer path:
FakeVM obj = new FakeVM { Child = new FakeVM { Value = 10 } };
SimplePropertyPath path = new SimplePropertyPath { Root = obj, Path = "Child.Value" };
Assert.AreEqual(10, path.Value);
path.Value = 17;
Assert.AreEqual(17, obj.Child.Value);
Now it’s starting to act more like a real binding. Invalid property paths will result in a Value of null (writing errors to PresentationTraceSources.DataBindingSource just like WPF data binding does):
FakeVM obj = new FakeVM { Value = 13 };
SimplePropertyPath path = new SimplePropertyPath { Root = obj, Path = "value" };
Assert.IsNull(path.Value);
path.Value = 17;
Assert.AreEqual(13, obj.Value);
Assert.IsNull(path.Value);
The “childmost” property is not the only one that is being monitored; SimplePropertyPath will monitor INotifyPropertyChanged for each object along the path:
FakeVM obj = new FakeVM { Child = new FakeVM { Value = 100 } };
SimplePropertyPath path = new SimplePropertyPath { Root = obj, Path = "Child.Value" };
Assert.AreEqual(100, path.Value);
obj.Child = new FakeVM { Value = 113 };
Assert.AreEqual(113, path.Value);
Finally, SimplePropertyPath will raise its own INotifyPropertyChanged for its Value property every time it changes, whether it was caused by a change in the “childmost” property or any of the objects along the path.
SimplePropertyPath is used by the Nito.MVVM library as a building block to construct some of the more advanced classes, such as MultiProperty and MultiCommand. However, it can be useful in its own right.
[Note: all of the examples above were copied almost verbatim from the unit tests in the Nito.MVVM library].