在C# 弱事件模式(一)中介绍了弱事件模式是如何工作的,接下来我们介绍弱事件模式是如何在WPF中发挥其强大的作用。
首先,我们实现一个简单的WPF双向绑定
代码:
<!-- MainWindow.xaml -->
<Window.DataContext>
<local:SayHelloViewModel/>
</Window.DataContext>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Name="txbName" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</StackPanel>
<TextBlock Text="{Binding SayHello}"></TextBlock>
</StackPanel>
// SayHelloViewModel.cs
public class SayHelloViewModel : INotifyPropertyChanged
{
private event PropertyChangedEventHandler _PropertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { _PropertyChanged += value; }
remove { _PropertyChanged -= value; }
}
private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
if (_PropertyChanged != null)
this._PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; SayHello = "Hello " + _Name; }
}
private string _SayHello;
public string SayHello
{
get { return _SayHello; }
set { _SayHello = value; RaisePropertyChanged(); }
}
}
效果:
来分析一下SayHelloViewModel ,实现了INotifyPropertyChanged接口,包含一个_PropertyChanged委托及封装了_PropertyChanged的事件PropertyChanged,一个封装了触发_PropertyChanged委托的RaisePropertyChanged方法,还有两个属性Name和SayHello。好像这个类非常平常,没有什么特别的。下面来分析MainWindow和SayHelloViewModel是如何协调工作的。
MainWindow中的一个TextBox的Text属性绑定了Name,TextBox默认是双向绑定,所有在TextBox的Text属性改变时会修改Name的值,从而修改SayHello的值并调用RaisePropertyChanged触发_PropertyChanged事件,最终的结果导致TextBlock的值改变。
我们的核心问题是:SayHello的值被修改后是如何通知到TextBlock并修改Text属性
通用的说法就是:在WPF中绑定源的属性被修改后是如何通知绑定目标,并修改其相应属性值
根据以上的代码知道,核心就在PropertyChanged事件。很奇怪,我们的代码中PropertyChanged并没有被注册,但我们可以肯定PropertyChanged一定被注册过。如果想仔细研究,把代码编译后用ILDASM工具可以找到根源。我们先简单的调试一下,在事件的add位置打上断点,如图:
在SayHelloViewModel完成构造后,PropertyChanged事件被PropertyChangedEventManager注册,终于找到元凶了。在Reference Source中找到PropertyChangedEventManager的源码,派生自WeakEventManager。至此,我们可以确定,绑定源至目标的属性更改通知是通过PropertyChangedEventManager实现的弱事件模式完成工作的。