标签:des style blog http color os io strong
WPF的一大基础就是Data Binding。在基于MVVM架构的基础上,只有通过实现INotifyPropertyChanged接口的ViewModel才能够用于Data Binding。
要实现INotifyPropertyChanged接口,只需要实现一个事件,event PropertyChangedEventHandler PropertyChange。
先来回顾下C#里delegate和event的基础知识。
我们知道在C#里,event其实就是一种简化的delegate。所谓的简化其实是为了更好地解决订阅和广播的问题。为什么?我们知道在处理订阅时,各个listener之间应该时相互不知道对方的存在的,并且相互间互不影响。delegate和event都通过+=和-=来注册和反注册listener。而delegate还可以通过=进行赋值。这个=赋值完美地破坏了刚才我们期望的listener之间的相互关系。
如何实现一个event?
我们这里简单列出一个完整的event实例,然后就这个实例来讨论核心问题INotifyPropertyChanged。
1 public class ValueChangedEventArgs : System.EventArgs 2 { 3 public readonly int oldValue; 4 public readonly int newValue; 5 6 public ValueChangedEventArgs(int oldOne, int newOne) 7 { 8 oldValue = oldOne; 9 newValue = newOne; 10 } 11 } 12 13 public class Commodity 14 { 15 string symbol; 16 int volume; 17 18 public event EventHandler<ValueChangedEventArgs> ValueChanged; 19 20 protected void OnValueChanged(ValueChangedEventArgs e) 21 { 22 if (ValueChanged != null) 23 { 24 ValueChanged(this, e); 25 } 26 } 27 28 public int Volume 29 { 30 get { return volume; } 31 set 32 { 33 if (volume == value) { return; } 34 35 int oldValue = volume; 36 volume = value; 37 38 OnValueChanged(new ValueChangedEventArgs(oldValue, volume); 39 } 40 } 41 42 class Test 43 { 44 static void Main() 45 { 46 Commodity c = new Commodity(“600000”); 47 c.Volume = 100; 48 c.ValueChanged += commodity_ValueChanged; 49 c.Volume = 200; 50 } 51 52 static void commodity_ValueChanged(object sender, ValueChangedEventArgs e) 53 { 54 Console.WriteLine(“Volume Changed!”); 55 } 56 }
运行上面的例子我们会发现,c.Volume = 100时执行了OnValueChanged。但是由于ValueChanged这个event为空,OnValueChanged直接返回了。接下来通过给ValueChanged赋上commodity_ValueChanged后继续执行c.Volume = 200,这时控制台就打印出我们想要的结果了。
现在我们回到对INotifyPropertyChanged的讨论上来。
在WPF编程过程中,当我们要实现一个ViewModel时,我们会发现我们通过下面这一行代码就实现了对PropertyChanged这个event的增删操作:
public event PropertyChangedEventHandler PropertyChanged;
为什么说上面这一行实现了event的增删操作?事实上编译器我们后做了很多事情。参考C# 5.0 in a Nutshell: The Definitive Reference,上述的event语句会被编译器展开成类似下面的语句:
1 PropertyChangedEventHandler _propertyChanged; 2 public event PropertyChangedEventHandler PropertyChanged 3 { 4 add { _propertyChanged += value; } 5 remove { _propertyChanged -= value; } 6 }
所以,对于event我们就可以简写成public event SomeEventHandler SomeEvent。
接下来,通过XAML的data binding就可以将控件中的某个属性绑定ViewModel的某个属性上了。比方说Commodity.Volume(这里假定Commodity 实现了INotifyPropertyChanged接口)。
再之后,改变Volume数值,相应控件的绑定属性就同步更改了。
我在接触WPF data binding时,最大的疑惑就在这里。PropertyChanged我从来就没有去显示的初始化,为什么运行时,PropertyChanged非空?是谁初始化了PropertyChanged?
要回答这个问题,不得不说,需要一定的篇幅才能说清楚。
首先我们可以根据现象确定PropertyChanged再运行是是会被初始化的。既然这样,那么我们可以通过设置断点来看到底什么时候会被初始化。
如何设置断点?
我们需要手动将编译器做的事情自己来做一遍。自己写event的add和remove。也就是上文看到的那个样子。
然后再打断点到add行。运行后我们会看到如下堆栈:
OK!PropertyChanged被初始化了。于此同时我们也发现了一个和PropertyChanged相关联的类:PropertyChangedEventManager。
好家伙,这个manager类在MSDN上还有介绍!
PropertyChangedEventManager class provides a WeakEventManager implementation so that you can use the "weak event listener" pattern to attach listeners for the PropertyChanged event.
到此,我们算是明白PropertyChanged是被谁自动初始化的了。不过,我觉得这还不够。既然.Net Framework都开源了,为啥不去看看这个类的源码是怎么处理也个业务逻辑的呢?
打开Reference Source主页,搜索PropertyChangedEventManager,找到StartListening方法:
1 protected override void StartListening(object source) 2 { 3 INotifyPropertyChanged typedSource = (INotifyPropertyChanged)source; 4 typedSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); 5 }
不管怎么说,这个实现简直太直观了。如果你想看OnPropertyChanged是如何实现的,请自行前往。所以当ViewModel里的属性改变后,会调用ViewModel.PropertyChanged,继而调用PropertyChangedEventManager.OnPropertyChanged。PropertyChangedEventManager.OnPropertyChanged会继而调用WeakEventManager.DeliverEventToList,然后调用ListenerList.DeliverEvent。DeliverEvent会把sender和EventArgs e传递给Listener.Handler(sender, e)完成PropertyChanged的整个过程。
关于PropertyChangedEventManager和WeakEventManager是什么东西,请参阅MSDN:Weak Event Patterns。
我们就拿TextBlock.Text来分析这个问题。
首先TextBlock.Text属性是一个dependency property。只有dependency property才能执行data binding操作。
废话不多说,直接上源码:
1 public static readonly DependencyProperty TextProperty = 2 DependencyProperty.Register( 3 "Text", 4 typeof(string), 5 typeof(TextBlock), 6 new FrameworkPropertyMetadata( 7 string.Empty, 8 FrameworkPropertyMetadataOptions.AffectsMeasure | 9 FrameworkPropertyMetadataOptions.AffectsRender, 10 new PropertyChangedCallback(OnTextChanged), 11 new CoerceValueCallback(CoerceText)));
应该就是加粗的那一行用来处理PropertyChanged事件。
简单验证下,在Visual Studio里设置一个断点:PresentationFramework.dll!System.Windows.Controls.TextBlock.OnTextChanged
我这里看到的堆栈调用是:
通过Visual Studio自带的Locals窗口,你还可以看到传入的newText值是多少!
完!
Data Binding和INotifyPropertyChanged是如何协调工作的?,布布扣,bubuko.com
Data Binding和INotifyPropertyChanged是如何协调工作的?
标签:des style blog http color os io strong
原文地址:http://www.cnblogs.com/wpcockroach/p/3909081.html