WPF MVVM應(yīng)用方式解析
WPF開(kāi)發(fā)工具中有各種各樣的模式,運(yùn)用這些不同的模式可以為我們創(chuàng)造各種不同的基于圖像處理的功能需求。其中,WPF MVVM是專為WPF打造的模式,也可以說(shuō)MVVM僅僅是MVC的一個(gè)變種,但無(wú)論如何,就實(shí)踐而言,如果你或你的團(tuán)隊(duì)沒(méi)有使用"Binding"的習(xí)慣,那么研究MVVM就沒(méi)有多大意義。#t#
另外,個(gè)人覺(jué)得,使用Command以及打造一種合理的簡(jiǎn)化的方式去使用Command也與使用Binding一樣重要。
為了解決現(xiàn)實(shí)世界中的問(wèn)題,我們需要將現(xiàn)實(shí)世界中的事物加以抽象,然后得到了Domain Object,無(wú)論貧血的還是富血的,我們都可以簡(jiǎn)單地把他們歸結(jié)為"由現(xiàn)實(shí)世界抽象出來(lái)的模型",也就是我們的model,也就M-V-VM中的"M"。
但其無(wú)法與我們的用戶進(jìn)行交互,所以,我們需要為其創(chuàng)建一個(gè)界面(視圖,View),該視圖可以與用戶輸入設(shè)備進(jìn)行交互,這很棒,但問(wèn)題是如何將View與我們的model關(guān)聯(lián)起來(lái)? Binding便可以發(fā)揮作用了,比如視圖上的某一個(gè)文本框中的文本和Model中的"用戶名"關(guān)聯(lián)起來(lái),用戶便可以通過(guò)操作該文本框來(lái)訪問(wèn)和修改Model的"用戶名"了。
這是極其簡(jiǎn)單的情況,但實(shí)際編程時(shí)我們發(fā)現(xiàn),Model中的屬性(與方法)往往不那么容易與View中的界面控件關(guān)聯(lián)起來(lái),比如,"類(lèi)型不匹配": 界面控件所需要的類(lèi)型與模型中屬性提高的類(lèi)型不匹配。 "需要額外操作": 模型中的數(shù)據(jù)需要經(jīng)過(guò)一些額外的處理才能傳給視圖,反之亦然。 此時(shí),我們意識(shí)到View似乎需要一個(gè)"Helper"類(lèi)來(lái)處理一些額外工作。
這個(gè)helper所包含的代碼可以放在除了Model外的很多地方(我們現(xiàn)在不考慮貧血富血之類(lèi)的爭(zhēng)論),比如View中,記得自己剛學(xué)習(xí)窗體程序開(kāi)發(fā)時(shí)就是這么干的,將絕大多數(shù)處理邏輯放在那個(gè)所謂的CodeBehind中。
后來(lái),正如大家在各種設(shè)計(jì)模式書(shū)籍中所看到的一樣,為了將View和Model剝離開(kāi)來(lái),實(shí)現(xiàn)view可替換(比如你可以講自己精心設(shè)計(jì)的軟件同時(shí)運(yùn)行于窗體程序,Web甚至Mobile上),便有了MVC。 有了MVC以后似乎就開(kāi)始滋生M-V-XXX之類(lèi)的爭(zhēng)論與變種模型,比如MVP以及這里的WPF MVVM,甚至MVP也有著Supervising Controller與Presentation Model兩種方式。
但主要圍繞兩個(gè)問(wèn)題,一是model與view之間的關(guān)系,完全隔離的?單向的還是雙向的? 二是這個(gè)"XXX"需要完成哪些功能,簡(jiǎn)單流程調(diào)度?復(fù)雜規(guī)則處理? OK,這些爭(zhēng)論都沒(méi)有關(guān)系,是否采用某種模式取決于你的開(kāi)發(fā)所處的環(huán)境(比如語(yǔ)言特性,框架特性)以及你的業(yè)務(wù)特性以及所面臨的主要變化點(diǎn)等等。
但與MVC,MVP所不同的是,WPF MVVM的引入不僅僅是技術(shù)上的原因(解除耦合應(yīng)對(duì)變化等老生常談),另外一個(gè)很大原因是:軟件團(tuán)隊(duì)開(kāi)發(fā)方式的改變。如果你做過(guò)一段時(shí)間的WPF項(xiàng)目開(kāi)發(fā)的話,你可能會(huì)有比較明顯的感覺(jué):在View層打造上,如何分配程序員和美工的工作。
以前我們團(tuán)隊(duì)采用的便是"集成模式",我便兼職了其中的"Integrator"角色。這還不錯(cuò)。但說(shuō)實(shí)在的,這僅僅是一個(gè)在特殊情況下不得已而為之的暫時(shí)方案,所以我們付出了很大的努力開(kāi)始轉(zhuǎn)向"收割模式"了,要轉(zhuǎn)向這個(gè)模式,至少需要兩個(gè)基本條件:
(1)你擁有能夠熟練運(yùn)用Blend等工具能為程序員輸出XAML的美工,他專注于純粹的UI/UE,另外他還必須具有一定的"程序員"思維。以便輸出的東西能很好地作為程序的一部分而運(yùn)轉(zhuǎn)起來(lái),而不是僅僅"看上去"是那樣的。
(2)你需要能夠脫離View層但仍能編寫(xiě)出高質(zhì)量代碼的程序員。
幸運(yùn)的是,我們?cè)谂?chuàng)造條件1,并取得了很好的效果。(你可以招一個(gè)具有Flash腳本編寫(xiě)經(jīng)驗(yàn)的并且有極大的學(xué)習(xí)熱情的美工人員,并對(duì)他進(jìn)行Blend的相關(guān)培訓(xùn))。 而WPF MVVM模式為我們實(shí)現(xiàn)第二個(gè)條件提供了極大的便利。 為什么MVC/MVP模式不行而MVVM可以呢? 很簡(jiǎn)單,在MVC和MVP模式中,View層都具有很多代碼邏輯,開(kāi)發(fā)View層的是程序員,雖然UI/UE團(tuán)隊(duì)會(huì)做很多工作,但這個(gè)層的"實(shí)現(xiàn)者"仍然是程序員。
在以前的開(kāi)發(fā)中,其工作得很好,而在WPF開(kāi)發(fā)中程序員對(duì)View層的展現(xiàn)顯得力不從心了,美工(指符合上面條件1的美工)雖然很擅長(zhǎng),但他會(huì)說(shuō)"可惜我不會(huì)程序"。于是,我們需要一種方式將View層的代碼邏輯抽取出來(lái),并View層很純粹以便完全讓美工去打造它。相應(yīng)地,需要將View層的相應(yīng)邏輯抽取到一個(gè)代碼層上,以便讓程序員專注在這里。
回想一下,我們只所以要在View(Xaml)背后寫(xiě)一些代碼(C#),無(wú)非是想傳遞一些數(shù)據(jù)以及傳遞數(shù)據(jù)時(shí)的數(shù)據(jù)的處理或在用戶與界面控件進(jìn)行交互時(shí)執(zhí)行一些操作,最簡(jiǎn)單的例子是在MVC中當(dāng)界面發(fā)生交互時(shí)View去調(diào)用Controler中的某個(gè)方法,以便將該操作的相應(yīng)"指示"傳遞到"后臺(tái)"去。 在以前的技術(shù)中,這樣的"銜接性"的代碼是必須的。
而在WPF中,則可以通過(guò)另外的技術(shù)來(lái)進(jìn)行層與層之間的"銜接",這就是"Binding" 和"Command",以及稍后我們會(huì)提到的"AttachBehavior"。 通過(guò)Binding,我們可以實(shí)現(xiàn)數(shù)據(jù)的傳遞; 通過(guò)Command,我們可以實(shí)現(xiàn)操作的調(diào)用。(AttachBehavior的作用稍后再談)。 Binding和Command是可以寫(xiě)在XAML中的,這樣看來(lái)XAML后面對(duì)于的CS文件可以被完全拋棄或不予理會(huì)了。 這樣的XAML文件正是美工所需要的。
而這些對(duì)于Binding以及Command的定義描述以及其他相關(guān)信息的代碼應(yīng)該放在那里呢,當(dāng)然不是View,更不是Model,是"ViewModel"。 ViewModel是為這個(gè)View所量身定制的,它包含了Binding是所需的相關(guān)信息,比如Converter以及為View的Binding提供DataContext,它包含了Command的定義以便View層可以直接使用,另外,它還是一個(gè)變種的Controler,它得負(fù)責(zé)業(yè)務(wù)流程的調(diào)度。
于是,正如"時(shí)勢(shì)造英雄"所言,WPF MVVM就誕生了。