MVVM模式構建WPF_第1頁
MVVM模式構建WPF_第2頁
MVVM模式構建WPF_第3頁
MVVM模式構建WPF_第4頁
MVVM模式構建WPF_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、使用MVVM設計模式構建WPF應用程序本文是翻譯大牛 Josh Smith 的文章,WPF Apps With The Model-View-ViewModel Design Pattern , 譯者水平有限,如有什么問題請看原文,或者與譯者討論(非常樂意與你討論)。本文討論的內容:WPF與設計模式MVP模式對 WPF來說為什么 MVVM是更好的選擇用MVVM 構建 WPF程序本文涉及的技術:WPF、數據綁定內容列表有序與混亂模型-視圖-視圖模型的演變?yōu)槭裁碬PF開發(fā)者喜歡MVVM 演示程序中繼命令邏輯ViewModel類層級結構ViewModelBase 類CommandViewModel

2、類Mai nWin dowViewModel 類View 對應 ViewModel數據模型和Repository新增客戶數據表單所有客戶視圖總結開發(fā)UI,對一個專業(yè)軟件并不容易。 它需要未知數據、 交互式設計,可視化設計、聯通性, 多線程、國際化、驗證、單元測試以及其他的一些東西才能完成??紤]到UI要展示開發(fā)的系統并且必須滿足用戶對系統風格不可預知的變更,因此它是很多應用程序最脆弱的地方。有很多的設計模式可以幫助解決UI不斷變更這頭難纏的野獸,但是恰當的分離和描述多個關注點可能很困難。模式越復雜,之后用到的捷徑越可能破壞之前正確的努力。這并不總是設計模式的錯。有時使用要寫很多的代碼復雜設計模式

3、,這是因為我們使用的UI平臺并不適合簡單是設計模式。UI平臺需要做的是很容易使用簡單的,久經考驗的,開發(fā)者認識的設計模式構建UI。慶幸的是,WPF就是這樣一個平臺。隨著是使用 WPF開發(fā)的比例不斷升高,WPF社區(qū)發(fā)展了自己的模式與實踐生態(tài)圈子。在本文,我將討論一些設計與實現客戶端應用程序的WPF最佳實踐。利用 WPF和MVVM設計模式銜接的一些核心功能,我將通過一個例子介紹,用“正確”的方式構建一個WPF程序是多么的簡單。data templates, comma nds, data binding, the resource system 以及 MVVM 模式怎么揉合至 U 起創(chuàng)建一個簡單的

4、、可測試的、健壯的框架,并且任何WPF程序都能使用,到文章最后,這一切都很清晰明了。文中的例程可以作為現實中一個WPF應用程序的模版,并且使用MVVM設計模式作為其核心架構。例程解決方案中的單元測試部分,展示了測試ViewModel 類的功能是很容易的。在深入本文之前,我們首先看一下我們要使用像 MVVM 這樣的設計 模式。有序與混亂沒有必要在一個” Hello,World! ”的程序中使用設計模式。任何一個合格的開發(fā)者看一眼就 指導那幾行代碼是干什么的。 然而隨著程序功能點的增加, 隨之代碼的數量以及移動部件也 會增多。 最終系統的復雜度以及不斷出現問題, 促使開發(fā)者組織他們的代碼, 以便它

5、們更容 易理解, 討論、 擴展以及維護。 我們通過給代碼中某些實體命以眾所周知的名字, 減少復雜 系統認知誤區(qū)。我們給函數塊命名主要依據系統中的功能角色。開發(fā)者有意識的根據設計模式組織他們的代碼, 而不是根據設計模式自動去組織。 無論哪一 種,都沒有什么問題。但是在本文中,我說明在WPF 程序中明確使用 MVVM 模式的好處。某些類的名稱,包括 MVVM 模式中著名的術語,如果類是 view 的抽象類就以 ViewModel 結束。這種方式有助于避免之前提到的認知誤區(qū)。相反,你也可以讓那種受控的誤區(qū)存在, 這正是大部分軟件開發(fā)項目的自熱狀態(tài)。模型 -視圖-視圖模型的演變 自從人們開始構建 UI

