《Windows fun 7》一:MVVM for Windows Phone 7

每個Silverlight程序員都有一段痛苦的MVVM經歷,MVVM是Silverlight開發永恆的話題。

完美的概念後面是沒有標準框架,網絡上所能找到的例子也並沒有深入介紹,使用過程中一定會接觸深層次的問題,並且各個開源框架也是實現方式各異,叫我們Silverlight程序員情何以堪?

網絡上一搜MVVM關鍵字,最常出現的就是MSDN上的一篇:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

但實際這篇只講述一般應用場景,實際在使用中還有很多問題需要自己解決。

MVVM的概念最早由Josh Smith於2005年提出:http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx

網絡上比較完善比較經典的可能是唯一比較全面的教程也是他那本只有54頁的《Advanced MVVM》,這本Google上能找到PDF鏈接。

不過,經仔細閱讀和分析之後發現,這個例子仍是講述一般場景,真正開發仍然還有很多問題留給開發者自己解決。

我的安排是首先我們來總結一下到底哪些問題令我們痛苦,然後分析MVVM的起因,最後我們談談怎樣消除這些痛苦。

 

1.MVVM到底有哪些令人痛苦的問題?

我這裏總結一下,你也可以一起思考,自己有沒有遇到過這些問題,或者正在思考這些問題。

  1. 設計時和運行時支持?XAML需要設計時支持,很多MVVM框架直接將ViewModel定義在Xaml資源中,這不是View引用了ViewModel嗎?
  2. 如果View不能引用ViewModel,那麼View怎樣取得ViewModel作爲上下文?有很多框架都是直接在CodeBehind中加載ViewModel對象?
  3. ViewModel的定義好像很亂,標準的ViewModel應該怎樣定義?
  4. ViewModel應該怎樣加載數據?
  5. 在導航的時候,怎樣賦予另一個頁面一個ViewModel上下文?有View來指定ViewModel實例還是另外的方式
  6. ViewModel的構造函數應不應該有參數?
  7. 一個方法有多個參數,View應該怎麼樣傳遞這些參數?
  8. View的CodeBehind中應不應該有代碼?
  9. 很多時候,ViewModel中的一個方法都要對應View中的一個UI操作,或則切換狀態,或者修改屬性,ViewModel怎樣觸發UI變化?
  10. ViewModel怎樣調用需要在CodeBehind中執行的方法,比如有些控件的操作不能通過屬性修改,而只能在CodeBehind中調用這個控件的某個方法,比如彈出一個ChildWindows,或者MessageBox?比如VisualStateManager.GotoState就只能在CodeBehind中執行?
  11. 上述問題:ViewModel怎樣切換狀態?
  12. 多線程?
  13. Windows Phone 7上的ViewModel怎樣切換多個Application bar?
  14. Windows Phone 7中ViewModel如何處理回退和導航?

看看吧,你有沒有遇到活思考過這些問題?

 

2.MVVM的由來

其實,這些大多數問題都是有由來的,我們來分析一下。

大多數知道MVVM的人,都可能知道它和MVC有些聯繫?然而是什麼聯繫和區別?爲什麼一般人很容易理解和使用ASP.NET MVC,卻始終不會應用MVVM?可能都說不清楚。

事實上,拿MVC和MVVM比較並沒有多大意義,反而會使人糊塗。在ASP.NET MVC中的View只承擔顯示數據的作用,它和Controller的交互是通過用戶重新發起一起請求,這個請求和View幾乎沒有關係。並且MVC中得HTML是解釋性的,服務器端按View的定義,順序解釋,替換掉相應的數據即可。

然而,Xaml卻不是那麼簡單的,Xaml不僅定義了UI(渲染引擎按照Xaml語言的定義計算和排列元素),Xaml更是一棵對象樹,Xaml對象樹的概念十分重要,以至於我們應該時時記住Xaml中的每個節點都是一個對象,這些對象由Xaml解析器實例化,並由Silverlight的UI渲染引擎根據佈局原理將UI元素繪製到屏幕的相應位置。

可以看到,這和ASP.NET MVC的機制是完全不一樣的,雖然都是基於控件事件模型,但是ASP.NET中對象數實例化和顯示是分開的,而Silverlight的UI元素對象樹就是實際顯示在屏幕的UI元素,它們是一個東西,因爲Silverlight中這些UI元素和程序的交互和聯繫就十分重要和強大。

在Silverlight中,可以說最重要的是引入Xaml編程模型,使我們很好和很方便的定義UI元素和對象樹,加上它的Xaml解析器,順序的解析Xaml中的一個一個對象,Xaml解析器通過調用每個節點對應的類型的夠構造函數來構造對象樹,而Xaml能夠識別每個節點的類型,能夠識別像INotifyChanged這樣的類型,所以Silverlight加入依賴屬性,綁定這樣一些概念。這都跟Xaml特點是有關的。Xaml是一種強大的編程模型,據悉Xaml團隊已經加入Windows 8 Team,所以可以預見Windows 8的應用程序跟Xaml有很大的關係。

