You may already have read my posts about how to use INotifyPropertyChanged in a type-safe way (here and here), but sometime you don’t want to modify all your classes to use this method. All you want is avoid the use of a magic string to define the property. Your code will be refactoring proof.
For example let say you have this property:
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
Some refactoring tool, like Resharper, will be able to change the “FirstName” string but not Visual Studio itself. The solution? Replace this string with a strong type value. How? Let’s assume we can do this:
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
_firstName = value;
RaisePropertyChanged(this.NameOf(p => p.FirstName));
}
}
Notice that you must specify the “this” keyword to make it work. That is exactly What this extension method let you do:
public static class ObjectExtensions
{
public static string NameOf<T>(this T target, Expression<Func<T, object>> propertyExpression)
{
MemberExpression body = null;
if (propertyExpression.Body is UnaryExpression)
{
var unary = propertyExpression.Body as UnaryExpression;
if (unary.Operand is MemberExpression)
body = unary.Operand as MemberExpression;
}
else if (propertyExpression.Body is MemberExpression)
{
body = propertyExpression.Body as MemberExpression;
}
if (body == null)
throw new ArgumentException("'propertyExpression' should be a member expression");
// Extract the right part (after "=>")
var vmExpression = body.Expression as ConstantExpression;
// Extract the name of the property to raise a change on
return body.Member.Name;
}
}
You can even use it in your property changed handler:
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == this.NameOf(p => p.FirstName))
{
// ...
}
}
Enjoy!
2 comments:
This is excellent stuff! Thanks for sharing.
protected void Notify(T obj, Expression> selector)
{
if (propertyChangedEvent != null)
{
MemberExpression memberExpression = selector.Body as MemberExpression;
if (memberExpression == null)
{
UnaryExpression unary = selector.Body as UnaryExpression;
if (unary != null)
{
memberExpression = unary.Operand as MemberExpression;
}
}
if (memberExpression == null)
throw new ArgumentException("member should be of MemberExpression type", "selector");
propertyChangedEvent(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
Then use
Notify(this, o=> o.PropertyName);
Post a Comment