转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式
原文地址:http://www.cnblogs.com/xiwang/
序言
借助WPF/Sliverlight强大的数据绑定功能,可以比实现比MFC,WinForm更加优雅轻松的数据绑定。但是在使用WPF/Silverlight绑定时,有件事情是很苦恼的:当ViewModel对象放生改变,需要通知UI。我们可以让VM对象实现INotifyPropertyChanged接口,通过事件来通知UI。但问题就出现这里……
一,描述问题
情形:现在需要将一个Person对象的Name熟悉双向绑定到UI中的TextBox,的确这是一件很简单的事情,但还是描述下:
XAML:
<TextBox Text="{Binding Name,Mode=TwoWay}"/>
C# Code:

public class Person : INotifyPropertyChanged
{
private string m_Name;
public string Name
{
get { return m_Name; }
set
{
if (m_Name == value) return;
m_Name = value;
this.Notify("Name");
}
} public Person()
{
this.m_Name = "墨梅,在这里......";
} public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

是的,这就可以实现了。但是这里一个问题困惑我,曾经就在this.Notify("Name"),将参数写错,UI迟迟得不到响应。这个错误很难发现!!!也很难跟踪,但是这个细微的错误可以导致一个很严重的运行时错误。这的确是一件很苦恼的事情。
二解决问题
方法一:添加验证

public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName); PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
} [Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName; if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}

这里对验证事件参数使用条件编译[Conditional(“DEBUG”)],在release版本中这个函数是不会调用的,比使用#if 等有更明显有优势。
这个方法虽然可以达到目的,但是还是那么的别扭,必须到运行时才能知道是否有错误,所以还是不怎么好。
方法二,使用Lambda表达式,静态扩展语法

public static class NotificationExtensions
{
public static void Notify(this PropertyChangedEventHandler eventHandler, Expression<Func<object>> expression)
{
if( null == eventHandler )
{
return;
}
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var constantExpression = memberExpression.Expression as ConstantExpression;
var propertyInfo = memberExpression.Member as PropertyInfo; foreach (var del in eventHandler.GetInvocationList())
{
del.DynamicInvoke(new object[] {constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)});
}
}
}

这里用使用的静态扩展语法,我还是比较喜欢这个的,但是并不是所有人都喜欢哦。如何使用呢:

public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set
{
this._firstName = value;
this.PropertyChanged.Notify(()=>this.FirstName);
}
}
}

这里还可以添加一个很实用的扩展:

public static void SubscribeToChange<T>(this T objectThatNotifies, Expression<Func<object>> expression, PropertyChangedEventHandler<T> handler)
where T : INotifyPropertyChanged
{
objectThatNotifies.PropertyChanged +=
(s, e) =>
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo; if(e.PropertyName.Equals(propertyInfo.Name))
{
handler(objectThatNotifies);
}
};
}

通过上面的代码,可以订阅熟悉改变事件,如:

myObject.SubscripeToChange(()=>myObject.SomeProperty,SomeProperty_Changed);
And then your handler would look like this: private void SomeProperty_Changed(MyObject myObject)
{
/* ... implement something here */
}

方法三,net4.5,框架提供的解决方法

private string m_myProperty;
public string MyProperty
{
get { return m_myProperty; }
set
{
m_myProperty = value;
OnPropertyChanged();
}
} private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
// ... do stuff here ...
}

属性CallerMemberName的解决办法和方法二是基本相同的,不同的是这个在net框架中解决的。更多信息可以查看CallerMemberName,net4.5还提供了
CallerFilePath,CallerLineNumber,这几很有用的语法
方法四,这个也不错哦

public static class SymbolExtensions
{
public static string GetPropertySymbol<T,R>(this T obj, Expression<Func<T,R>> expr)
{
return ((MemberExpression)expr.Body).Member.Name;
}
}
public class ConversionOptions : INotifyPropertyChanged
{
private string _outputPath;
public string OutputPath
{
get { return _outputPath;}
set
{
_outputPath = value;
OnPropertyChanged(o => o.OutputPath);
}
} private string _blogName;
public string BlogName
{
get { return _blogName;}
set
{
_blogName = value;
OnPropertyChanged(o => o.BlogName);
}
} private string _secretWord;
public string SecretWord
{
get { return _secretWord; }
set
{
_secretWord = value;
OnPropertyChanged(o => o.SecretWord);
}
} protected virtual void OnPropertyChanged<R>(Expression<Func<ConversionOptions, R>> propertyExpr)
{
OnPropertyChanged(this.GetPropertySymbol(propertyExpr));
} protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
} public event PropertyChangedEventHandler PropertyChanged;
}