6、 時,就有很多流行的設計模式讓 UI 構建更容易。 比如, MVP 模式在各 種 UI 編程平臺中都非常流行。 MVP 是 MVC 模式的一種變體, MVC 模式已經流行了幾十年 了。以防你之前從沒用過 MVP 模式,這里做一個簡單的解釋。你在屏幕上看到的是 View , 它顯示的數據是 Model ,Presenter 就是把兩者聯系起來。 View 依賴 Presenter 并通過 Presenter 展示 Model 數據,響應用戶輸入,提供數據驗證(或許委托給 Model 去完成)以及其他的 一些任務。如果你想了解更過關于 MVP 模式,我建議你去讀 Jean-Paul Boodhoo

7、 的 August 2006 Design Patterns column 。2004 年晚些時候, Martin Fowler 發(fā)表了一篇叫 Presentation Model ( PM)的模式。PM 模式 和 MVP 類似, MVP 是把一個 View 從行為和狀態(tài)分離出來。 PM 中令人關注的部分是創(chuàng)建 view 的抽象,叫做 Presentation Model 。之后, View 就僅僅是 Presentation Model 的展示了。在 Fowler的論文中,他展示了 Presentation Model經常更新 View,以便兩個彼此同步。同步邏 輯組作為代碼存在于 Pres

8、entation Model 類中。2005年,John Gossman,目前是微軟WPF和 Silverlight架構師,在他的博客上披露了Model-View-ViewModel (MVVM) 模式。 MVVM 和 Fowler 的 Presentation Model 是一致的,兩 個模式的特征都是 View的抽象,都包含了 View的行為和狀態(tài)。Fowler引入Presentation Model 是作為創(chuàng)建獨立平臺的 View 的抽象,而 Gossman 引入 MVVM 是作為標準化的方法,利用 WPF的核心特點去簡化 UI的創(chuàng)建。從這種意義上來講,我把MVVM作為一般PM模式的一個

9、特例。在 Glenn Block 一遍優(yōu)秀的文章"Prism: Patterns for Building Composite Applications with WPF", 于2008年9月微軟大會發(fā)布,他解釋了 WPF微軟組合程序開發(fā)向導。術語ViewModel沒有用到,然而 PM 卻用來描述 View 的抽象。這篇文章自始至終,都沒沒有出現我要將 MVVM 模式,以及View的抽象ViewModel。我發(fā)現這個術語在 WPF和Silverlight社區(qū)中比較流行。 不像 MVP 中的 Presenter, ViewModel 不需要引用 View。 View 綁定 V

10、iewModel 的屬性, ViewMode 向 Viewl 暴露 Model 對象的數據以及其他的狀態(tài)。 View 和 ViewModel 之間的綁定 很容易構造,因為 ViewModel 對象可以設置為 View 的 DataContext 。如果 ViewModel 中的 屬性值發(fā)生改變,新值將通過綁定自動傳送給View。當用戶點擊View中的按鈕時,ViewMode對于的Comma nd將執(zhí)行請求的動作。ViewModel,絕不是 View,去執(zhí)行實體對象的修改。View類并不知道 Model類是否存在,同時 ViewModel和Model也不知道 View。實際上, Model 完全

11、不知道 ViewModel 和 View 存在,這是一個非常松耦合的設計,在很多方面都有 好處,這不就你就會看到。為什么 WPF 開發(fā)者喜歡 MVVM一旦開發(fā)者適應了 WPF和MVVM,就很難區(qū)別兩者。因為MVVM非常適合 WPF平臺,并且WPF被設計使用 MVVM模式更容易構建應用程序, MVVM就成了 WPF開發(fā)者的通用語。事實上,微軟內部正在用 MVVM開發(fā) WPF應用程序,像Microsoft Expression Blend,然而當 時WPF平臺的核心功能依然在開發(fā)之中。WPF的很多方面,像控制模型以及數據模版,都利用了 MVVM 推薦的顯示狀態(tài)和行為分離技術。MVVM之所以成為一個

12、偉大設計模式,是因為WPF的一個最重要的特征數據綁定構造。通過把 Viewde 屬性綁定到 ViewModel ,你就可以得到兩者松耦合的設計,并且完全去除 ViewModel 更新 View 的那部分代碼。數據綁定系統支持輸入驗證,并且輸入驗證提供了傳 遞錯誤給 View 的標準方法。另兩個 WPF的特點,數據模版和資源系統讓MVVM模式更加可用。數據模版把View應用在 ViewModel 對象上,以便其能夠在 UI 上顯示。你可以在 Xaml 中聲明模版,讓資源系統 在系統運行過程中自動定位并應用這些模版。你可以從我 2008 年 7月寫的一篇文章 , "Data and WP

