標籤:wpf mvvm 執行個體
一、
什麼是MVVM模式
MVVM是Model-View-ViewModel的簡寫。微軟的WPF帶來了新的技術體驗,如Silverlight、音頻、視頻、3D、動畫……。這導致了軟體UI層更加細節化、可定製化。同時,在技術層面,WPF也帶來了諸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)架構的由來便是MVP(Model-View-Presenter)模式與WPF結合的應用方式時發展演變過來的一種新型架構架構。它立足於原有MVP架構並且把WPF的新特性揉合進去,以應對客戶日益複雜的需求變化。
二、 為什麼要有MVVM模式
因為MVVM模式解決了在日常開發中Model與View之間相互連信之間存在的問題,如轉換類型等額外操作。
記得幾年前,本人接觸MVC設計模式的時候,那時候感覺前台後台之間工作與呈現是如此的井然有序。開發擴充的時候需要的只是添加新的View,新的Model和相應的Controller代碼。後期開發維護實在是容易。
時間回溯到幾個月前,本人用WPF開發了一個軟體系統。這個系統算是使用WPF各種技術的總結。但是沒有引入任何模式。在開發完成以後,xaml以及xaml後的cs代碼裡堆積了大量的內容。導致維護的時候需要在設計視窗裡通過點擊控制項地區定位到代碼,再通過代碼找到後台事件,再通過後台事件找到處理方法,這一大長串難以分離的耦合困區。每個頁面的xaml堆積到最多幾千行,背景cs代碼也是幾千行,維護較為困難,同時再開發擴充的時候也有諸多不變性。
後來網上找到了很多執行個體,其中MVVM首當其衝進入我的眼簾。這個模式吸收了MVC模式的精華,同時又針對於WPF有特定的實現方法,可以做到代碼井然有序。
1,展現了MVVM是如何做到井然有序的。
圖1
現在這附圖說明了MVVM模式的實現關係。在開發中View整合了使用者的操作以及資料的展現方式。很大程度上解決了直接編寫代碼存在的兩個問題:
1. View中很多控制項的資料類型和Model中的屬性不相同,例如開發中,性別這種,Model中很可能就放置一個bool類型的變數。但是在前台的展現View中,使用者看到的應該是“男”和“女”。這就需要一種轉化。這種轉化放在View中?不合適,介面上不應該出現邏輯代碼。放在Model中?不合適,這會出現更多的屬性,方法,導致Model臃腫龐大。
2. 在WPF開發中事件和命令同樣都可以讓一個UI正常的工作。我們知道Winform是事件驅動的。所以理所當然使用事件更容易理解和實現。但是帶來的問題是後期的龐大與多種多樣的事件。這些事件真的不能複用?不是。
這兩種問題很大的催生出Model和View中間的一個背景工作角色ViewModel。它需要協助View轉化相應的資料給Model或者從Model處轉化成View可以顯示的內容。同時它也需要將View的多種命令綁定給Model中的處理方法上。這些命令可以複用,當其他View需要的時候,同樣可以調用命令中綁定的方法。ViewModel可以看成一個變種的Controller。
三、 實現原理
解決了上一節提出的兩個問題,實際上就解決了ViewModel的全部工作原理。
首先,從Binding問題入手。在View中的控制項存在一個屬性,叫做“DataContext”。這個是控制項資料使用的源頭。DataContext屬性會給控制項指定一個後台模型,使得該控制項使用的資料都是來自於這個模型類。所以,ViewModel應該充當這個後台模型的作用,給View的控制項提供顯示資料。同時,ViewModel的資料應該是來自於背後的Model所提供的。所以,簡要的說,根據View中顯示的資料是何種Model,來定義ViewModel。舉個例子,如果View構造了一個TextBlock控制項,想要顯示的僅僅是Model中的一個string。那麼在ViewModel中,應該引用這裡的Model,這樣作為View部分,就可以調用這個Model的某個string屬性了。再舉個實際一些的例子,如果View中構建了一個ListView,這裡展現出一系列的商品名稱。對於Model來說,每個執行個體是一個商品。那麼ViewModel中應該是一個Model的集合,是儲存了所有需要ListView顯示出來的Model集合。
其次,解決Command問題。WPF中已經構建了實現了ICommand的類RoutedCommand和RoutedUICommand。針對於不同的View事件,單獨使用哪一種都不是全權之策。因此,需要定義一個實現了ICommand介面的類。目前網上有現成的DelegateCommand和RelayCommand兩種解決方案。他們的共同點都是實現了ICommand介面,同時對於不同的事件,都可以綁定Model不同的處理方法。其區別是:i)DelegateCommand使用了一個RaiseCanExecuteChanged方法,需要開發人員手動來觸發控制項可執行判斷。而RelayCommand中對於此處的觸發判斷是代理給CommandManager自己判斷了。更加方便;ii)DelegateCommand因為是開發人員手動控制的,所以資源佔用低,而RelayCommand在各種命令觸發的時候都需要判斷一下。所以資源佔用也相對較高。這一點尤為能體現在複雜的系統中。所以使用哪一個都是看開發人員自己選擇。當然也可以自己手動寫一些更加適合自己的XXXCommand。其實現原理就是實現了ICommand介面。另外,使用委託的方法,將無返回值的Execute使用Action委託,有返回值的CanExecute使用Func委託。
四、 實現過程
上面說了這麼多,必須得實際操練一下才能深切體會MVVM。
這裡就以之前商品列表作為背景。View介面我打算示範一個ListView用來展現一個列表。然後下方有一個按鈕,單擊列表中的某一項商品,然後點擊下方的按鈕讓商品價格加1元。介面如:
當選中某個項,點擊“Add 1¥”以後,那個商品會隨之加1元。(價格顯示美元符是因為本地地區性設定,可更改,這裡不做說明)。
好,下面來實現這個例子。
首先,先觀察一下我的例子結構:
這裡每個檔案做個說明:
Milk.cs這個是一個Model,裡面含有商品應有的資訊,如ID、類型、價格等。
NotificationObject.cs這其實是一個公用類,其提供的方法就是將需要提醒前台自動更新數值的屬性在必要的時候更新數值。
MilkListViewModel.cs這是一個ViewModel,這裡為前台的列表準備了一個ObservableCollection,並且為前台提供Command綁定支援。簡單的來說,它將自己的某種對商品集合準備方法執行個體化為一個命令類型的屬性,為前台提供綁定。
RelayCommand.cs這是網上廣為流傳的一個MVVM實現所用的ICommand介面實現。原理之前已經說了,網上也有DelegateCommand.cs的源碼。都很好用。
MainWindow.xaml這裡充當View,布置了顯示商品的各種UI。通過Binding來綁定資料來源和命令。不過應該注意,必須是DataContext是編寫的ViewModel時,才能綁定那個ViewModel的資料和方法命令。
這裡就不貼代碼了,原理講了基本大家都能摸索個八九不離十。感興趣研究的朋友可以下載我的資源裡上傳的樣本程式(vs2010用)。歡迎廣大愛好者共同研究,提出寶貴問題。支援原創,轉載請註明出處。
WPF教程:MVVM模式的理解與應用