Task-It Series
This post is part of a series of blog posts and videos about the Task-It (task management) application that I have been building with Silverlight 4 and Telerik's RadControls for Silverlight 4. For a full index of these resources, please go here. One of the posts listed in the index provides a full source download for the application, and I will be referring to that source code in this post.
More about MVVM
I recently received a comment on one of my blog posts about how the reader liked the strongly-typed property changed notification technique that I have in my Task-It, and it occurred to me that I hadn't explained this in my first MVVM blog post. You may or may not know what this is referring to, but I'll start w/ the basics and lead up to it.
INotifyPropertyChanged
If you are already familiar w/ this interface, you may want to skip ahead to the next section. If you are not, the MSDN documentation does a good job of describing it:
"The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed."
So basically, it says to the UI, "Hey, a property's value has changed. If you are bound to it, please re-bind and display the new value".
This is the way that you'll often see it implemented in a view model class, and it is in fact how I implemented it in Task-It.
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
One thing you may notice though is that the name of the class is ViewModelBase. If you use the MVVM pattern, you will probably be calling OnPropertyChanged (or some people prefer to call it NotifyPropertyChanged) in each of your view model classes, so it makes sense to have it in a base class so that we don't have to repeat it. Notice that ViewModelBase is abstract, so you can't create an instance of this class, but you'll want all of your view models to inherit from it.
The problem
So, now that we have access to OnPropertyChanged in our view model classes, we can call OnPropertyChanged("FirstName"), or whatever the property name is that's changed, whenever we want to notify the UI. So you may have a property that looks like this:
public String FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
You will see a *lot* of code out there that does this, but there is a problem. We are passing the name of the property as a string to OnPropertyChanged, which is not strongly-typed. So what if some developer comes along and changes the name of the property to FName (probably not the most realistic sample I could come up with, but you get the idea). What would happen is that the developer would need to remember to do two other things.
One, change the binding in the XAML from {Binding FirstName} to {Binding FName}. The other is that they have to remember to change OnPropertyChanged("FirstName") in the code above to OnPropertyChanged("FName"). If they forget that second part, it'll still compile fine, it's just that the UI will never know when the FName property changes, because we are still telling it that a property named FirstName has changed.
The solution
There has to be a better way, right? Yes, indeed there is. The answer is an extension method on ViewModelBase that exposes a strongly-typed OnPropertyChanged.
// This provides support for strongly-typed property changed notification
public static class ViewModelExtension
{
public static void OnPropertyChanged<
T
, TProperty>(this T observableBase, Expression<
Func
<T, TProperty>> expression) where T : ViewModelBase
{
observableBase.OnPropertyChanged(observableBase.GetPropertyName(expression));
}
public static string GetPropertyName<
T
, TProperty>(this T owner, Expression<
Func
<T, TProperty>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression == null)
{
throw new NotImplementedException();
}
memberExpression = unaryExpression.Operand as MemberExpression;
if (memberExpression == null)
{
throw new NotImplementedException();
}
}
return memberExpression.Member.Name;
}
}
If you're not familiar with extension methods, that's ok, you don't have to be (though I do recommend that you read up on them). All you have to know is that now we have a better way to implement our property. It would now look like this:
public String FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
this.OnPropertyChanged(m => m.FirstName);
}
}
One thing that you need to do to call an extension method is to put "this." before it. If you don't, you won't see this version of the method in Intellisense.
If you're not familiar with lambdas, you don't have to be (though I do recommend that you read up on them also). However, what we have inside the parenthesis is a lambda expression. The "m" in this code represents the view model class, which of course inherits from ViewModelBase (if it doesn't, this won't work).
So inside the parenthesis as I type "m => m.", you'll get Intellisense. It will allow you to choose from the properties in this view model class, and as you can see FirstName is one of them.
This is a much safer way of firing property changed notifications, because now if someone comes along and changes the property name to FName, it won't compile unless you update your lambda expression to "m => m.FName".
If you develop an application using the MVVM pattern you will end up firing property changed notifications a *lot*. So if you do not use this strongly-typed implementation, the chance of even just accidentally misspelling the property name is fairly high, which will result in a bug in your user interface.
Wrap up
The source code for my Task-It application, which you can download here, makes extensive use of this implementation, so feel free to go grab the code!