首页 > 代码库 > WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?
WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?
通常实现INotifyPropertyChanged接口很简单,为你的类只实现一个PropertyChanged 的Event就可以了。
例如实现一个简单的ViewModel1类:
public class ViewModel1 : INotifyPropertyChanged { private string _data; public string Data { get { return _data; } set { if (_data =http://www.mamicode.com/= value) return; _data = value; // Type un-safe PropertyChanged raise PropertyChanged(this, new PropertyChangedEventArgs("Data")); } } #region Implementation of INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged = null; #endregion }
上面是一个标准的实现一个可绑定的属性的方式,问题是其中使用了一个“Data”的字符串来表明发生变化的属性名。问题在于如果有人改变了属性的名字而忘记改变这里的字符串的话
,即使编译不会产生问题,但binding也会失败掉。最好的方法当然是当有人改变属性的名字时,编译器可以告诉我们这个地方也需要相应改变。
例如实现下面这样:
private string _data; public string Data { get { return _data; } set { PropertyChanged.ChangeAndNotify(ref _data, value, () => Data); } }
让我们利用扩展方法来扩展PropertyChangedEventHandler实现这个目标:
public static class PropertyChangedEventHandlerExtentions { public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler, ref T field, T value, Expression<Func<T>> memberExpression) { if (memberExpression == null) { throw new ArgumentNullException("memberExpression"); } // Retreive lambda body 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; } // Extract the right part (after "=>") var vmExpression = body.Expression as ConstantExpression; if (vmExpression != null) { // Create a reference to the calling object to pass it as the sender LambdaExpression lambda = Expression.Lambda(vmExpression); Delegate vmFunc = lambda.Compile(); object sender = vmFunc.DynamicInvoke(); if (handler != null) { //body.Member.Name,Extract the name of the property to raise a change on handler(sender, new PropertyChangedEventArgs(body.Member.Name)); } } field = value; return true; } }
方法的核心思想就是1.利用扩展方法来扩展delegate:PropertyChangedEventHandler,2.用引用传值把原来的值传入扩展方法用来做比较,3.用lambda expression表达式树来传递待更新的属性名称来达到类型安全的目的,并且传递了调用这个lambda的对象实例(sender)。
如果上面的方法比较难理解,我觉得下面的方法也是可以接受的:
1.依然传递Magic string,但是可以简化值比较的代码:
public class Data : INotifyPropertyChanged { // boiler-plate public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } protected bool SetField<T>(ref T field, T value, string propertyName) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } // props private string name; public string Name { get { return name; } set { SetField(ref name, value, "Name"); } } }
2.不用传递Magic string,也不用ExtentionMethod,只利用lambda expression来取得属性名:
public class Data2 : INotifyPropertyChanged { // boiler-plate public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression) { if (selectorExpression == null) throw new ArgumentNullException("selectorExpression"); MemberExpression body = selectorExpression.Body as MemberExpression; if (body == null) throw new ArgumentException("The body must be a member expression"); OnPropertyChanged(body.Member.Name); } protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(selectorExpression); return true; } // props private string name; public string Name { get { return name; } set { SetField(ref name, value, () => Name); } } }
完毕。
参考文章:
http://www.wpftutorial.net/INotifyPropertyChanged.html
http://blog.decarufel.net/2009/07/how-to-use-inotifypropertychanged-type_22.html
http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist
作者:Andy Zeng
欢迎任何形式的转载,但请务必注明出处。
http://www.cnblogs.com/andyzeng/p/3710421.html