1.Overview
基於MVVM實現一段綁定大伙都不陌生,Binding是wpf整個體系中最核心的對象之一這里就來解讀一下我花了純兩周時間有哪些秘密。這里我先提出幾個問題應該是大家感興趣的,如下:
(1)INotifyPropertyChanged是如何被加載、觸發的(Binding如何完成數據更新的)?
(2)為什么需要開發者手動實現INotifyPropertyChanged接口來為每個成員實現數據通知,為什么不集成在wpf框架里?
(3)藏在WPF體系里的觀察者模式在哪里?
2.Detail
想了解以上問題,我們先補充以下前置知識點。
我們帶着以上幾個問題來看本文的后續內容,首先我們通過下面這張圖來了解綁定的過程。
根據以上過程我們可以基於MVVM模式下,在Xaml中寫出這樣的語句來表示綁定。
<TextBoxName="mytextbox"Height="25"Width="150"Text="{BindingPath=Name,Mode=**TwoWay**,UpdateSourceTrigger=**PropertyChanged**}"></TextBox>
那么如果把他轉換成c#代碼,將會是如下表示。
public string BeachName{ get; set; } private void Test() { BeachName="BikiniBeach"; TextBoxtextBox = newTextBox(); textBox.Name = "myTextBox"; Binding binding = new Binding(); binding.Source = BeachName; binding.Path = new PropertyPath("BeachName"); textBox.SetBinding(TextBox.TextProperty, binding); }
(1-1)
上面這段代碼,包含了兩個關鍵對象Textbox和Binding它們里面大有文章首先我們逐個拆解這兩個對象里都有什么。
Textbox
在(1-1)的代碼中初始化一個Textbox對象,它會創建一個依賴屬性TextProperty用於綁定要素之一。
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof (Text), typeof (string), typeof (TextBox), (PropertyMetadata) new FrameworkPropertyMetadata((object) string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(TextBox.OnTextPropertyChanged), new CoerceValueCallback(TextBox.CoerceText), true, UpdateSourceTrigger.LostFocus));
Binding
當我們在日常開發實現綁定過程當中,WPF的體系會默默幫你創建Binding對象,這里我們來看看Binding包含了哪些定義(為了觀看體驗刪除了大部分不相關代碼)。
namespace System.Windows.Data { public class Binding : BindingBase { //....其它代碼省略 public static void AddSourceUpdatedHandler( DependencyObject element, EventHandler<DataTransferEventArgs> handler) { UIElement.AddHandler(element, Binding.SourceUpdatedEvent, (Delegate) handler); } public static void RemoveSourceUpdatedHandler( DependencyObject element, EventHandler<DataTransferEventArgs> handler) { UIElement.RemoveHandler(element, Binding.SourceUpdatedEvent, (Delegate) handler); } public static void AddTargetUpdatedHandler( DependencyObject element, EventHandler<DataTransferEventArgs> handler) { UIElement.AddHandler(element, Binding.TargetUpdatedEvent, (Delegate) handler); } public static void RemoveTargetUpdatedHandler( DependencyObject element, EventHandler<DataTransferEventArgs> handler) { UIElement.RemoveHandler(element, Binding.TargetUpdatedEvent, (Delegate) handler); } public Binding(string path) { if (path == null) return; if (Dispatcher.CurrentDispatcher == null) throw new InvalidOperationException(); this.Path = new PropertyPath(path, (object[]) null); } public PropertyPath Path { get => this._ppath; set { this.CheckSealed(); this._ppath = value; this._attachedPropertiesInPath = -1; this.ClearFlag(BindingBase.BindingFlags.PathGeneratedInternally); if (this._ppath == null || !this._ppath.StartsWithStaticProperty) return; if (this._sourceInUse == Binding.SourceProperties.None || this