博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF学习总结1:INotifyPropertyChanged接口的作用
阅读量:6404 次
发布时间:2019-06-23

本文共 9699 字,大约阅读时间需要 32 分钟。

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

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

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#endregionnamespace System.ComponentModel{    // Summary:    //     Notifies clients that a property value has changed.    public interface INotifyPropertyChanged    {        // Summary:        //     Occurs when a property value changes.        event PropertyChangedEventHandler PropertyChanged;    }}
View Code

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;            }        }    }}
View Code

 再创建一个MainWindow.xaml

View Code

在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{    ///     /// Interaction logic for MainWindow.xaml    ///     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";        }    }}
View Code

运行起来,效果如下:

也就是说,给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"));                }            }        }    }}
View Code

运行如下:

可见,只要实现了这个接口,在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 weakReference = (WeakReference)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(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 = -1;                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                }));            }        }    }
View Code

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

public class Binding : BindingBase{  [DefaultValue(0)]  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);    }  }}
View Code
它的类型就是UpdateSourceTrigger枚举,这个枚举类型的值如下:

public enum {     ,     ,     ,     }

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);  }}
View Code

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;        }}
View Code

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

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

 

转载地址:http://msjea.baihongyu.com/

你可能感兴趣的文章
微信小程序联盟:官方文档+精品教程+demo集合(5月31日更新,持续更新中……)...
查看>>
Fastjson 的 Set类型和 WriteClassName 选项引起的BUG
查看>>
翻译: 星球生成 II
查看>>
IOS 多线程
查看>>
python序列化数据本地存放
查看>>
#CCNA#IP地址、子网划分参考资料网址
查看>>
比较不错的图片上传插件
查看>>
判偶不判奇
查看>>
Sequelize 数据库的支持
查看>>
BigDecimal类的加减乘除
查看>>
lighttpd中实现每天一个访问日志文件
查看>>
node.js发送邮件email
查看>>
查看nginx配置文件路径的方法
查看>>
接口性能调优方案探索
查看>>
kali安装包或更新时提示“E: Sub-process /usr/bin/dpkg return”
查看>>
网站管理后台模板 Charisma
查看>>
EL:empty的用法
查看>>
Saltstack配置之 nodegroups
查看>>
Servlet和JSP优化经验总结
查看>>
squid使用rotate轮询(分割)日志
查看>>