An elegant way to implement INotifyPropertyChanged
Introduction
A very common and popular way to synchronize data between the model and the view in WPF is using DataBinding. The value of the model is transferred to the view once, when the binding is initialized. But for every subsequent change, the model must notify the binding to transfer the value again. This is done by implementing the INotifyPropertyChanged interface on the model.
In the setter of every bound property there must be some code to raise the PropertyChanged event, passing the name of the modified property as a string . This has some disadvantages:
- You cannot use Auto-properties
- The property name is passed as a string. This brings ugly errors, when you forgot to change the string after a rename.
- Every property needs extra code
The basic way to do it
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
PropertyChanged("Name");
}
}
private void PropertyChanged(string prop)
{
if( PropertyChanged != null )
{
PropertyChanged(this, new PropertyChangedEventArgs(prop);
}
}
A more elegant way to do it
The basic implementation requires two lines of code in the setter and the property name is passed as a string, which is not very robust. A more elegant solution is to use the power of expression trees to get the property name out of a lambda expression. The result looks like this:
private string _name;
public string Name
{
get { return _name; }
set { PropertyChanged.ChangeAndNotify(ref _name, value, () => Name); }
}
public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler,
ref T field, T value, Expression<Func<T>> memberExpression)
{
if (memberExpression == null)
{
throw new ArgumentNullException("memberExpression");
}
var body = memberExpression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException("Lambda must return a property.");
}
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
var vmExpression = body.Expression as ConstantExpression;
if (vmExpression != null)
{
LambdaExpression lambda = Expression.Lambda(vmExpression);
Delegate vmFunc = lambda.Compile();
object sender = vmFunc.DynamicInvoke();
if( handler != null)
{
handler(sender, new PropertyChangedEventArgs(body.Member.Name));
}
}
field = value;
return true;
}
The solution is ispired by the following articles:
Last modified: 2011-03-03 20:28:18
Copyright (c) by Christian Moser, 2011.
Comments on this article
Show all comments
 |
Adrian Cole | |
|
Commented on 4.March 2011 |
The property value needs to be set before the notification.
|
|
|
 |
Jared Barneck | |
|
Commented on 8.March 2011 |
I am testing this out now.
Thanks,
Jared
Http://www.rhyous.com
|
|
|
 |
Manu Reva | |
|
Commented on 26.April 2011 |
What about using Reflection, like "MethodBase.GetCurrentMethod", for giving name of the property.
Do we expect performance issue ? or it is just too ugly :-)
|
|
|
 |
Denis González | |
|
Commented on 12.July 2011 |
What about when you obfuscate the code?
|
|
|
 |
Jim Yost | |
|
Commented on 21.July 2011 |
If I was debugging this code. I would appreciate the basic way
|
|
|
|