13、F: Customize Data Display with Data Binding and WPF." ,獲取更多關于綁定和數據模版 的信息。要不是 WPF 對 Command 的支持, MVVM 模式就不會那么強大。本文中,我會為你展示ViewModel怎樣把 Comma nds暴露給 View,并且讓 View消費它的功能。如果你對 Comma nd 不是很熟悉,我推薦你讀一下 2008 年 9 月 Brian Noyes 發(fā)布的文章, "Advanced WPF: Understanding Routed Events and Commands in WPF&qu

14、ot; 。除了 WPF( Silverlight2 )本身讓MVVM以一種自然的方式去構建程序之外,造成MVVM模式流行還有一個原因,那就是 ViewModel 類很容易進行單元測試。從某種意義來講, View 和單元測試只是 ViewModel 兩個不同類型的消費者。擁有一套應用程序的單元測試,可以 為提供更自由、快速的回歸測試,而回歸測試有助于降低之后應用的維護成本。 除了促進創(chuàng)建自動化回歸測試外, ViewModel 類的可測試性也有助于設計更容易分離的 UI。 當你設計應用時, 你可以通過想象某些東西是否要創(chuàng)建單元測試消費 ViewModel ,來確定它 們是放到 View 里面還是

15、ViewModel 里面。 如果你可以為 ViewModel 寫單元測試而不用創(chuàng)建 任何 UI 控件,你也可以把 ViewModel 剝離出來,因為它不依賴任何具體可視化的組件。 最后,對于要和設計者合作的開發(fā)者來說,使用 MVVM 模式使得創(chuàng)建平滑的開發(fā) /設計工作 流更加容易。既然 View 可以是 ViewModel 的任意一個消費者,就很容易去掉一個 View 通 過新增一個 View 去渲染 ViewModel 。這個簡單的步驟允許設計師構建快速原型以及評估 UI 設計。這樣開發(fā)團隊可以關注創(chuàng)建健壯的 ViewModel 類,而設計團隊可以關注設計界面友好的 View。 要融合兩個團

16、隊輸出只需要在 View 的 xaml 上進行正確的綁定即可。演示程序到此為止,我們回顧了MVVM的歷史以及具體操作理論。我也說明了它在WPF開發(fā)者中間如此流行的原因。現在是時候繼續(xù)我們的步伐,看一下MVVM 模式在實際中的應用。這篇文章中的演示程序以各種方式使用 MVVM 設計模式,它提供了豐富的例子,幫助在上下文 中理解 MVVM 的概念。我用 VS2008 SP1 創(chuàng)建的這個演示程序,框架是 Microsoft .NET Framework 3.5 SP1。單元測試是用的 Visual Studio unit testing。應用可以包含任意數量的“Workspace”,每一個都可以由用

