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