注释:这里还有更多参考信息,您可以在这里了解更加清楚:
转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式的更多相关文章
- WPF MVVM之INotifyPropertyChanged接口的几种实现方式(转)
原地址:https://www.cnblogs.com/xiwang/archive/2012/11/25/2787358.html 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比 ...
- C#接口的三种实现方式
转自原文C#接口的三种实现方式 public interface MyInterface { /// 下面三个方法的签名都是 /// .method public hidebysig newslot ...
- (C#)WPF:关于INotifyPropertyChanged接口的介绍
注意:INotifyPropertyChanged接口位于System.CompenentModel名称空间中,想使用INotifyPropertyChanged接口时,头文件需添加“using Sy ...
- Java Callable接口与Future接口的两种使用方式
Java Callable.Future的两种使用方式Callable+Futurepublic class Test { public static void main(String[] args) ...
- JavaScript实现接口的三种经典方式
/* 接口:提供一种说明一个对象应该有哪些方法的手段 js中有三种方式实现接口: 1 注释描述接口 2 属性检测接口 3 鸭式辨型接口 */ /* 1 注释描述接口: 不推荐 优点: 利用注解,给出参 ...
- 【转载】 Java中String类型的两种创建方式
本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...
- 通过Javascript调用微软认知服务情感检测接口的两种实现方式
这是今天在黑客松现场写的代码.我们的项目需要调用认知服务的情感识别接口.官方提供了一种方式,就是从一个远程图片进行识别.我另外写了一个从本地文件读取并上传进行识别的例子. 官方文档,请参考 https ...
- 【spring】RestTemplate发送请求,请求第三方接口 的几种请求方式POST,GET,DELETE,PUSH
org.springframework.web.client.RestTemplate 参考地址:http://www.cnblogs.com/UniqueColor/p/7123347.html G ...
- mybatis 批量修改接口的几种实现方式
-----------------我也是有上线的--------------我也是有上线的------------我也是有上线的---------------我也是有上线的-------------- ...
随机推荐
- 201521123084 《Java程序设计》第4周学习总结
第4周作业-面向对象设计与继承 1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 总结: (1)学会了如何给类和方法进行简单的注释: (2)学 ...
- 集美大学网络1413第十三次作业成绩(团队八) -- 第二次项目冲刺(Beta阶段)
题目: 团队作业8--第二次项目冲刺(Beta阶段) 团队作业8-成绩: 团队/分值 新加入成员 角色 技术特点 改善的功能. 原因. bug 新增功能. 方法. 如何实现 团队分工改进. 原因 改 ...
- 扫雷游戏制作过程(C#描述):第四节、菜单操作
前言 这里给出教程原文地址. 该项目已经放在github上托管. 菜单操作 我们现在的程序单击菜单的时候不会有任何反应,这一节我们主要介绍菜单的相关代码,使得菜单能够正常使用. 现在我们希望在对应级别 ...
- #黑客攻防实战详解#[Chapter 1]
1.1 ①配置模拟入侵环境 ②配置网络拓扑
- SharePoint备份文件
stp文件:SharePoint的.stp文件 在做一个和SharePoint有关的项目时,由于对SharePoint的unfamiliar,所以客户发了几个后缀为.stp的文件将我纳闷了半天,不 ...
- Eclipse集成spket插件_Ext-5.1.0
1, spket-1.6.23.jar下载 官网下载:http://www.spket.com/download.html , 百度网盘地址: http://pan.baidu.com/s/1qXH ...
- 添加事务后 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type available
今天遇到了一个奇怪的问题 在没添加事务之前 所有的代码都是好的 , 当我添加了事务之后, 代码报错 org.springframework.beans.factory.NoSuchBeanDef ...
- MySql Jar 包下载
MySql JAR 包下载 我们要使用Spring 链接MySql 需要两个Jar 包 一个是C3p0 一个是MySql 的Connection Jar 包 C3p0: 进入下面的网址 h ...
- [python学习笔记] python程序打包成exe文件
安装 pyinstaller pip3 install pyinstaller 命令 pyinstaller -F -w -i ../ui/icon.ico --clean ../Login.py 参 ...
- 如何快速成长?我的java之路!
由于一些外部的原因,我不得不从自己熟悉的php领域,转战到java战场.我个人觉得还是有些心得吧,不管怎么样,或多或少可能都会有那么些经历的人,和你一起走在这世上!尽管你不知道TA是谁. 其实,转换一 ...