Fluent Silverlight – Auto Wiring INotifyPropertyChanged
In Gabriel’s introductory post for Fluent Silverlight, he showed that the code typically associated with implementing INotifyPropertyChanged can be reduced to a simple auto property. This can really improve the clarity of a large class as well as save some typing. I’m going to show you how to get it setup.
INotifyPropertyChanged
First lets talk a little about normal INotifyPropertyChanged. This is an interface that is typically implemented on a ViewModel that you wish to participate in two way data binding between itself and some sort of DependencyProperty which typically lives on a control. The interface declaration looks like:
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
public class MyViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
if (value == name) return;
name = value;
OnPropertyChanged("Name");
}
}
private int age;
public int Age
{
get { return age; }
set
{
if (value == age) return;
age = value;
OnPropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
IAutoNotifyPropertyChanged
We got to thinking that if we could intercept the changes to the properties (AOP), then we could automatically throw the PropertyChanged event. One way to do this is to use Castle.DynamicProxy (the Silverlight edition). DynamicProxy allows us to create a runtime generated subclass of our ViewModel. The generated subclass allows interception of any virtual member where we can chose to do what we please. To get it all started, we needed an interface that would allow us to trigger the event:
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
void OnPropertyChanged(string propertyName);
}
Next we updated our ViewModel to implement the new IAutoNotifyPropertyChanged interface and removed all the extras:
public class MyViewModel : IAutoNotifyPropertyChanged
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
As you can see, there is a lot less noise here and we don’t have the magic strings for the property names laying around. Also note that we made the properties virtual so that they can be intercepted. To actually make the auto wiring work, we have to create an instance of the ViewModel class (actually it’s a generated subclass) using our AutoNotifyPropertyChangedProxyCreator. I’m not sure if we’ll stick with that name, but it will do for now. Take a look at the following little test:
[TestFixture]
public class when_creating_viewmodel_with_creator
{
private MyViewModel model;
private string lastPropChanged;
[SetUp]
public void SetUp()
{
model = new AutoNotifyPropertyChangedProxyCreator().Create<MyViewModel>();
model.PropertyChanged += (s, e) => lastPropChanged = e.PropertyName;
lastPropChanged = null;
}
[Test]
public void should_send_property_changed_for_given_property()
{
model.Name = "test";
Assert.That(lastPropChanged, Is.EqualTo("Name"));
Assert.That(model.Name, Is.EqualTo("test"));
}
}
Because the proxy is a subclass, we can treat it as it’s original type MyViewModel. As the Name property changes, it fires the PropertyChanged event which is wired to update the lastPropChanged field.
Summary
INotifyPropertyChanged is a handy interface for doing two way data binding but it causes a lot of extra noise in your classes. In Fluent Silverlight, we have introduced the IAutoNotifyPropertyChanged interface which is used in conjunction with AutoNotifyPropertyChangedProxyCreator which uses Dynamic Proxy to intercept the calls and auto throw the PropertyChanged event. This allowed us to reduce the noise and completely remove the magic strings typically associated with standard INotifyPropertyChanged.
In a future post I’ll demonstrate how you can do other things with this such as ignore certain properties, tap into the interception to execute methods and have an IoC container handle the proxy creation.