WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?
通常实现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
作者:Andy Zeng
欢迎任何形式的转载,但请务必注明出处。
http://www.cnblogs.com/andyzeng/p/3710421.html
WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?的更多相关文章
- [译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)
应用不只是包含textboxs和labels,还包含actions,如按钮和鼠标事件等.接下来我们加上一些像按钮这样的UI元素来看MVVM类怎么演变的.与之前的UI相比,这次我们加上一个"C ...
- 转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式
原文地址:http://www.cnblogs.com/xiwang/ 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比实现比MFC,WinForm更加优雅轻松的数据绑定.但是在使用 ...
- WPF编游戏系列 之三 物品清单
原文:WPF编游戏系列 之三 物品清单 本篇将介绍如何通过C#自动生成游戏界面,主要演示点击"My Shop"后如何显示所有物品清单.其中数据源来自于Access 2 ...
- WPF MVVM之INotifyPropertyChanged接口的几种实现方式(转)
原地址:https://www.cnblogs.com/xiwang/archive/2012/11/25/2787358.html 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比 ...
- WPF学习总结1:INotifyPropertyChanged接口的作用
在代码中经常见到这个接口,它里面有什么?它的作用是什么?它和依赖属性有什么关系? 下面就来总结回答这三个问题. 1.这个INotifyPropertyChanged接口里就一个PropertyChan ...
- WPF学习笔记:(二)数据绑定模式与INotifyPropertyChanged接口
数据绑定模式共有四种:OneTime.OneWay.OneWayToSource和TwoWay,默认是TwoWay.一般来说,完成数据绑定要有三个要点:目标属性是依赖属性.绑定设置和实现了INotif ...
- (C#)WPF:关于INotifyPropertyChanged接口的介绍
注意:INotifyPropertyChanged接口位于System.CompenentModel名称空间中,想使用INotifyPropertyChanged接口时,头文件需添加“using Sy ...
- WPF 之 INotifyPropertyChanged 接口的使用 (一)
一.INotifyPropertyChanged 的基本概念 INotifyPropertyChanged 的作用:通知客户端属性值已经更改.详细信息见:INotifyPropertyChange ...
- 如何优雅的实现INotifyPropertyChanged接口
INotifyPropertyChanged接口在WPF或WinFrom程序中使用还是经常用到,常用于通知界面属性变更.标准写法如下: class NotifyObject : INotifyProp ...
随机推荐
- Deeplearning - Overview of Convolution Neural Network
Finally pass all the Deeplearning.ai courses in March! I highly recommend it! If you already know th ...
- 33.[LeetCode] Search in Rotated Sorted Array
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...
- Linux(Contos7.5)环境搭建之Linux远程登录(一)
1.下载<putty-0.70cn.zip>工具包 2.解压到适合的文件夹下
- 腾讯视频qlv格式转换MP4普通视频方法
QLV格式视频不是那么好对付的,似乎是一种加密格式,试着把.qlv改成.mp4或.flv都没有用,用格式工厂等转换软件转换也根本无法识别.但这并不意味着没有办法,其实真正的方法是不用任何工具: 1,我 ...
- SAP(ABAP) ABAP内部外部数据转换常用function
文本相关CONVERSION_EXIT_CUNIT_OUTPUT 将内部单位转为单位文本CONVERSION_EXIT_ISOLA_OUTPUT 根据语言代码取文本CONVERSI ...
- activemq 持久化
转自: http://blog.csdn.net/kobejayandy/article/details/50736479 消息持久性的原理很简单,就是在发送者将消息发送出去后,消息中心首先将消息存储 ...
- NFS 它的目的就是想让不同的机器、不同的作业系统可以彼此分享个别的档案啦
NFS即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源.在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件, ...
- sublime插件时间
import datetime import sublime_plugin class AddCurrentTimeCommand(sublime_plugin.TextCommand): def r ...
- 分享几个IP获取地理位置的API接口(最全面的了)
转载;https://cloud.tencent.com/developer/article/1152362 全网首发,最全的IP接口,不服来辩!博主找了几个小时的资料,又手动抓取到了几个接口补充进来 ...
- oracle 绝对值小于1的数值显示小数点前面的0
SELECT DECODE(TRUNC(-.98),0,REPLACE(TO_CHAR(-.98), '.', '0.'),TO_CHAR(-.98))FROM DUAL;