因此,MVVM基本上是Xaml這種編程模型催生出來的。它和MVC除了分離的思想外沒有什麼可比的意義。

 

3.MVVM痛苦的根節點

然而,不管怎麼樣,Silverlight終究還是基於事件模型,事件的編程模型是一種極簡的編程模式,它讓開發者很容易的處理用戶的輸入(文本或者事件)。不管是Windows Form,Web Form還是Silverlight,都是基於這種編程模型。

可是事件編程模型有一個很大的缺點,開發者很容易將業務邏輯混在UI操作中,這經常被稱爲Codebehind的文件。這樣的後果是容易使開發者將注意力放到一些表現層的細節上,而且,很容易將業務邏輯與一些表現的細節耦合在一起,更嚴重的是,由於大多數控件對象的不可構造,對程序的可測試性帶來很大挑戰。它也使美工和開發的分工很不明確。

 

因此,早期有分層的思想,分層實際上就是將某一抽象級別的業務封裝。

而在分層的接近UI層面,也出現分離的思想,它企圖將UI徹底分離出來。MVC即是一種方案,可惜的是VS對ASP.BET MVC的設計時支持不夠好。

而在Silverlight開發中,Blend能支持很好的設計時。

 

可是問題是,Xaml並不是爲MVVM設計的,它的核心思想任然是控件和事件。所以MVVM試圖將View和ViewModel分開,它需要將用戶事件轉化爲ViewModel的方法,它需要在ViewModel中驅動一個UI變換,而View有各種各樣的事件和控件,View也有各種各樣的變換。因此原本在Codebehind中輕鬆搞定的事情,在MVVM模式下需要做大量的事情。我承認Codebehind的方式讓業務有點混亂,但是MVVM實在是很麻煩,有時候你會覺得MVVM實際上把一件事情搞複雜了。

 

5.MVVM痛苦級別

MVVM的痛苦級別,取決於你想讓Codebehind有多幹淨,這也有需求方面的考量。

1. 如果你僅僅是爲了易於測試,業務清晰,這很簡單,你只要注意把業務儘量移到ViewModel即可,View引用ViewModel都無所謂,在Codebehind中調用ViewModel中方法也無所謂,這實際上跟一般的分層沒多大區別。

2. 如果View和ViewModel都是你在設計,不需要View和ViewModel嚴格分離,你可以大部分使用各個MVVM框架的標準做法,但是,你知道的,每個MVVM框架都有很難完全分離的方面,這個時候可以在CodeBehind中處理,因爲轉換到ViewModel中需要增加很大的工作量和理解上的複雜度。這也是我推薦的,或者說這是一個折中。

3. 如果你想完全將View交給另一個你甚至不認識的傢伙去設計。你將挑戰最難的MVVM設計--將事件模型完美轉化爲MVVM模型,你將不得不去解決所有問題,包括我上面列舉出的一些問題。這樣應該很少。

 

6.MVVM核心技術和思想

1.附加屬性和Binding

附加屬性是WPF提出的一個全新的屬性系統,它用來支持綁定,變更通知,以及Xaml的樣式Style系統,在MVVM中主要是View和ViewModel之間的數據綁定和傳輸。

2.附加屬性

不可否認,Xaml編程模型,最強大和最重要的概念是附加屬性。

Blend中最強大的Behavior,Action,Trigger,VisualState等等,絕大部分特性都和附加屬性有關。要使View和ViewModel完全分開,必須有一層抽象層作爲兩者之間的橋樑。

Xaml對於附加屬性的支持,使得這個中間層(附加屬性所屬的類)可以得到一個被附加的對象的引用,這個中間層拿到引用之後就可以做一些翻譯,比如一個ButtonCommand就是一箇中間層,它拿到一個Button對象之後,訂閱這個Button的Click事件到一個方法,並讓CanExecute方法執行時,去修改Button的IsEnable屬性。

還有比如你要想在ViewModel控制PhoneApplicationPage的導航和回退,Application Bar的切換等,都可以通過類似的思路來實現。

總之,你記住附加屬性是Silverlight最重要的知識點。

3.上訴情景有時候也可以用擴展方法來代替,這個擴展方法就充當中間層,但是這種情況下要假設這個中間層能夠根據某種規則找到ViewModel對象,並且需要在Codebehind中調用擴展方法。不過這種方式有時候確實有一定的好處。

 

總之,掌握上述兩點你能應付大部分場景,並且在遇到問題之後可以想辦法去解決,核心思想就是一箇中間層,它要麼是利用附加屬性,要麼是利用擴展方法。

 

7.MVVM技術分享

9月17號,我們將在微軟亞太研發集團舉辦Windows Phone 7的技術沙龍,將會詳細分享MVVM的知識和經驗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章