The Silverlight and WPF binding system is extremely powerful.
With it, you can bind your UI to your viewmodels (or other backing
objects), bind control values to each other (for example, a slider
controlling an object's scale or rotation) all with little or no
code.
In order for binding to work, it requires a little cooperation
from code. The target of a binding statement (typically a property
of some bit of the UI) must be a dependency property - that is, an
implemented instance of DependencyProperty which can get its value
from external sources. Similarly, the source of the binding must be
able to notify that the value has changed. In the UI layer, this is
typically a DependencyProperty, but in your non-UI objects, you'll
need to implement INotifyPropertyChanged (often called "INPC") and
raise an event whenever values change. Why? Because any class which
has Dependency Properties must derive from DependencyObject, a
constraint which makes no sense in entities, viewmodel and
business-layer classes.
Basic, Verbose, INPC Example
Here's an example using INPC in a viewmodel class.
public class BasicApproach : INotifyPropertyChanged
{
private int _level;
public int Level
{
get { return _level; }
set
{
if (_level != value)
{
_level = value;
NotifyPropertyChanged("Level");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName != value)
{
_lastName = value;
NotifyPropertyChanged("LastName");
NotifyPropertyChanged("FullName");
}
}
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
NotifyPropertyChanged("FirstName");
NotifyPropertyChanged("FullName");
}
}
}
public string FullName
{
get { return FirstName + " " + LastName; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Typically I'll refactor this so I have an abstract base class
named Observable which contains the interface and
event-raising function. Nevertheless, this code is tedious
and error-prone. It's tedious because you have to expand
properties which would have been fine as auto-props. It's
error-prone because you need to pass the string name of the
property to the event. If you make a typo or change the name of the
property without changing the string, binding will never be
notified that the property changed.
The string-based approach, however, was the best for the
framework developers because it is flexible, very fast, and gives
you the option to implement other approaches on top of it.
Other folks have come up with their own base classes, many of
which include optimizations to avoid creating so many instances of
the PropertyChangedEventArgs class and include helpful checking
information using reflection when in debug mode. Here's an example from Josh Smith. One nice
thing about Josh's approach is that takes a performance hit
primarily when in debug mode, not release mode.
There are several other elegant approaches to solving this.
Let's look at the common reflection and lambda approach first.
Using Reflection and Lambda Expressions
One particularly slick approach uses both reflection and lambda
expressions to add in a compile-time check on the property
name.
public class ReflectionLambdaApproach : INotifyPropertyChanged
{
private int _level;
public int Level
{
get { return _level; }
set
{
if (_level != value)
{
_level = value;
NotifyPropertyChanged(() => Level);
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (_lastName != value)
{
_lastName = value;
NotifyPropertyChanged(() => LastName);
NotifyPropertyChanged(() => FullName);
}
}
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
NotifyPropertyChanged(() => FirstName);
NotifyPropertyChanged(() => FullName);
}
}
}
public string FullName
{
get { return FirstName + " " + LastName; }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged<T>(Expression<Func<T>> property)
{
if (PropertyChanged != null)
{
var memberExpression = property.Body as MemberExpression;
PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
}
Now, as this does use reflection, it is much slower than
the hard-coded version. You need to be very aware of this
because the PropertyChanged event can be raised a large number of
times in a class. You'll need to make a decision based on how
you will use this in your
project.
One optimization to this approach would be to cache the property
descriptor information so you reflect on it only once. Another
common optimization is to wrap all the code into a SetProperty (or similar)
method (and this update) which handles the property value
checking, assignment, and event raising.
In any case, the amount of code doesn't change significantly
unless you use the SetProperty approach. What you've gained by this
approach is compile-time checking of the property names - a
non-trivial improvement. Another approach which gives us both the
checking and the code reduction and which has gained traction
lately is IL Weaving. This injects property changed information
into the IL rather than into the source code.
Using IL Weaving
The ins and outs of IL Weaving is too much to include in this
post. However, I want to refer you to Justin Angel's great post on the topic. Justin
goes through from the top down to what it takes to use MSIL Weaving
in your own project. Be sure to read the comments as well.
When using weaving, you can go back to using auto-properties and
leave out all the INPC gunk. It does have its limitations, though.
In the examples you'll find, it doesn't do anything for dependent
properties like our FullName property. You can certainly expand the
examples to include something like that, but I haven't seen it
done.
Now, despite weaving not doing anything for our FullName
property, that's a heck of a lot cleaner than the other approaches
we've been using. It handles the majority of simple cases where the
properties are not dependent upon each other.
Summary
There are tons of other approaches, including this interesting
AutoPropertyChanged approach on codeproject and
this post by Jonas on using a dynamic proxy and Ninject to
do the dirty work. This post is by no means a survey of all of
those, but instead a summary of a couple common approaches. I've
seen some really slick implementations that go above and beyond
what I've done here, so be sure to check them out. A simple bing search for INotifyPropertyChanged will
show you a ton of examples.
In addition, most MVVM frameworks have taken one of these
approaches and built upon it to make your life easier. That's one
of the benefits of picking a MVVM framework when using that pattern
with Silverlight and WPF.
The WPF team is also considering possible approaches for this.
No promises, but it is on their radar :)
If you have a favorite approach I haven't mentioned
here, feel free to link to it or describe it in the
comments.
There's no downloadable source for this post on purpose. Don't
use this code directly, instead go check out some of the more
robust implementations out there. :)