原文地址: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

ingebrigtsen

MSDN

dorony blogs

转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式的更多相关文章

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

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

  2. C#接口的三种实现方式

    转自原文C#接口的三种实现方式 public interface MyInterface { /// 下面三个方法的签名都是 /// .method public hidebysig newslot ...

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

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

  4. Java Callable接口与Future接口的两种使用方式

    Java Callable.Future的两种使用方式Callable+Futurepublic class Test { public static void main(String[] args) ...

  5. JavaScript实现接口的三种经典方式

    /* 接口:提供一种说明一个对象应该有哪些方法的手段 js中有三种方式实现接口: 1 注释描述接口 2 属性检测接口 3 鸭式辨型接口 */ /* 1 注释描述接口: 不推荐 优点: 利用注解,给出参 ...

  6. 【转载】 Java中String类型的两种创建方式

    本文转载自 https://www.cnblogs.com/fguozhu/articles/2661055.html Java中String是一个特殊的包装类数据有两种创建形式: String s ...

  7. 通过Javascript调用微软认知服务情感检测接口的两种实现方式

    这是今天在黑客松现场写的代码.我们的项目需要调用认知服务的情感识别接口.官方提供了一种方式,就是从一个远程图片进行识别.我另外写了一个从本地文件读取并上传进行识别的例子. 官方文档,请参考 https ...

  8. 【spring】RestTemplate发送请求,请求第三方接口 的几种请求方式POST,GET,DELETE,PUSH

    org.springframework.web.client.RestTemplate 参考地址:http://www.cnblogs.com/UniqueColor/p/7123347.html G ...

  9. mybatis 批量修改接口的几种实现方式

    -----------------我也是有上线的--------------我也是有上线的------------我也是有上线的---------------我也是有上线的-------------- ...

随机推荐

  1. 【Alpha】——Seventh Scrum Meeting

    一.今日站立式会议照片 二.每个人的工作 成员 昨天已完成的工作 今天计划完成的工作 李永豪 将项目做成APK 用户界面改善 郑靖涛 协助设计账目一览表板块 用户界面改善 杨海亮 查询功能测试 用户界 ...

  2. 201521123062《Java程序设计》第2周学习总结

    1.本周学习总结 声明一个变量后必须对变量进行显式的初始化: 各种数据类型,运算符,表达式的使用: 枚举是初步了解及使用: 字符串String类 2.书面作业 Q1.使用Eclipse关联jdk源代码 ...

  3. 201521123024 java 第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 finally 题目4-2 1.1 截图你的提交结果(出现学 ...

  4. hadoop运行wordcount实例,hdfs简单操作

    1.查看hadoop版本 [hadoop@ltt1 sbin]$ hadoop version Hadoop -cdh5.12.0 Subversion http://github.com/cloud ...

  5. Memcached-高性能的分布式内存缓存服务器

    Memcached是高性能的分布式内存缓存服务器,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像.视频.文件以及数据库检索的结果等, 由国外社区网站 LiveJou ...

  6. Azure ARM (17) 基于角色的访问控制 (Role Based Access Control, RBAC) - 自定义Role

    <Windows Azure Platform 系列文章目录> 在上面一篇博客中,笔者介绍了如何在RBAC里面,设置默认的Role. 这里笔者将介绍如何使用自定的Role. 主要内容有: ...

  7. pig报错

    pig failed to read data from....... 错误可能1:load data的目录不在,或者引用出错,load data '/in/train'这里的红色/应该去掉,因为默认 ...

  8. 关于Visio Studio 2012使用Nuget获取Sqlite驱动包报错:“System.Data.SQLite.EF6”的架构版本与 NuGet 的版本 2.0.30625.9003 不兼容

    背景 笔者的VS2012版本比较老旧,是几年以前下载的.平时添加三方包和驱动包都是手动添加.后来了解到有Nuget这个工具,如获至宝.可是在使用过程中却出了不少问题. 最初,笔者尝试使用Nuget添加 ...

  9. Node.js博客搭建

    Node.js 博客搭建 一. 学习需求 Node 的安装运行 会安装node,搭建node环境 会运行node. 基础模块的使用 Buffer:二进制数据处理模块 Event:事件模块 fs:文件系 ...

  10. XML(二)之DTD——XML文件约束

    前面介绍了XML的作用和基本的格式,今天我给大家分享的是关于XML的约束.废话不多说,我们直接来正题! 一.DTD简介 1.1.DTD概述 DTD(Document Type Definition,文 ...