1. 程式人生 > >Mvvm Light Toolkit for wpf/silverlight系列之資料繫結

Mvvm Light Toolkit for wpf/silverlight系列之資料繫結

Mvvm的框架的實現依賴於完善的資料繫結機制,因此熟練使用mvvm就必須熟練掌握WPF/SL的資料繫結機制。下面我們從幾個方面來看看mvvm資料繫結與傳統的.net控制元件使用方式有什麼不一樣;

一、給控制元件屬性賦值

首先我們定義個公有的普通屬性:

傳統的.net控制元件的賦值都是在頁面的後臺程式碼中通過以下方式實現:

資料繫結方式需要在Xaml中的Text屬性中新增繫結語法:

如果TextProperty在後臺更改了,要想在UI也反映更改,傳統的方式仍然是重新給TextBox1.Text賦值,那麼資料繫結方式該怎麼做呢,這時需要在ViewModel中實現INotifyPropertyChanged介面,通過觸發PropertyChanged事件達到通知UI更改的目的;這裡我們定義的ViewModel都繼承自ViewModelBase,ViewModelBase封裝在MvvmLight框架中,它已經實現了INotifyPropertyChanged介面,因此我們在定義ViewModel屬性時,只需要呼叫RaisePropertyChanged(PropertyName)就可以進行屬性更改通知了;具備屬性更改通知的屬性定義如下:

或者使用程式碼段mvvminpc自動生成ViewModel屬性。

ViewModelBase中INotifyPropertyChanged介面實現部分如下:

 

兩種方式實現起來都很簡單,看起來都差不多,那麼使用資料繫結有什麼好處呢:

    1、繫結方式使業務邏輯和UI設計可以完全分離,因此沒必要在後臺程式碼xaml.cs中操作控制元件屬性,而繫結的屬性都定義在ViewModel中,因此我們完全可以將xaml.cs刪除,ViewModel與View(UI)的關聯通過UI控制元件的DataContext屬性實現,通常我們都將ViewModel繫結到UI的頂層佈局控制元件上(如Window,UserControl),從而使得ViewModel對整個UI可見。

    2、簡單的賦值可能看不出繫結的優勢,而且要讓View反映ViewModel的更改還必須在ViewModel中實現INotifyPropertyChanged介面,但是在這種情況,只要屬性更改了,View控制元件就會響應更改,ViewModel是面向屬性進行程式設計,而傳統方式需要面對控制元件程式設計,如果屬性更改,需要查詢對應的UI控制元件,然後重新設定控制元件屬性;資料繫結在對列表資料繫結時,優勢更為突出,比如資料來源的資料更改了,傳統方式必須重新繫結才能反映資料來源的更改,而繫結方式資料來源更改甚至資料來源部分欄位資料的更改,UI都可以自動響應更改

    3、傳統方式必須給控制元件命名才能在後臺程式碼使用,而繫結方式控制元件與ViewModel的屬性繫結,使用ViewModel的屬性跟使用控制元件的屬性一樣,可以不用給控制元件命名

二、讀取控制元件屬性值

通常在UI控制元件屬性更改時,需要在邏輯處理的地方重新獲取控制元件的值,傳統方式必須找到控制元件,然後獲取控制元件屬性的值,而在資料繫結的方式中只需要在繫結語法中新增繫結方式Mode=TwoWay或OneWayToSource,這樣,只要UI中繫結目標的值更改,就會觸發ViewModel中屬性的Set方法對屬性進行賦值,保證任何時候使用屬性都是UI的最新值,xaml中繫結語法如下:

注意:

    SL中不支援OneWayToSource,只能使用TwoWay;

    WPF中對於TextBox、Combobox等控制元件,預設(不寫Mode)的繫結方式是TwoWay,而SL中好像預設都是OneWay,如果你搞不清楚,索性都寫全了就不會混淆了;

    對於更新資料來源的時機,TextBox預設都是LostFocus方式,也就是失去焦點時才會更新源,WPF中可以指定更新源的時機,Wpf示例中為TextBox屬性更改時同時更新資料來源,也就是每輸一個字母,資料來源都會更改一次,而SL中不支援UpdateSourceTrigger,因此只能使用預設的LostFocus方式。

本章示例中我們建立了3個文字框來測試繫結方式,3個文字框以不同繫結方式繫結到相同資料來源,更改其中一個文字框,我們可以觀察其他2個文字框的值變化情況