17、戶點擊左側導航區(qū)的命令鏈接打開。所有的 Workspace 寄宿在主區(qū)域 TabControl 中,用戶可以通過點擊 workspace 的 tab item 上關閉按鈕關閉 workspace。應用程序有兩個可用的workspace : "All Customers"和"NewCustomer"。運行程序,打開一些workspace, UI看起來如圖1所示。勺 MWMSfflwApU ® 口jMl lustrumAll Cwstwnrri 遇J£us(nr«rNjnn*T嵌訓* !rrunj 敏 on)o網一 Qi(8.8

18、33.163An©canTx>so .ram(12.81,73Hdrun CornetalexQ) contaw. com3*7 3,7M.02Knlich E»s«nlllipQCOntMOUQWII1 PeopleGreggregi 低 ontwsommIrmCTaFtQf>9CvntQ>SQbCOrTiHinkson. GrJinthmkson cxxntow 工 omMcCorta txnuedem seco<it »o .com拍.址氧ASjonlji "0 亡 Xi c4o .c omTotdt 昶leciM

19、 tares; i2.e?X2&圖 1 Workspaces一次只有一個“ All Customers" Workspace的實例可以打開,但是可以打開多個New CustomerWorkspace。當用戶決定創(chuàng)建一個新的客戶時,她必須填完圖2所示的數據輸入表單。All 亡ustornerE | New Custon>er ,x | Kew Custcm-er J£j|Customer type: (Not Spetfi&d)CustOfD&r fype must set&ctMIFirst rarne;F*ret name is mi

20、ssingILast narprLast name rs m<55?ryIE-rnan:E rnffFi missingI圖2新客戶數據輸入表單填完數據輸入表單的所有有效值點擊“ Save”按鈕,新客戶的名稱將會出現在tab item上面,同時新客戶也會增加到客戶列表中。應用程序不支持刪除或者編輯客戶,但是這和其它功能類似,很容易在已有的程序架構上去實現?,F在你已經對演示程序有了更深層次的理解了, 接下來我們研究它是如何設計以及實現的。中繼命令邏輯(Relayi ng Comma nd Logic)除了類構造器里調用初始化組件標準的樣板代碼,應用中的每一 View的codebehind文

21、件都是空的。實際上你可以移除View的codebehind文件,程序讓人能夠爭正確的編譯和運行。盡管View中沒有事件處理方法,但是當用戶點擊按鈕時,程序依然能夠響應并滿足用戶的請求。之所以這樣,是因為UI上Hyperli nk、 Button以及Me nultem 控件的Comma nd屬性被綁定了。綁定機制確保當用戶在控件上點擊時,由ViewModel暴露的ICommand對象能夠執(zhí)行。你可以把 comma nd對象看作一個適配器,這個適配器讓comma nd對象很容易消費在View中聲明的 ViewModel功能。當 ViewModel 暴露 ICommad 類型的實例屬性,被暴露的 C

22、ommand 對象使用 ViewModel 中 的對象去完成它的工作。 其中一個可能的實現模式是在 ViewModel 內創(chuàng)建一個私有嵌套類, 以便 command 能夠訪問包含在 ViewModel 中的私有成員,而不至于污染命名空間。嵌套類 實現了 ICommand 接口,包含在 ViewModel 中對象的引用注入到其構造器中。但是為 ViewModel 暴露的每個 Command 創(chuàng)建實現 ICommad 的嵌套類,會增加 ViewModel 類的大 小。更多的代碼意味著存在 BUGS 潛力更大。在演示程序中, RelayCommand 類解決了這個問題。 RelayCommand 允

23、許通過把委托傳給其 構造器,以實現對命令邏輯的注入。這種方式允許在 ViewMode 類中可以簡單明了的實現 Command。RelayCommand 是 DelegateCommand 的一個簡單的變體, DelegateCommand 可以在 Microsoft Composite Application Library 找到。 RelayCommand 類代碼如圖 3 所示。圖 3 RelayCommand 類public class RelayCommand : ICommand#region Fieldsreadonly Action<object> _execute;r

24、eadonly Predicate<object> _canExecute;#endregion / Fields#region Constructorspublic RelayCommand(Action<object> execute): this(execute, null)public RelayCommand(Action<object> execute, Predicate<object> canExecute)if (execute = null)throw new ArgumentNullException("execu

25、te");_execute = execute;_canExecute = canExecute;#endregion / Constructors#region ICommand MembersDebuggerStepThroughpublic bool CanExecute(object parameter)return _canExecute = null ? true : _canExecute(parameter);public event EventHandler CanExecuteChangedadd CommandManager.RequerySuggested +

26、= value; remove CommandManager.RequerySuggested -= value; public void Execute(object parameter)_execute(parameter);#endregion / ICommand Members作為接口 ICommad 實現一部分,事件 CanExecuteChanged 有一些值得關注的特征。它委托 訂閱 CommandManager. RequerySuggested 事件。這樣以確保無論何時調用內置命令時, WPF 命令架構都能調用所有能夠執(zhí)行的 RelayCommand 對象。RelayCom

27、mand _saveCommand;public ICommand SaveCommandgetif (_saveCommand = null)_saveCommand = new RelayCommand(param => this.Save(),param => this.CanSave );return _saveCommand;ViewModel 類層級圖大部分 ViewModel 類有共同的特征,他們要實現 INotifyPropertyChanged 接口,需要顯示一 個友好的名字,以之前說道 Workspace 為例,它需要能夠關閉(即從 UI 上移除)。要解決這 個

28、問題,自然就需要創(chuàng)建一個或二個 ViewModel 基類,以便新的 ViewModel 類能夠從基類 集成通用的功能。所有的 ViewModel 類形成如圖 4 的層級圖。CwrtiimVirModcl.3:<尿口ft9 i »沱入AUC ut>o«n> uV Jim M odf IDass< WorkipsL-e- / 甘呂 hBeflMaanW indiowV Bew M ocfe-lClass專 Wo rhjspac # i ebd#IOi>pGMb4er i PraptfrtittJi幽r 軍lidPrpcr聊* Method!.:*

29、OnDrapcSeQnP斗 Vsr時PrpfKrfyNHRtk Eveb/ PrcpenyClhtng«lLCDmmdrbdTVirw Model Hittiftractcms * YwWtoiJrBflK-Picpeues* F紹商亍 Conrrind M«h仙Comrrk&ndView McsdelL_J"Properties.呵少 OnReQu«tOciie 丹0>rkip尿凹Events華 RetpuntOose圖4繼承層級圖 為你的ViewModel創(chuàng)建一個基類并不是必須。如果你喜歡在類中通過組合幾個小一點的類 以獲得那些功能,而

30、不是用繼承的方式,這并沒有什么問題。就像任何其他的設計模式一樣, MVVM是一套指導方針,而不是規(guī)則。ViewModelBase 類ViewModelBase是層級中的根類,這就是它要實現通用INotifyPropertyChanged接口以及有一個 DisplayName 屬性的原因。INotifyPropertyChanged 接口包含一個叫 PropertyChanged 的 事件。無論何時 ViewModel對象的屬性的發(fā)生改變時,它都會觸發(fā)PropertyChanged事件,把新值通知給 WPF綁定系統。根據通知,綁定系統檢索屬性,UI組件上綁定的屬性將接受新值。為了讓WPF知道是那

31、一個屬性發(fā)生了改變,PropertyChangedEventArgs類暴露了一個 string類型的屬性 PropertyName。你一定要為事件參數傳遞正確的屬性名,否則WPF將會為新值檢索出一個錯誤的屬性。ViewModelBase 一個值得關注的地方就是它為給定的屬性名提供了驗證,驗證屬性是否存在ViewModel對象上。重構時,這非常有用。因為通過VS 2008重構功能去改變屬性名,不會更新源代碼中字符串,而這些字符串正好包含屬性名(其實不應該包含)。在事件參數中傳 很難追蹤, 因此這個細微的特征將會節(jié)省大量的時間。 ViewModelBase 中增加了這個有用的 特征,其代碼如下:圖

32、 5 屬性驗證/ In ViewModelBase.cspublic event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName)this.VerifyPropertyName(propertyName);PropertyChangedEventHandler handler = this.PropertyChanged;if (handler != null)var e = new PropertyChangedEventArgs(

33、propertyName);handler(this, e);Conditional("DEBUG")DebuggerStepThroughpublic void VerifyPropertyName(string propertyName)/ Verify that the property name matches a real,/ public, instance property on this object.if (TypeDescriptor.GetProperties(this)propertyName = null)string msg = "In

