通常实现INotifyPropertyChanged接口很简单,为你的类只实现一个PropertyChanged 的Event就可以了。

例如实现一个简单的ViewModel1类:

 public class ViewModel1 : INotifyPropertyChanged
{
private string _data;
public string Data
{ get { return _data; }
set
{
if (_data == value)
return;
_data = value;
// Type un-safe PropertyChanged raise
PropertyChanged(this, new PropertyChangedEventArgs("Data"));
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = null;
#endregion
}

ViewModel1

上面是一个标准的实现一个可绑定的属性的方式,问题是其中使用了一个“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;
}
}

PropertyChangedEventHandlerExtentions

方法的核心思想就是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

WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?的更多相关文章

  1. [译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)

    应用不只是包含textboxs和labels,还包含actions,如按钮和鼠标事件等.接下来我们加上一些像按钮这样的UI元素来看MVVM类怎么演变的.与之前的UI相比,这次我们加上一个"C ...

  2. 转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式

    原文地址:http://www.cnblogs.com/xiwang/ 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比实现比MFC,WinForm更加优雅轻松的数据绑定.但是在使用 ...

  3. WPF编游戏系列 之三 物品清单

    原文:WPF编游戏系列 之三 物品清单        本篇将介绍如何通过C#自动生成游戏界面,主要演示点击"My Shop"后如何显示所有物品清单.其中数据源来自于Access 2 ...

  4. WPF MVVM之INotifyPropertyChanged接口的几种实现方式(转)

    原地址:https://www.cnblogs.com/xiwang/archive/2012/11/25/2787358.html 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比 ...

  5. WPF学习总结1:INotifyPropertyChanged接口的作用

    在代码中经常见到这个接口,它里面有什么?它的作用是什么?它和依赖属性有什么关系? 下面就来总结回答这三个问题. 1.这个INotifyPropertyChanged接口里就一个PropertyChan ...

  6. WPF学习笔记:(二)数据绑定模式与INotifyPropertyChanged接口

    数据绑定模式共有四种:OneTime.OneWay.OneWayToSource和TwoWay,默认是TwoWay.一般来说,完成数据绑定要有三个要点:目标属性是依赖属性.绑定设置和实现了INotif ...

  7. (C#)WPF:关于INotifyPropertyChanged接口的介绍

    注意:INotifyPropertyChanged接口位于System.CompenentModel名称空间中,想使用INotifyPropertyChanged接口时,头文件需添加“using Sy ...

  8. WPF 之 INotifyPropertyChanged 接口的使用 (一)

    一.INotifyPropertyChanged 的基本概念 ​ INotifyPropertyChanged 的作用:通知客户端属性值已经更改.详细信息见:INotifyPropertyChange ...

  9. 如何优雅的实现INotifyPropertyChanged接口

    INotifyPropertyChanged接口在WPF或WinFrom程序中使用还是经常用到,常用于通知界面属性变更.标准写法如下: class NotifyObject : INotifyProp ...

随机推荐

  1. django项目中关于跨域CORS

    1.使用django-cors-headers扩展,但首先进行安装 2.在配置中添加应用 3.在中间层中设置:“corsheaders.middleware.CorsMiddleware” 4.添加C ...

  2. kaldi DNN在线解码 aishell为例

    在kaldi 的工具集里有好几个程序可以用于在线识别.这些程序都位在src/onlinebin文件夹里,他们是由src/online文件夹里的文件编译而成(你现在可以用make ext 命令进行编译) ...

  3. app结合unity3D程序中遇到的问题 MapFileParser unity3d导出到IOS程序下 集成unity3dAR功能

    转载自: 来自AR学院(www.arvrschool.com),原文地址为:http://www.arvrschool.com/index.php?c=post&a=modify&ti ...

  4. C++ map 遍历

    #include <iostream> #include <map> using namespace std; int main(){ map<int,int> m ...

  5. Scrum立会报告+燃尽图(十一月二十二日总第三十次):加强回归测试

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2410 项目地址:https://git.coding.net/zhang ...

  6. Scrum立会报告+燃尽图(十月十七日总第八次)

    本次作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2246 一.小组介绍 组名:杨老师粉丝群 组长:乔静玉 组员:吴奕瑶.公 ...

  7. 解决因生成javadoc失败导致Maven打包失败问题

    方案就是设置javadoc生成失败时不导致整个打包失败: <plugin> <groupId>org.apache.maven.plugins</groupId> ...

  8. jdbc 2.0

    1.Statement接口不能接受参数 2.PreparedStatement接口在运行时接受输入参数 3.CallableStatement接口也可以接受运行时输入参数,当想要访问数据库存储过程时使 ...

  9. 分享几个IP获取地理位置的API接口(最全面的了)

    转载;https://cloud.tencent.com/developer/article/1152362 全网首发,最全的IP接口,不服来辩!博主找了几个小时的资料,又手动抓取到了几个接口补充进来 ...

  10. canvas - 简单的神经网络

    1.国际惯例,先上效果图 一下效果图使用三次贝塞尔曲线进行连线,代码中有直接使用直线连线的代码,可直使用. 2.查看演示请看 这里. 3 代码     html: <canvas id=&quo ...