另一個示例中我演示了省市聯動的下拉框繫結效果,WPF中xaml程式碼如下:

 

後臺程式碼:

可以看出,通過繫結到屬性就可以完成下拉列表的聯動,這裡要注意的是SL中不能通過繫結到CityName來設定城市列表的選中項,如果省份列表更改,CityName就不能關聯到Combobox的選中項,原因可能是SL通過引用查詢選中項,WPF可以通過字串相等來查詢選中項,因此SL是通過SelectedItem來繫結選中項,可以看出來,在WPF中,我們的繫結可以比較隨意,而在SL中要特別小心,一個小小的不同可能讓你花很長時間找不到問題的原因,這時就要仔細看幫助文件了

SL設定下拉列表選中項方法:

 

WPF中除了以上方法,可以直接給_CityName賦值字串來設定列表選中項

三、控制元件事件處理

傳統方式處理控制元件事件都是在xaml中定義事件屬性,然後在後臺xaml.cs中新增事件處理方法:

Xaml: 

xaml.cs:

在mvvm中,可以通過Command繫結進行按鈕點選事件的處理:

xaml:

ViewModel:

本示例中,可以通過點選Button按鈕彈出MessageBox顯示當前時鐘。

當然,我們不光要處理Button的點選事件,還需要處理其他一些事件型別,我們會在下一章節專門介紹mvvm命令和事件

四、 列表型別控制元件的處理

這裡的列表型別指的是包括Combobox、ListBox、Datagrid等所有能繫結到集合的控制元件,到這裡我們更需要了解一下WPF/SL的控制元件的模板和樣式與繫結之間的關係。

比如ListBox控制元件,依然繫結到之前定義的Provinces列表,Xaml中定義如下:

 

xaml定義的顯示DisplayMemberPath就是列表要顯示的欄位名,執行結果顯示:

湖北

廣東

湖南

ListBox其實有一個預設的ItemTemplate,它以一個Content控制元件來顯示DisplayMemberPath定義的欄位的內容,我們可以自定義ItemTemplate讓ListBox顯示更多的內容,注意ItemTemplate與DisplayMemberPath不能同時定義

顯示結果如下:

我們可以將ListBox看成是集合資料的顯示方式,一旦ItemsSource被繫結到集合List<Model.Province>
,那麼ListBox的每個ItemTemplate的DataContext對應集合中的每個Model.Province物件,ItemTemplate的內容是呈現Model.Province物件的方式;這是我的理解方式,每個列表控制元件呈現資料方式不一樣,但是資料來源集合可以是一樣的,UI設計人員則可以根據需要選擇不同的資料顯示方式。同樣是List集合的資料來源,讓我們看看示例中另一種列表顯示方式:

這個是一個完成分組和排序功能的Datagrid,同樣只是簡單的繫結到List集合,後臺不用額外的程式碼,所有功能都在Xaml中完成:

首先在UI中定義CollectionViewSource資源,在這裡定義排序和分組的規則

WPF中定義如下:

  

SL中定義如下:

 

後臺程式碼:

最後還有一種特殊的列表控制元件,那就是TreeView ,TreeView 用一個專門繫結層次關係的ItemTemplate來顯示樹狀結構,它就是HierarchicalDataTemplate,WPF模板定義方式如下:

 

SL中定義方法一樣,不同是TreeView所在的命名控制元件不一樣

資料來源的類定義如下:

Treeview繫結顯示結果如下:

使用ItemsControl控制元件還可以用來處理動態生成控制元件的情況,只需將控制元件的屬性對映到Model屬性,然後在ItemTemplate中將控制元件屬性繫結到Model屬性,就可以動態建立控制元件列表;通過自定義ItemsControl的ItemsPanel模板,就可以控制ItemsControl內的控制元件排列方式,比如示例中使用WrapPanel來顯示一個圖片瀏覽器,當一行顯示的圖片總寬度超過WrapPanel寬度,就會自動換行顯示,WPF中xaml程式碼如下:

 

WrapPanel 的寬度繫結到它的父元素ListBox的寬度,這麼寫的好處是ItemsPanelTemplate可以當作公共資源定義在資原始檔中供多處同時使用,而在SL中,由於不支援FindAncestor繫結語法,需要指定父元素的ElementName來繫結,或者直接使用數字

本章主要介紹MvvmLight中資料繫結的實現,但並不涉及WPF/SL資料繫結的全部內容,下章我們將主要介紹MvvmLight中命令和事件的用法。

本章示例程式碼如下: