MVVM設計模式
一、背景:與用戶界面 (UI) 相關的最大的問題就是大量的凌亂的代碼,原因兩個:
(1) 用戶界面包含負責的邏輯用於維護界面相關對象;
(2) 其次也包含了應用程序狀態的維護。
用戶界面的3大問題:狀態 (State) , 邏輯 (Logic) ,同步 (Synchronization),其中狀態是用戶界面最關心的問題之一。
二、簡述MVC、MVP、MVVM
(1) MVC:模型-視圖-控制器(Model View Controller),它強制性的使應用程序的輸入、處理和輸出分開。
(2) MVP:模型-視圖-表現類(Model-View-Presenter)
(3) MVVM:模型-視圖-視圖模型(Model-View-ViewModel)
三、比較
(1) 發展過程:MVC->MVP->MVVM
(2) MVC->MVP
MVC中Model不是純Model,因爲它要有View的一些數據結構。
(3) MVC、MVP->MVVM
View沒有大量代碼邏輯。結合WPF、Silverlight綁定機制,MVP演變出了MVVM,充分利用了WPF、Silverlight的優勢,將大量代碼邏輯、狀態轉到ViewModel,可以說MVVM是專門爲WPF、Silverlight打造的。
(4)用戶界面問題比較
|
MVC |
MVP |
MVVM |
|||
|
V |
C |
V |
P |
V |
VM |
狀態 |
√ |
|
√ |
|
|
√ |
邏輯 |
|
√ |
|
√ |
|
√ |
同步 |
|
√ |
|
√ |
√ |
|
四、MVVM
(1) 組成部分Model、View、ViewModel
(a) View:UI界面
(b) ViewModel:它是View的抽象,負責View與Model之間信息轉換,將View的Command傳送到Model;
(c) Model:數據訪問層
(2) View與ViewModule連接:
(a) Binding Data:實現數據的傳遞
(b) Command:實現操作的調用
(c) AttachBehavior:實現控件加載過程中的操作
Binding和Command可以寫在XAML中。
(3) 優勢
(a) ViewModule易於單元測試;
(b) View沒有MVC、MVP複雜的代碼邏輯,讓整個開發過程中的UI設計和後臺的代碼編寫完全分開,設計者可以專注於使用Express Blend等去設計頁面也就是View,而開發的可以完全通過Model來定義要操作的object,通過ViewModel來定義出來需要對這些model做哪些操作,最後使用Command來把Model和View完全聯繫到一起。
(4) 不足
(a) 編寫Command的任務重;
(b) 由於Silverlight不能引用非Silverlight項目,許多界面層的邏輯也得放到後臺(非ViewModel部分),如Command實現,必須通過WCF通信調用服務。
(5) ICommand
(a) 編寫每一個需要綁定的Command
(b) 網上有寫好的基於WPF的Command模板,利用Prism特性重用這部分。
(c) 直接利用Prism的DelegateCommand和CompositeCommand類
DelegateCommand接受Delegate參數
CompositeCommand可以將多個Command組合在一起。
注:Prism 是微軟最佳實踐,可以簡化建設WPF和Silverlight應用。
(6) Command原理
(7)AttachBehavior
(a) 背景
假設我們有一個Button, 當該Button被點擊的時候我們要完成一些操作, 很簡單, 將該操作封裝成一個Command並綁定到該Button上就可以了, 但如果我們要在Button被Load的時候執行另外一些操作呢? 由於Button沒有直接被Load事件所觸發的Command, 所以不能使用Command了. 不能直接將Load事件處理器寫在Button所在的xaml所對應的CS文件裏, 這和我們剛纔對MVVM的設計是相矛盾的. 一個不太好的方案是繼承一下Button, 並撰寫一個由Load所觸發的Command, 這可行, 但明顯不好. 正如一個控件沒有某個屬性並且在不繼承的情況下而採用AttachProperty一樣, 我們可以採用AttachBehavior.
五、MVVM具體應用
(1) Command工作流程:
主要使用Prism的DelegeCommand、CompositeCommand類與訂閱、發佈原理。
(a) ViewModel初始化命令(ICommand)
public DelegateCommand<object> AddAdministorCommand { get; private set; }
this.AddAdministorCommand = new DelegateCommand<object>(this.AddAdministor);
(b) View UI元素綁定ViewModel Command屬性
Command="{Binding Path=AddAdministorCommand}"
(c) 在ViewModel中定義一個事件(event),實現CompositePresentationEvent<object>接口(object-傳遞參數)。
public class AdministorRequestEvent: CompositePresentationEvent<AdministorViewModel>
{ }
(d) 使用TheAggregator訂閱一個事件的執行者(Action)
this.TheAggregator.GetEvent<AdministorRequestEvent>().Subscribe(ShowAdministorVie w);
public void ShowAdministorView(AdministorViewModel administorVM)
{
AdministorView administorV = new AdministorView();
administorV.ViewDataContext = administorVM;
administorV.Show();
}
(e) 使用發佈一個事件
private void AddAdministor(object obj)
{
this.TheAggregator.GetEvent<AdministorRequestEvent>().Publish( new AdministorViewModel(Administor.CreateNewAdministor(),_administorRepository));
}