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;
        }
    }

 

 基本的邏輯結構可以用下圖來表示。不同的庫是由自底向上的方式設計的。

image

    這種由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中使用插件的設計思想。若按插件的思路,庫應該按功能划分。在這種設計思路下,不同的庫便不是自下而上的分層了,而是通過領域和功能分層,如下圖:

image

  每一個功能庫都有完整的自治性,當你將該功能庫拷貝到主框架之下時,它就會自動加載,由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沒有任何管理的權限。下圖展示了它的基本邏輯:

image

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

4. 總結

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

名稱 組裝邏輯 適用場合 缺點 備注
標准MVVM View實例化Model 常用的跨平台場合 Model無法控制任何View 適用於自底向上的分層設計
Model實例化View Model實例化View 插件結構或用於游戲開發 存在一定的耦合 適用於按功能划分的插件型類庫設計,或要求Model大量控制View的場合
組裝車間 第三方管理器實例化和組裝Model和View 可動態替換所有View 兩者徹底隔絕,沒有控制靈活性 大型系統的嚴格設計

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

    有任何問題,歡迎隨時討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM