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 ...
随机推荐
- 日本IT行业劳动力缺口达22万 在日中国留学生迎来就业好时机 2017/07/18 11:25:09
作者:倪亚敏 来源:日本新华侨报 发布时间:2017/07/18 11:25:09 据日本政府提供的数据,日本2018年应届毕业生的“求人倍率”已经达到了1.78倍.换言之,就是100名大学生 ...
- 团队介绍&学长采访
1. 团队介绍 刘畅 博客园ID:森高Slontia 身份:PM 个人介绍: 弹丸粉 || 小说创作爱好者 || 撸猫狂魔 我绝对不知道,我一个写代码的怎么就当PM去了? 张安澜 博客园ID:Mins ...
- eclipse版本信息及操作系统
一.查看版本信息: 进入到eclipse安装目录下,有一个.eclipseproduct文件,用记事本打开,就可以知道版本了,后面version=的值就是版本 二.是否为32位操作系统: 找到ecli ...
- [Ubuntu] sogou中文输入法安装
I install sogou 中文输入法 successfully, after following below steps: 1. install sogou pingyin by deb pac ...
- 最全的NB-IoT芯片厂商、模组厂商信息
NB-IoT作为LPWAN(低功耗广域网)的新兴技术,因为具有低功耗.低成本.广覆盖.海量节点等优势,并且在授权频段可以与2G.3G无缝连接而被运营商所青睐且接受.特别是到了2017年,据统计全球有5 ...
- Scrum立会报告+燃尽图(十月二十九日总第二十次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2288 项目地址:https://git.coding.net/zhang ...
- c# 画一个报告
填充控件的grafics属性: private void printReportPage(object sender, System.Drawing.Printing.PrintPageEventAr ...
- Android开发第二阶段(6)
今天:对sdcard的操作有了进一步的了解和深入,为程序可以自主扫描并添加sdcard的MP3格式文件 明天:最后的修正.
- python 动态获取当前运行的类名和函数名的方法
一.使用内置方法和修饰器方法获取类名.函数名 python中获取函数名的情况分为内部.外部,从外部的情况好获取,使用指向函数的对象,然后用__name__属性 复制代码代码如下: def a():pa ...
- 关于C语言的问卷调查!!!!!!!!!!
1.我对自己的未来是现在通过大学这一平台逐渐接触社会,通过大学的这段时间学习C语言等计算机语言技术,有一技之长在手,并且通过大学时间丰富自己的业余生活,加强自己的人脉关系,为未来在事业上的发展做准备! ...