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 ...
随机推荐
- charles 在mac下 抓取 https包
1. 打开charles --> help --> SSL proxying --> install charles root certificate 2. 在弹出的添加证书窗口中 ...
- 曾经我是一个只会excel的数据分析师,直到我遇到了……
我是一个数据分析师. 准确来说我是一个当年只会excel数据透视表,就天不怕地不怕地来当数据分析师的人.当年的某一天,我的老板Q我: 小刘啊,我小姨子给了我一个全国市委书记的名单,你帮我看看,有什么规 ...
- Live Archive 训练题
7091 Height Ordering Mrs. Chambers always has her class line up in height order (shortest at the fro ...
- 互评Alpha版本——二次元梦之队——“I Do”
基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 随着智能科技的发展和普及,编程教育的重要性已经逐渐凸显出来.美国前总统奥巴马曾说“编程应当与 ...
- “我爱淘”第二冲刺阶段Scrum站立会议7
完成任务: 完成学院分类的点击查看书籍功能,可以点击书的条目查看书的详细信息.将登陆界面以及注册发布界面完善了一下修复一些bug. 计划任务: 将书的详细信息进行完善,并且可以点击收藏以及已预订等功能 ...
- Java微笔记(7)
String 类常用方法 注意点: 字符串 str 中字符的索引从0开始,范围为 0 到 str.length()-1 使用 indexOf 进行字符或字符串查找时,如果匹配返回位置索引:如果没有匹配 ...
- Task Class .net4.0异步编程类
文章:Task Class 地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task?view=netfra ...
- Qt使用QNetworkAccessManager实现Http操作
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Qt使用QNetworkAccessManager实现Http操作 本文地址:http ...
- 利用Docker安装Web前端性能测试工具Sitespeed.io
目录结构 一.Sitespeed.io概述 1.Sitespeed.io简介 2.Sitespeed.io使用场景 二.Sitespeed.io的安装和使用 1.安装Sitespeed.io 2.连接 ...
- 最简单的Linux下apache+mysql+php安装
转载:http://www.jb51.net/article/29843.htm ubuntu下需要先更新系统后 流程笔记: 1.打开终端,输入“sudo apt-get install apache ...