MVVM模式和在WPF中的實現(二)資料繫結

durow發表於2015-10-06

MVVM模式解析和在WPF中的實現(二)

資料繫結

系列目錄:

MVVM模式解析和在WPF中的實現(一)MVVM模式簡介

MVVM模式解析和在WPF中的實現(二)資料繫結

MVVM模式解析和在WPF中的實現(三)命令繫結

MVVM模式解析和在WPF中的實現(四)事件繫結

MVVM模式解析和在WPF中的實現(五)View和ViewModel的通訊 

MVVM模式解析和在WPF中的實現(六)用依賴注入的方式配置ViewModel並註冊訊息

0x00 資料繫結要達到的效果

資料繫結要達到什麼效果呢,就是在介面中繫結了資料來源之後,資料在介面上的修改能反映到繫結源,同時繫結源的修改也能反映到介面上。從介面反映到繫結的資料來源是很容易理解的,因為在繫結過程中我們指定了DataContext和Binding的物件,很容易找到繫結的源並修改。但資料來源修改時怎麼通知介面呢?因為ViewModel中被繫結的屬性並不知道誰繫結了它,如果在ViewModel中存一個View的引用,在資料發生變化時修改View,這無疑又將ViewModel和View耦合在了一起,而且這樣做View中相應的控制元件沒有開發完善難以進行測試,同樣View中控制元件型別或名稱發生改變時,ViewModel中相關程式碼都需要修改。在WPF中從資料來源通知介面發生變化是通過傳送通知的方式進行的,你可以想象一個string型別的Property,名字是TestString,在它發生變化時對著View大喊“TestString發生變化了,你們誰繫結了TestString需要跟著變啊!”,至於繫結的是TextBlock的Text,還是Label的Content,還是TextBox的Text,ViewModel並不關心,同樣喊了後結果如何ViewModel也不關心。View在收到這個通知後看有沒有繫結 了TestString的地方,找到了就修改,找不到就不管了,也不會在乎這個通知是哪個型別的ViewModel發的。這樣ViewModel和View就解耦了,誰也不依賴對方。

0x01 INotifyPropertyChanged介面

在WPF中能夠實現ViewModel向View喊話功能的就是INotifyPropertyChanged介面,它就像一個大喇叭一樣,我們實現了這個介面,就可以通過觸發PropertyChanged事件並給出改變的資料來源的物件和屬性名稱,以此來通知資料的變化。這個介面的實現是非常簡單的,下圖程式碼就是一種非常簡易的實現方式。由於在MVVM中所有的ViewModel和部分Model都需要實現這個介面來達到繫結的效果,因此一般會專門用一個類來實現這個介面,並將這個類作為ViewModel等需要資料更改後傳送通知的類的基類。

 

0x02 ObservableCollection<T>集合

NotifyObject貌似把一切都解決了,但是考慮這樣一種情況,有一個List<string>列表顯示人員名單,View中的一個ListBox繫結了這個列表。每次我們新增新成員時,為了能在View中立即看到結果必須呼叫RaisePropertyChanged方法讓ListBox控制元件重新載入資料來源。這種做法執行了大量的無效操作,就像我們只是改變了螢幕上一小部分內容卻要重新整理整個螢幕以更新顯示一樣。如果新增的不是人員而是一種新增動作頻繁發生的操作,這種無效操作會極大影響效能。因此WPF提供了一個ObservableCollection<T>集合,可以將資料項的新增、刪除等反映到View中繫結的控制元件上而無需我們做任何操作。所以在遇到對集合新增、刪除等操作又需要使用資料繫結時要優先考慮ObservalbeCollection<T>。

0x03 資料繫結的示例

有了NotifyObject這個基類後,我們就可以測試資料繫結了。為了讓示例針對性更強,該示例僅僅用到了資料繫結,未新增命令繫結等其它內容。示例介面如圖所示:

1. 拖動滑塊時,最上面TextBox中的數值會跟著變化,顯示滑塊條的當前值,同時主介面背景色的透明度也會跟著變化。

2. 左下角的DataGrid和右下角的ListBox以及右邊標籤為下拉選單的ComboBox中的資料都是一樣的。選中這三個控制元件中的某行時,其它兩個控制元件也會選中該項。右上方紫色方框內會顯示選中行的資訊。

3. 在左下角DataGrid控制元件、右下角ListBox控制元件、右上角紫色區域的某一處修改資料時,其它兩處的資料也會隨著修改。

下面說明如何通過資料繫結實現上面的功能。

先說一下主介面的ViewModel,其中有double型屬性DoubleValue,TestData型屬性SelectedData,List<TestData>型屬性TestDataList。這裡沒有使用ObservableCollection集合因為不涉及到集合內資料項的動態新增和刪除。如果要使用ObservableCollection也非常簡單,只需要把List<TestData>改為ObservableCollection<TestData>即可。

功能1:Slider的Value、TextBox的Text和DockPanel的Background的Opacity都繫結了ViewModel中的DoubleValue。當拖動滑塊時,Slider的Value發生了變化,會更新ViewModel中的DoubleValue,DoubleValue更新後會呼叫RaisePropertyChanged方法觸發INotifyPropertyChanged中的PropertyChanged事件,並在事件引數中儲存了發生改變的屬性名稱“DoubleValue”,這樣View中繫結了DoubleValue的屬性就要更新資料,TextBox的Text屬性繫結了DoubleValue,所以TextBox更新了Text以顯示最新的Slider的Value。同樣主介面最頂層的佈局容器DockPanel的Background中的Opacity也繫結了DoubleValue,更改後的表現就是背景色透明度發生了變化。整個過程如圖中藍色箭頭所示。

功能2:DataGrid、ComboBox、ListBox的ItemsSource都繫結了ViewModel中的TestDataList,所以他們的列表項都是一樣的。在DataGrid中我們顯示了TestData的所有屬性,ComboBox中我們只顯示了StringValue屬性,在ListBox中則重寫了資料項模板,按照我們想要的方式顯示TestData中的BoolValue、AddDateTime、和IntValue。同時這三個控制元件的SelectedItem也都繫結了ViewModel中的SelectedData,因此當其中一個的選中項發生改變時,其它兩個控制元件的選中項也會相應發生改變,原理參照功能1。此外右上角紫色區域的幾個TextBox和一個CheckBox分別繫結了SelectedData的不同屬性,因此當SelectedData發生改變時,紫色區域內各個控制元件的顯示內容也會發生改變。

功能3:因為TestData類也繼承自NotifyObject,而且幾個屬性在發生改變時也會呼叫RaisePropertyChanged方法發出通知,因此所有繫結了這些屬性的控制元件也會隨著更新資料。表現出來就是在任何地方修改了TestData中的屬性,所有繫結了這些屬性的控制元件都會更新。

當然了,還需要在主介面的後臺程式碼中把DataContext設定為ViewModel的一個例項。

DataContext = new MainWindowViewModel();

其實上面很多功能可以不借助ViewModel來實現的。例如功能1中把TextBox的Text直接繫結到Slider的Value上,同樣功能二中ListBox的SelectedItem也可直接繫結到DataGrid的SelectedItem上,之所以使用ViewModel是為了演示MVVM模式下的資料繫結。

0x04 相關下載

示例程式碼:https://github.com/durow/TestArea/tree/master/MVVMTest

 


更多內容歡迎訪問我的部落格:http://www.durow.vip

相關文章