在代码中经常见到这个接口,它里面有什么?它的作用是什么?它和依赖属性有什么关系?

下面就来总结回答这三个问题。

1.这个INotifyPropertyChanged接口里就一个PropertyChanged的event,这个接口其实是从.net 2.0就引入进来的,用它实现观察者模式很是方便。

#region Assembly System.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.dll
#endregion namespace System.ComponentModel
{
// Summary:
// Notifies clients that a property value has changed.
public interface INotifyPropertyChanged
{
// Summary:
// Occurs when a property value changes.
event PropertyChangedEventHandler PropertyChanged;
}
}

2.它的作用是什么?

首先创建一个不用这个接口的例子。

创建一个Employee.cs类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace WpfAppLearning
{
public class Employee {
private string _name;
public string Name
{
get {
return _name;
}
set
{
_name = value; }
}
}
}

再创建一个MainWindow.xaml

<Window x:Class="WpfAppLearning.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="" Width="">
<Grid>
<TextBox Name="txt1" HorizontalAlignment="Left" Height="" Margin="148,74,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width=""/>
<Button Content="Button" HorizontalAlignment="Left" Margin="148,143,0,0" VerticalAlignment="Top" Width=""/> </Grid>
</Window>

在MainWindow.xaml.cs里将Employee的Name属性和TextBox的Text属性绑定起来。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace WpfAppLearning
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); //新建一个员工,并给员工姓名赋初值
Employee employee = new Employee();
employee.Name = "Tom"; //创建绑定
Binding bind = new Binding();
bind.Source = employee;
bind.Path = new PropertyPath("Name"); //设置绑定
this.txt1.SetBinding(TextBox.TextProperty, bind); //修改员工姓名以后
employee.Name = "Bob";
}
}
}

运行起来,效果如下:

也就是说,给textbox绑定了数据源Employee对象之后,我修改了Employee对象的Name属性,但在界面上并没有显示出来,界面上显示的还是原来的初始值。

这时,可以让PropertyChanged登场了,其他都不动,只重新修改Employee.cs代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel; namespace WpfAppLearning
{
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
}

运行如下:

可见,只要实现了这个接口,在Name属性值改变时激发一下PropertyChanged这个event,就能使binding得到变更通知了。

显然,在创建Binding对象并将它作为数据源绑定到TextBox控件时,TextBox控件自动订阅了这个PropertyChanged event。

但它是在哪里订阅的呢?很想知道,于是...

在Reflector里查看Binding.cs的代码,从它的构造函数,到Source及Path属性的代码中都找不到订阅该event的踪影。

    public class Binding : BindingBase
{
public Binding()
{
} public object Source
{
get
{
WeakReference<object> weakReference = (WeakReference<object>)base.GetValue(BindingBase.Feature.ObjectSource, null);
if (weakReference == null)
{
return null;
}
object result;
if (!weakReference.TryGetTarget(out result))
{
return null;
}
return result;
}
set
{
base.CheckSealed();
if (this._sourceInUse != Binding.SourceProperties.None && this._sourceInUse != Binding.SourceProperties.Source)
{
throw new InvalidOperationException(SR.Get("BindingConflict", new object[]
{
Binding.SourceProperties.Source,
this._sourceInUse
}));
}
if (value != DependencyProperty.UnsetValue)
{
base.SetValue(BindingBase.Feature.ObjectSource, new WeakReference<object>(value));
this.SourceReference = new ExplicitObjectRef(value);
return;
}
base.ClearValue(BindingBase.Feature.ObjectSource);
this.SourceReference = null;
}
} public PropertyPath Path
{
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this._ppath;
}
set
{
base.CheckSealed();
this._ppath = value;
this._attachedPropertiesInPath = -;
base.ClearFlag(BindingBase.BindingFlags.PathGeneratedInternally);
if (this._ppath == null || !this._ppath.StartsWithStaticProperty)
{
return;
}
if (this._sourceInUse == Binding.SourceProperties.None || this._sourceInUse == Binding.SourceProperties.StaticSource)
{
this.SourceReference = Binding.StaticSourceRef;
return;
}
throw new InvalidOperationException(SR.Get("BindingConflict", new object[]
{
Binding.SourceProperties.StaticSource,
this._sourceInUse
}));
}
}
}

实际上Binding类中有一个UpdateSourceTrigger属性:

