天天看點

MVVM 開發的幾種模式讨論(WPF)

   在WPF系(包括SL,WP或者Win8)應用開發中,MVVM是個老生常談的問題。初學者可能不會有感覺,但當你寫一個核心邏輯能在各種平台上無縫移植,而隻需改改UI的時候,那種快感是無法用語言來形容的。

   筆者當初接觸時,對MVVM并不以為然,編了很多代碼以後,反過來看MVVM for WPF的經典文章以後,才若有頓悟。标準的MVVM把程式分成了Model, ViewModel和 View三個部分,但方法是死的,人是活的。我一般的做法是邏輯寫一個,View寫一個,沒有那麼嚴格。為了友善讨論,我們把ViewModel和Model合稱Model, View還是View, 分别代表邏輯和界面。分離是肯定的,可是在程式中終究是要把View和Model在某個地方結合起來。 本文就讨論下幾種結合的方式。

1. 标準MVVM(由View執行個體化Model)

     标準的MVVM,做法當然是先設計Model, 然後再設計View, 在View的代碼裡有且僅有這麼幾句話:

public partial class PluginMangerUI : UserControl         {             public PluginMangerUI()             {                 this.InitializeComponent();                 PluginManager manager = new PluginManager();                 this.DataContext = manager;             }         }      
基本的邏輯結構可以用下圖來表示。不同的庫是由自底向上的方式設計的。      
MVVM 開發的幾種模式讨論(WPF)

    這種由View調用Model, 并具體由View負責Model執行個體化的方式是最為普遍的,非常适合于需要跨平台的應用。當然,Model并不知道View的存在,是以View要承擔所有的界面邏輯,好在WPF已經給出了足夠多的解決方案,觸發器,模闆。基本絕大多數需求都能滿足。

2. 插件結構(Model執行個體化View)

     這種做法,是筆者經常用的。在Model裡,通過以下代碼來實作界面生成

internal class ViewExample : UserControl { }         public class ModelExample : IView         {             private readonly ViewExample view;             public ModelExample()             {                 this.view = new ViewExample();             }             public object UserControl             {                 get                 {                     return this.view;                 }             }      

   肯定會有同學問道,怎麼會有這麼奇怪的寫法?這種做法的最常見場合應該是插件系統。一個個的Model其實是一個個的插件,它們應該具備自治性。是以,應該由自身負責界面的産生。

   它的好處是可以通過Model更加精細的調節View的行為,你可以在任何時候獲得View内部ListBox的SelectIndex, 而不用麻煩的用Binding。 打個比方說,遊戲開發中,你需要随時控制物體的運動速度和方向,這樣Model就必須控制View. 綁定很難解決這類問題。

   我不知道有多少同學在WPF中使用插件的設計思想。若按插件的思路,庫應該按功能劃分。在這種設計思路下,不同的庫便不是自下而上的分層了,而是通過領域和功能分層,如下圖:

MVVM 開發的幾種模式讨論(WPF)

  每一個功能庫都有完整的自治性,當你将該功能庫拷貝到主架構之下時,它就會自動加載,由Model負責View的生成。一切合情合理。

  3. 組裝工廠中的房間(第三方組裝View和Model)

   這種思路來自于工廠方法,類似于裝配工廠中的房間,View和Model都不負責互相的執行個體化。而有一個“管理器”負責組裝它們。這樣的好處在于可配置。你可以通過配置檔案動态的改變View.

    我記得一種比較著名的WPF向導(Wizard)就是這樣的設計思路:

private static List<CompleteStep<DataProcessTask>> CreateSteps(DataProcessTask o)             {                 var welcomeModel = new WelcomeModel(o);                 var step1ViewModel = new UserCoreModel(o);                 var step2ViewModel = new UserDataModel(o);                 var step3ViewModel = new ConnectModel(o);                 var step6ViewModel = new FinishModel(o);                 return new List<CompleteStep<DataProcessTask>>                            {                                /// Each step contains a ViewModel and a View type (the type representing the actual Xaml to be shown).                                new CompleteStep<DataProcessTask>                                    {ViewModel = welcomeModel, ViewType = typeof (Welcome), Visited = true},                                new CompleteStep<DataProcessTask> {ViewModel = step1ViewModel, ViewType = typeof (UserCore)},                                new CompleteStep<DataProcessTask>                                    {ViewModel = step2ViewModel, ViewType = typeof (UserDataView)},                                new CompleteStep<DataProcessTask> {ViewModel = step3ViewModel, ViewType = typeof (Connect)},                                new CompleteStep<DataProcessTask> {ViewModel = step6ViewModel, ViewType = typeof (Finish)}                            };             }      

    如上圖所示,DataProcessTask類是控制整個流程的核心,第一步先執行個體化所需的ViewModel, 第二部,通過建構一個List清單,将View的Type的方法傳到清單中。最終管理器通過反射來執行個體化View,并将DataContext綁定到對應的ViewModel完成整個組裝過程。

    可以看出,這種做法徹底的隔絕了View和Model, 同時通過配置選項,可以随時修改View。可謂是一種不錯的設計。 但是,必需看到,對于View來說,Model沒有任何管理的權限。下圖展示了它的基本邏輯:

MVVM 開發的幾種模式讨論(WPF)

  如果最終你依舊需要兩邊互相控制,可以考慮采用dynamic關鍵字。

4. 總結

    其實沒有哪種方式是最好的,完全是看你對整個系統的設計需求。但不論如何,界面和邏輯的分離,這是毋庸置疑的。下面的表格總結了幾種做法的特點和适用場合:

名稱 組裝邏輯 适用場合 缺點 備注
标準MVVM View執行個體化Model 常用的跨平台場合 Model無法控制任何View 适用于自底向上的分層設計
Model執行個體化View 插件結構或用于遊戲開發 存在一定的耦合 适用于按功能劃分的插件型類庫設計,或要求Model大量控制View的場合
組裝工廠中的房間 第三方管理器執行個體化群組裝Model和View 可動态替換所有View 兩者徹底隔絕,沒有控制靈活性 大型系統的嚴格設計

    當然,如果用MVVMLight等第三方類庫的話,就應該按照它的方案去開發。但我們的原則是,解決問題,但不要引入更複雜的問題。為了解耦,搞了大量的複雜邏輯,反而舍本逐末。

    有任何問題,歡迎随時讨論。

作者:熱情的沙漠

出處:http://www.cnblogs.com/buptzym/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