原文地址: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. 201521123084 《Java程序设计》第9周学习总结

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

  2. Java星星打印三角形小结

    1.直角三角形的打印

  3. 201521123065 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1.ArrayList只能存放对象: 2.对象包装类之间使用equals进行比较 ...

  4. 201521123006 《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 本周除了继承,我们还重点学习了多态. (1)多态性在于有相同的形态,却是不同的行为或者说是不 ...

  5. 201521123054 《Java程序设计》第14周学习总结

    1. 本周学习总结 2. 书面作业 1. MySQL数据库基本操作 1.1建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 1.2在自己建立的数据库上执行常见SQL语句 ...

  6. 201521123008 《Java程序设计》 第九周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集异常 1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避 ...

  7. Python2.7笔记——常用技术点汇总

    目录 · 概况 · 安装 · 基础 · 基础语法 · 数据类型 · 变量 · 常量 · 字符编码 · 字符串格式化 · list · tuple · dict · set · if语句 · for语句 ...

  8. SpringMVC第一篇【介绍、入门、工作流程、控制器】

    什么是SpringMVC? SpringMVC是Spring家族的一员,Spring是将现在开发中流行的组件进行组合而成的一个框架!它用在基于MVC的表现层开发,类似于struts2框架 为什么要使用 ...

  9. Mysql免安装版配置【图文版和文字版】

    图文版 配置环境变量 新建一个my.ini文件,添加下面内容 [mysqld] basedir=C:\\software\Mysql\mysql-5.7.14-winx64 datadir=C:\\s ...

  10. 与 Hadoop 对比,如何看待 Spark 技术?

    主要是先看MapReduce模型有什么问题? 第一:需要写很多底层的代码不够高效,第二:所有的事情必须要转化成两个操作Map/Reduce,这本身就很奇怪,也不能解决所有的情况. 其实Spark出现就 ...