public class Binding : BindingBase
{ [DefaultValue()]
public UpdateSourceTrigger UpdateSourceTrigger
{
get
{
switch (base.GetFlagsWithinMask(BindingBase.BindingFlags.UpdateOnLostFocus | BindingBase.BindingFlags.UpdateExplicitly))
{
case BindingBase.BindingFlags.UpdateOnPropertyChanged:
return UpdateSourceTrigger.PropertyChanged; case BindingBase.BindingFlags.UpdateOnLostFocus:
return UpdateSourceTrigger.LostFocus; case BindingBase.BindingFlags.UpdateExplicitly:
return UpdateSourceTrigger.Explicit; case (BindingBase.BindingFlags.UpdateOnLostFocus | BindingBase.BindingFlags.UpdateExplicitly):
return UpdateSourceTrigger.Default;
}
Invariant.Assert(false, "Unexpected UpdateSourceTrigger value");
return UpdateSourceTrigger.Default;
}
set
{
base.CheckSealed();
BindingBase.BindingFlags flags = BindingBase.FlagsFrom(value);
if (flags == BindingBase.BindingFlags.IllegalInput)
{
throw new InvalidEnumArgumentException("value", (int) value, typeof(UpdateSourceTrigger));
}
base.ChangeFlagsWithinMask(BindingBase.BindingFlags.UpdateOnLostFocus | BindingBase.BindingFlags.UpdateExplicitly, flags);
}
}
}
它的类型就是UpdateSourceTrigger枚举,这个枚举类型的值如下:

public enum UpdateSourceTrigger {     Default,     PropertyChanged,     LostFocus,     Explicit }

However, the default value for most dependency properties is System.Windows.Data.UpdateSourceTrigger.PropertyChanged,所以说,创建binding对象时虽然没有设置这个属性,但因为它有默认值,是PropertyChanged,如下:

//创建绑定
            Binding bind = new Binding();
            bind.Source = employee;
            bind.Path = new PropertyPath("Name");
            //bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; 这句可以省略。

这样看来,似乎已经找到根了,此时Binding对象应该知道了要监听PropertyChanged事件了,但实际上还没有具体订阅上,到底在哪里订阅上PropertyChanged事件的呢?

Debug一下,发现在创建完上面的绑定之后, employee.PropertyChanged为空,可见,此时还未订阅。

employee.PropertyChanged
null

当向下执行完this.txt1.SetBinding(TextBox.TextProperty, bind) 这句后, employee.PropertyChanged不为空了,说明此时已经订阅上了。

employee.PropertyChanged
{Method = {Void OnPropertyChanged(System.Object, System.ComponentModel.PropertyChangedEventArgs)}}
    base {System.MulticastDelegate}: {Method = {Void OnPropertyChanged(System.Object, System.ComponentModel.PropertyChangedEventArgs)}}

看看这个this.txt1.SetBinding的Reflector代码,实际上还是调用的BindingOperations类的SetBinding方法。

[RuntimeNameProperty("Name"), UsableDuringInitialization(true), StyleTypedProperty(Property="FocusVisualStyle", StyleTargetType=typeof(Control)), XmlLangProperty("Language")]
public class FrameworkElement : UIElement, IFrameworkInputElement, IInputElement, ISupportInitialize, IHaveResources, IQueryAmbient
{
public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding)
{
return BindingOperations.SetBinding(this, dp, binding);
}
}

BindingOperations类的SetBinding方法代码如下:

using MS.Internal.Data;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime;
namespace System.Windows.Data
{
public static BindingExpressionBase SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (dp == null)
{
throw new ArgumentNullException("dp");
}
if (binding == null)
{
throw new ArgumentNullException("binding");
}
BindingExpressionBase bindingExpressionBase = binding.CreateBindingExpression(target, dp);
target.SetValue(dp, bindingExpressionBase);
return bindingExpressionBase;
}
}

再跳进去查,还是没有发现具体订阅的代码,看来还藏得够隐蔽的!算了,不查了,以后再说。

2013/9/3 补充,之所以找不到显式的事件订阅,可能是使用了weakreference来实现更加高明的订阅,学习中。