34、valid property name: " + propertyName;if (this.ThrowOnInvalidPropertyName)throw new Exception(msg);elseDebug.Fail(msg);CommandViewModel 類CommandViewModel 是最簡單的 ViewModelBase 子類,它暴露了一個類型為 ICommad 的 Command 屬性。 MainWindowViewModel 通過 Commands 屬性暴露了 CommandViewModel 對象的一個集合。主窗口左手側的導航區(qū)域,顯示了MainWind

35、owViewModel 暴露每個CommandViewModel 對象鏈接,像"View all customers ”和"Create new customer ”。當用戶 點擊鏈接,將會執(zhí)行相應的 Command ,在主窗口的 TabControl 中打開一個 workspace 。 CommandViewModel 類的定義如下所示:public class CommandViewModel : ViewModelBasepublic CommandViewModel(string displayName, ICommand command)if (command =

36、 null)throw new ArgumentNullException("command");base.DisplayName = displayName;this.Command = command;public ICommand Command get; private set; 在 MainWindowResources.xaml 文件中存在一個 key 為 CommandsTemplate 的數據模版, 主窗 口( MainWindow )使用這個模版渲染之前提到的 CommandViewModel 對象集合。這個模版 是簡單在 ItemsControl 里把每

37、個 CommandViewModel 對象渲染成一個鏈接,每個鏈接的 Command 屬性綁定到 CommandViewModel 對象的 Command 屬性。數據模版 Xaml 如圖 6 所示:圖6渲染Comma nd列表<!- In MainWindowResources.xaml -><!-This template explains how to render the list of commands onthe left side in the main window (the 'Control Panel' area).-><Data

38、Template x:Key="CommandsTemplate"><ItemsControl ItemsSource="Binding Path=Commands"><ItemsControl.ItemTemplate><DataTemplate><TextBlock Margin="2,6"><Hyperlink Command="Binding Path=Command"><TextBlock Text="Binding Pat

