原文地址: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. 【2017集美大学1412软工实践_助教博客】团队作业10——项目复审与事后分析(Beta版本)

    写在前面的话 转眼轰轰烈烈本学期的软工实践就结束了,这个过程中想必在熬夜敲代码,激烈讨论中留下诸多回忆的同时,也收获了不少.恭喜所有团队完成了本阶段冲刺,此外,由于大家的贡献分给的都很平均,将个人贡献 ...

  2. 通过修改my.ini配置文件来解决MySQL 5.6 内存占用过高的问题

    打开后台进程发现mysql占用的内存达到400+M. 修改一下my.ini这个配置文件的配置选项是可以限制MySQL5.6内存占用过高这一问题的,具体修改选项如下: performance_schem ...

  3. sudoku--SE第二次作业

    git传送门 编译环境: windows10.vs2017 所用语言: c++ 首先作为一个晚上闭眼的玩家,我先来讲一下我的心路历程: 最开始接到作业的时候心里是拒绝的,刚出了一趟小远门就这样,就很难 ...

  4. C语言数据在内存分配

    一个由C/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈.程序结束时由编译器自动释放 ...

  5. smarty文件夹,第一个smarty实例总结

    主要是libs里面的smarty类,和init.inc.php配置文件   剩下的是php文件夹.模板文件夹,临时文件夹.缓存文件夹.配置文件夹.插件文件夹   调用main文件夹中的php文件,通过 ...

  6. Java 最常用类(前1000名) 来自GitHub 3000个项目

    这篇文章主要介绍了最常用的1000个Java类(附代码示例),需要的朋友可以参考下 分析Github 3000个开源项目,粗略统计如下.括号内的数字是使用频率 0-3000. 下面的列表显示不全,完整 ...

  7. 02函数-04-箭头函数(ES6)

    ES6新增的函数:Arrow Function,定义方式就是一个箭头 箭头函数相当于匿名函数,并且简化了函数定义,和匿名函数最大的区别在于其内部的this不再"乱跑",而是由上下文 ...

  8. 利用ASCII码生成指定规则的字符串

    /** * 上送终端编号的后两位生成规则 总共可以生成 (36*36-1)1295个编号 * 01...09 0A...0Z * 10...19 1A...1Z * ............... * ...

  9. HDU2688-Rotate

    Recently yifenfei face such a problem that give you millions of positive integers,tell how many pair ...

  10. Java中迭代器Iterator的使用

    Java集合类中Map接口下的相关类并没有像Collection接口的相关类一样实现get()方法,因此在要实现遍历输出的场景中没法直接用get()方法来取得对象中的数据,但Java本身提供了另一种遍 ...