WPF学习总结1:INotifyPropertyChanged接口的作用的更多相关文章

  1. C#学习笔记(17)——C#中接口的作用

    说明(2017-7-17 23:50:48): 原文: 接口的作用 接口的作用(C#)(另一篇) C#接口是一个让很多初学C#者容易迷糊的东西,用起来好像很简单,定义接口,里面包含方法,但没有方法具体 ...

  2. 深入浅出分析C#接口的作用

    1.C#接口的作用 :C#接口是一个让很多初学C#者容易迷糊的东西,用起来好像很简单,定义接口,里面包含方法,但没有方法具体实现的代码,然后在继承该接口的类里面要实现接口的所有方法的代码,但没有真正认 ...

  3. C#接口的作用详解

    .C#接口的作用 : C#接口是一个让很多初学C#者容易迷糊的东西,用起来好像很简单,定义接口,里面包含方法,但没有方法具体实现的代码,然后在继承该接口的类里面要实现接口的所有方法的代码,但没有真正认 ...

  4. INotifyPropertyChanged接口的实现

    何时实现INotifyPropertyChanged接口 官方解释:INotifyPropertyChanged  接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知.官方解释的很模 ...

  5. INotifyPropertyChanged接口的详细说明

    在windows phone开发8.1:数据绑定中,我们了解了数据绑定的基本知识.今后几篇文章会继续深入了解数据绑定.今天我们来看在数据绑定中十分重要的INotifyPropertyChanged接口 ...

  6. WPF学习笔记:(二)数据绑定模式与INotifyPropertyChanged接口

    数据绑定模式共有四种:OneTime.OneWay.OneWayToSource和TwoWay,默认是TwoWay.一般来说,完成数据绑定要有三个要点:目标属性是依赖属性.绑定设置和实现了INotif ...

  7. [译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)

    应用不只是包含textboxs和labels,还包含actions,如按钮和鼠标事件等.接下来我们加上一些像按钮这样的UI元素来看MVVM类怎么演变的.与之前的UI相比,这次我们加上一个"C ...

  8. 转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式

    原文地址:http://www.cnblogs.com/xiwang/ 序言 借助WPF/Sliverlight强大的数据绑定功能,可以比实现比MFC,WinForm更加优雅轻松的数据绑定.但是在使用 ...

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

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

随机推荐

  1. SpringMVC+Spring+mybatis项目从零开始--Spring mybatis mysql配置实现

    上一章我们把SSM项目结构已搭建(SSM框架web项目从零开始--分布式项目结构搭建)完毕,本章将实现Spring,mybatis,mysql等相关配置. 1.    外部架包依赖引入 外部依赖包引入 ...

  2. Aerospike系列:1:安装

    1:下载源文件 wget http://www.aerospike.com/artifacts/aerospike-server-community/3.5.9/aerospike-server-co ...

  3. Calling a PL/SQL procedure in ODI

    新建procedure 新建一个测试表格EMP: CREATE TABLE EMP ( ) CONSTRAINT PK_EMP PRIMARY KEY, ENAME ), JOB ), MGR ), ...

  4. 基于py3和pymysql的数据库查询,查询某几列的数据

    #python3 #xiaodeng #基于py3和pymysql的数据库查询,查询某几列的数据 import pymysql conn=pymysql.connect(....) cur=conn. ...

  5. 安装win和xp双系统 若干问题

    装了winxp和linux双系统,后先在winxp下手动格式化了linux系统,后启动时提示grub错误,重新分区后系统也 2010-09-17 21:07 [清风剑] | 分类:Linux | 浏览 ...

  6. 初次使用Microsoft Azure

    一.介绍 在微博上偶然发现微软的Azure有免费申请试用的机会,于是赶快给微软发邮件申请,第二天就通过了. 早就听说过微软在云计算方面发力,但一直没机会试用,之前用过国产的BAE.SAE,用GoAge ...

  7. 【linux】系统初始化的shell脚本

    根据参考网上的一些文章,总结出来一个系统初始化的shell脚本 1.初始化脚本 #!/bin/bash cat << EOF +------------------------------ ...

  8. [转]TensorFlow---岂止深度学习

    原文链接 TensorFlow不仅可以用于深度学习自动求导,它也可用于构建传统机器学习和经典算法. TensorFlow提供了"一揽子"常用数值计算和机器学习算法的构建模块.在本文 ...

  9. iphone 恢复出厂设置方法

    1.下载安装并打开itunes. 2.让手机进入恢复模式: 一.先长按住电源键,出现关机选项时,请滑动关机: 二.随后再按电源键开机,屏幕会出现苹果标志,不要松开电源键: 三.接着再按住主屏 Home ...

  10. 如何设置IIS程序池的回收时间,才能最大程度的减少对用户的影响?

    作为.Net开发人员,其实对IIS的应用程序池知之甚少,在工作中我也有几次遇到过网站无故打不开的情况,找了半天原因也找不到是怎么造成的,有一次我给网站找了一个程序程序池后发现就能正常访问了,这也让我对 ...