zoukankan      html  css  js  c++  java
  • WPF学习总结1:INotifyPropertyChanged接口的作用

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

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

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

    #region Assembly System.dll, v4.0.0.0
    // C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5System.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;
        }
    }
    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

    <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="350" Width="525">
        <Grid>
            <TextBox Name="txt1" HorizontalAlignment="Left" Height="23" Margin="148,74,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
            <Button Content="Button" HorizontalAlignment="Left" Margin="148,143,0,0" VerticalAlignment="Top" Width="75"/>
    
        </Grid>
    </Window>
    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
    {
        /// <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";
            }
        }
    }
    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<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 = -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 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);
      }
    }
    
     
    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来实现更加高明的订阅,学习中。

  • 相关阅读:
    HDU 6182 A Math Problem 水题
    HDU 6186 CS Course 位运算 思维
    HDU 6188 Duizi and Shunzi 贪心 思维
    HDU 2824 The Euler function 欧拉函数
    HDU 3037 Saving Beans 多重集合的结合 lucas定理
    HDU 3923 Invoker Polya定理
    FZU 2282 Wand 组合数学 错排公式
    HDU 1452 Happy 2004 数论
    HDU 5778 abs 数论
    欧拉回路【判断连通+度数为偶】
  • 原文地址:https://www.cnblogs.com/liuzhendong/p/3286278.html
Copyright © 2011-2022 走看看