39、h=DisplayName" /></Hyperlink></TextBlock></DataTemplate></ItemsControl.ItemTemplate></ItemsControl></DataTemplate>MainWindowViewModel 類 如前面看到的類圖一樣, WorkspaceViewModel 類繼承于 ViewModelBase 并增加了“關閉” 的能力。這個“關閉” ,我的意思是在運行的時候能把 workspace 從 UI 上移除。有三個類繼 承于 Workspa

40、ceViewModel ,他們分別為 MainWindowViewModel , AllCustomersViewModel 和 CustomerViewModel 。 MainWindowViewModel 的關閉請求是由 App 類處理的,其中 App 類創(chuàng)建了 MainWindow 以及它對應的 ViewModel 對象。創(chuàng)建代碼如圖7 所示 .圖 7 創(chuàng)建 ViewModel/ In App.xaml.csprotected override void OnStartup(StartupEventArgs e)base.OnStartup(e);MainWindow window =

41、new MainWindow();/ Create the ViewModel to which/ the main window binds.string path = "Data/customers.xml"var viewModel = new MainWindowViewModel(path);/ When the ViewModel asks to be closed,/ close the window.viewModel.RequestClose += delegatewindow.Close();/ Allow all controls in the win

42、dow to/ bind to the ViewModel by setting the/ DataContext, which propagates down/ the element tree.window.DataContext = viewModel;window.Show();MainWindow 包含一個菜單項,該菜單項的 Command 屬性綁定到 MainWindowViewModel 上的 CloseCommand 屬性上。當用戶點擊該菜單, App 類響應請求,調用窗體的關閉方法。 菜單 Xaml 如下所示:<!- In MainWindow.xaml ->&l

43、t;Menu><MenuItem Header="_File"><MenuItem Header="_Exit" Command="Binding Path=CloseCommand" /></MenuItem><MenuItem Header="_Edit" /><MenuItem Header="_Options" /><MenuItem Header="_Help" /></Menu&g

44、t;MainWindowViewModel 包含了 WorkspaceViewModel 對象一個 observable 類型的集合, 該集 合的名稱為 Workspaces。主窗體包含了一個 TabControl,其ItemsSource綁定到上述的集合。 每一個 tab item 都有一個關閉按鈕,其 Command 屬性綁定到它對應 WorkspaceViewModel 實例的 CloseCommand 上。模版展示了如何渲染一個帶關閉按鈕的tab item 。配置 tab item模版的簡化版會展示在下面代碼中,這段代碼可以在 MainWindowResources.xaml 文件中找

45、 到。<DataTemplate x:Key="ClosableTabItemTemplate"><DockPanel Width="120"><ButtonCommand="Binding Path=CloseCommand"Content="X"DockPanel.Dock="Right"Width="16" Height="16"/><ContentPresenter Content="Bindin

46、g Path=DisplayName" /></DockPanel></DataTemplate>當用戶點擊tab item上的關閉按鈕時,會執(zhí)行 WorkspaceViewModel的CloseCommand,觸 發(fā)它的 Requestclose事件。MainWindowViewModel 會監(jiān)控 workspace 的 Requestclose事件, 根據請求從 Workspaces 集合中移除相應的workspace 。因為 MainWindow 的 TabControl 的ItemsSource 綁定到 WorkspaceViewModel 的

47、observable 集合, 從集合中移除對象, 會引起從 TabControl 中移除相應的 workspace。 MainWindowViewModel 相應的邏輯如圖 8 所示 圖 8 從 UI 上移除 workspace/ In MainWindowViewModel.csObservableCollection<WorkspaceViewModel> _workspaces;public ObservableCollection<WorkspaceViewModel> Workspacesgetif (_workspaces = null)_workspace

48、s = new ObservableCollection<WorkspaceViewModel>(); _workspaces.CollectionChanged += this.OnWorkspacesChanged;return _workspaces;void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)if (e.NewItems != null && e.NewItems.Count != 0)foreach (WorkspaceViewModel worksp

49、ace in e.NewItems)workspace.RequestClose += this.OnWorkspaceRequestClose;if (e.OldItems != null && e.OldItems.Count != 0)foreach (WorkspaceViewModel workspace in e.OldItems)workspace.RequestClose -= this.OnWorkspaceRequestClose;void OnWorkspaceRequestClose(object sender, EventArgs e)this.Wor

50、kspaces.Remove(sender as WorkspaceViewModel);在 UnitTests 項目中, MainWindowViewModelTests.cs 文件包含了一個測試方法,該方法驗證 上述功能是否正確執(zhí)行。 很容易為 ViewModel 類創(chuàng)建單元測試是 MVVM 模式的一個大賣點, 因為它只需測試應用程序的功能,而不用寫和 UI 交互的代碼。上述測試方法圖 9 所示 圖 9 測試方法/ In MainWindowViewModelTests.csTestMethodpublic void TestCloseAllCustomersWorkspace()/ Cr

51、eate the MainWindowViewModel, but not the MainWindow.MainWindowViewModel target =new MainWindowViewModel(Constants.CUSTOMER_DATA_FILE);Assert.AreEqual(0, target.Workspaces.Count, "Workspaces isn't empty.");/ Find the command that opens the "All Customers" workspace.CommandVie

52、wModel commandVM = target.Commands.First(cvm => cvm.DisplayName = "View all customers");/ Open the "All Customers" workspace. commandVM.Command.Execute(null);Assert.AreEqual(1, target.Workspaces.Count, "Did not create viewmodel.");/ Ensure the correct type of workspa

53、ce was created.var allCustomersVM = target.Workspaces0 as AllCustomersViewModel; Assert.IsNotNull(allCustomersVM, "Wrong viewmodel type created.");/ Tell the "All Customers" workspace to close. allCustomersVM.CloseCommand.Execute(null); Assert.AreEqual(0, target.Workspaces.Count,

54、 "Did not close viewmodel."); 把 View 應用到 ViewModel 上MainWindowViewModel 間接從主窗體的 TabControl 控件中增加移除 WorkspaceViewModel 對象。通過數據綁定 , TabItem 的 Content 屬性顯示繼承 于 ViewModelBase 的對象。ViewModelBase并不是一個 UI元件,因此他并不支持渲染它自己。 默認在TextBlock中,WPF 的一個非可視化對象通過調用 ToString 方法以顯示該對象。 很明顯這不是你想要的, 除非你 的用戶迫切的想知道

55、ViewModel 的類型名。我們通過強類型數據模版很容易告訴 WPF如何渲染ViewModel對象。強類型數據模版 key 屬性名沒有賦值,但是其DataType屬性要賦以類型類的實例。如果WPF要去渲染ViewModel 對象,它會檢查在資源系統范圍內是否有一個強類型數據模版的DataType 和 ViewModel 對象(或者其基類) 的類型一樣。 如果找到一個這樣的模版的話, 他會用該模版去渲染被 TabItem Content 屬性綁定的 ViewModel 對象。MainWindowResources.xaml 文件中有一個 ResourceDictionary (資源字典) ,該

56、字典被增加到 主窗體的資源層級中,這意味著文件包含的資源在正窗體范圍內有效。當一個 TabItem 的 Content屬性設置ViewModel對象時,該字典中的強類型數據模版會提供一個View (即用戶自定義控件)去渲染 Tabitem Content。具體如圖10所示圖 10 提供 View<!-This resource dictionary is used by the MainWindow.-><ResourceDictionary xmlns=" xmlns:x="xmlns:vm="clr-namespace:DemoApp.Vie

57、wModel"xmlns:vw="clr-namespace:DemoApp.View"><!-This template applies an AllCustomersView to an instanceof the AllCustomersViewModel class shown in the main window.-><DataTemplate DataType="x:Type vm:AllCustomersViewModel"><vw:AllCustomersView /></Dat

58、aTemplate><!-This template applies a CustomerView to an instanceof the CustomerViewModel class shown in the main window.-><DataTemplate DataType="x:Type vm:CustomerViewModel"><vw:CustomerView /></DataTemplate><!- Other resources omitted for clarity. -></ResourceDictionary>你不需要寫任何代碼去決定哪一個View去展示ViewModel對象。WPF資源系統把你從繁重的工作解脫出來,讓你去關注更重要的事情。在復雜的場景中,可能需要通過編程去選擇View,但是在大部分情況下,通過編程選擇View是不必要的。The Data Model and Repository 數據模型和存儲庫 你已經知道應用程序如何去加載, 顯示以及關閉一個 ViewModel 對象?,F在一切已經就位, 你可以在整個應用程序范圍內,回顧一下具體實現的細節(jié)。在深入理解應用程序的兩個

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論