什么是MVVM模式
問題引入
1?場景一:團隊辛辛苦苦完成了一個項目,抱著激動的心情去給用戶做demo,而用戶給你的反饋是UI很不滿意,要重新修改,否則拒絕驗收。大規模修改UI,晴天霹靂!
2?場景二:產品在一家客戶上線運行反應不錯,公司準備擴大營銷市場,尋求更多的客戶,此時,不同客戶對UI紛紛提出修改意見,眾口難調,但是老總發話,客戶是上帝!
問題出來了,按照傳統的開發模式是基于CodeBehind這樣的方式,UI總是和業務邏輯緊密耦合在一起,?UI修改,無法避免的業務邏輯修改隨之而來,這無非就是我們老生常談的解耦問題,有沒有辦法做到UI層剝離出邏輯層呢?MVVM模式為你排憂解難。
一?什么是MVVM模式
MVVM(Model-View-ViewModel)是專為WPF和SilverLight設計的開發模式,?與之類似的有Asp.net程序對應的MVC模式,?WinForm程序對應的MVP,?關于MVC,?MVP此處不展開論述,詳情參考http://msdn.microsoft.com/zh-cn/library/dd381412(v=vs.98).aspx。?但在MVC和MVP模式中,?View層都具有很多代碼邏輯,?最簡單的例子是在MVC中當界面發生交互時View去調用Controler中的某個方法,所以?并沒有真正意義上實現View與ViewModel完全分離。
WPF真正引人入勝、使之與WinForm涇渭分明的特點就是——“數據驅動界面”,何為“數據驅動界面”?,?與傳統的“事件驅動見面”相比較,數據編程了核心,UI處于從屬地位;數據是底層、是心臟,數據變了作為表層的UI就會跟著變、將數據展現給用戶;如果用戶修改了UI元素上的值,相當于透過UI元素直接修改了底層的數據;圍繞著這個核心,WPF準備了很多概念相當前衛的技術,其中包括為界面準備的XAML、為底層數據準備的Dependency?Property?&?Binding和為消息傳遞準備的Routed?Event?&?Command。?
Binding和Command技術的出現,也為MVVM模式成為WPF平臺下一個優秀的開發模式奠定了基礎。通過Binding,可以綁定一個View的Property到ViewModel,?ViewModel?對象被設置為視圖的?DataContex,如果屬性值在?ViewModel?更改,這些新值自動傳播到通過數據綁定的視圖,實現在ViewModel里可以不通過編寫任何邏輯代碼就直接更新View,做到View與ViewModel之間的完全松耦合,關于Binding,想了解更多可參見?Data?and?WPF:?Customize?Data?Display?with?Data?Binding?and?WPF?"。?同樣,如果沒有WPF中的Command,?MVVM也很難展示出它的強大力量,ViewModel可將Command暴露給View,?使得View可以消費command中對應的邏輯功能,對于不熟悉command的朋友,可以參考這篇文章Advanced?WPF:?Understanding?Routed?Events?and?Commands?in?WPF。
下面簡要介紹一下MVVM每個模塊的主要職責
1) View主要用于界面呈現,與用戶輸入設備進行交互,在code-Behind中還可以些一些UI的邏輯的,比如一些豐富的動畫效果,或者直接設置某個元素的樣式等,此外,設置View層的DataContext為對于的ViewModel層的邏輯也是寫在code-Behind中。
2) ViewModel是MVVM架構中最重要的部分,ViewModel中包含屬性,命令,方法,事件,屬性驗證等邏輯,用于邏輯實現,負責View與Model之間的通信。
3) Model就是我們常說的數據模型,用于數據的構造,數據驅動,?主要提供基礎實體的屬性以及每個屬性的驗證邏輯。
MVVM中各個模塊的交互方式如圖所示:
?
二?為什么要使用MVVM模式
MVVM模式的引入能給我們帶來什么優勢呢?相信這是大多數學習MVVM的人關心的一個主要問題。
首先我們應該清楚地認識到,MVVM不是適用于任何的項目開發,一個項目是否要上一套框架取決于項目本身的規模和性質,盲目的使用開發模式可能會引起過度開發,通常情況下,企業級的WPF應用軟件建議使用,主要優勢下面將展開詳細闡述。
1團隊層面?統一了項目團隊的思維方式,也改變了開發方式,由于View與ViewModel之間的松耦合關系,我們可以輕易做到開發團隊與設計團隊的明確分工,開發團隊可以專注于創建功能強大的?ViewModel?類,而設計團隊能夠熟練運用Blend等工具能為程序員輸出用戶友好的試圖View的XAML文件。而且,隨著項目的進行,不斷會有新的成員加入,一個清晰的項目設計模式,能夠很大程度地減少他熟悉項目的所需時間,并能夠規范的進行接下來的開發維護工作。
2?架構層面?項目架構更加穩定,模塊之間松散的耦合關系使得模塊之間的相互依賴性大大降低,這也就意味著項目的擴展性得到了提高,即使以后需要加一些新的模塊,或者實現模塊的注入,我們也能做到最小的改動,從而保證項目的穩定。
3?代碼層面MVVM的引入也使得項目本身變得模塊清晰化,條理化,有助于我們更好地區分哪些邏輯是屬于UI操作,哪些邏輯是業務操作,增強了代碼的可讀性、可測性。對于ViewModel層,Views和Unit?tests是兩個不同類型的消費者,應用程序中的主要交互邏輯處于ViewModel層,這樣,在完成ViewModel之后,我們完全可以有理由相信,我們可以對ViewModel進行單元測試,因為它不依賴于任何UI控件,從這個角度看,似乎UnitTest相比于View而言具備更大的消費能力。
三?詳解ViewModel
ViewModel是MVVM架構中最重要的部分,負責View與Model直接的通信,對于ViewModel的理解是掌握MVVM的關鍵,下面我們針對ViewModel進行詳細剖析。?
1?ViewModel的屬性ViewModel的屬性是View數據的來源,但ViewModel層不能是Model層的簡單封裝,ViewModel層也不能是View層的簡單映射。ViewModel的屬性可由三部分組成:一部分是Model的復制屬性;另一部分用于控制UI狀態。例如Button屬性的Disable屬性,當操作完成時可以通過這個屬性更改通知View做相應的UI變換或者后面提到的事件通知;第三部分是一些方法的參數,可以將這些方法的參數設置成相應的屬性綁定到View中的某個控件,然后在執行方法的時候獲取這些屬性,所以一般方法不含參數。
2?ViewModel的命令?ViewModel中的命令用于接受View的用戶輸入,并做相應的處理。我們也可以通過方法實現相同的功能。
3?ViewModel的事件??ViewModel中的事件主要用來通知View做相應的UI變換。它一般在一個處理完成之后觸發,隨后需要View做出相應的非業務的操作。所以一般ViewModel中的事件的訂閱者只是View,除非其他自定義的非View類之間的交互。
4?View及ViewModel交互模式
在View與ViewModel模型之間進行雙向的聯系的主要方式是通過數據綁定。當正確地使用該設計模式后,每一個View除了純凈的XAML和非常少量的后置代碼外不會再包含任何東西,徹底地做到了界面展示和業務邏輯的分離,讓程序員更加專注于代碼的編寫。
ViewModel也能用來容納View的狀態以及執行View需要的任何命令。
因為WPF內置了Command模式,對于像Button控件之類的UI元素來說都有一個Command的屬性,它是WPF所定義的ICommand類型。可以把這些命令放到ViewModel中并以公有屬性的形式暴露出來,這樣就可以讓View對其進行綁定。這極其強大,因為它可以把ModelView中的可執行代碼綁定到窗體的Button上。
四?MVVM實踐
理論知識已經準備充分,現在是檢驗真理的時刻,以下是使用了Model-View-ViewModel?設計模式的s世上最簡單的WPF應用程序例子,簡單加法計算器。
1?代碼結構如下圖:
?
2?CaculatorModel類:
public?class?CaculatorModel
????{
????????public?int?Num1?{?get;?set;?}
????????public?int?Num2?{?get;?set;?}
????????public?int?Result?{?get;?set;?}
}
3?ICommand類型的基類DelegateCommand
using?System;
using?System.Windows.Input;
namespace?MVVMDemo.Commands
{
????public?class?DelegateCommand:ICommand
????{
????????public?DelegateCommand(Action<object>?executeCommand,?Func<object,?bool>?canExecuteCommand)
????????{
????????????this.executeCommand?=?executeCommand;
????????????this.canExecuteCommand?=?canExecuteCommand;
????????}
????????//?The?specific?ExecuteCommand?aciton?will?come?from?the?ViewModel,?the?same?as?CanExecuteCommand
????????private?Action<object>?executeCommand;
????????public?Action<object>?ExecuteCommand
????????{
????????????get?{?return?executeCommand;?}
????????????set?{?executeCommand?=?value;?}
????????}
????????private?Func<object,?bool>?canExecuteCommand;
????????public?Func<object,?bool>?CanExecuteCommand
????????{
????????????get?{?return?canExecuteCommand;?}
????????????set?{?canExecuteCommand?=?value;?}
????????}
????????public?event?EventHandler?CanExecuteChanged;
????????public?bool?CanExecute(object?parameter)
????????{
????????????if?(CanExecuteCommand?!=?null)
????????????{
????????????????return?this.CanExecuteCommand(parameter);
????????????}
????????????else
????????????{
????????????????return?true;
????????????}
????????}
????????public?void?Execute(object?parameter)
????????{
????????????if?(this.ExecuteCommand?!=?null)?this.ExecuteCommand(parameter);
????????}
????????public?void?RaiseCanExecuteChanged()
????????{
????????????if?(CanExecuteChanged?!=?null)
????????????{
????????????????CanExecuteChanged(this,?EventArgs.Empty);
????????????}
????????}
????}
}
注:ICommand中有兩個方法CanExecute和Execute必須實現,這兩個方法分別對應著當Command調用時判斷是否能執行和具體執行邏輯。
4?ViewModelBase類
using?System.ComponentModel;
namespace?MVVM.ViewModel
{
????public?class?ViewModelBase?:?INotifyPropertyChanged
????{
????????public?event?PropertyChangedEventHandler?PropertyChanged;
????????public?void?RaisePropertyChanged(string?propertyName)
????????{
????????????if?(this.PropertyChanged?!=?null)
????????????{
????????????????this.PropertyChanged(this,?new?PropertyChangedEventArgs(propertyName));
????????????}
????????}
????}
}
注:ViewModelBase實現了接口INotifyPropertyChanged,?在該接口中有一個PropertyChanged事件,?當ViewModel中的Property改變時,允許觸發PropertyChanged事件,繼而重新綁定數據到UI上。
5?CaculatorViewModel類
using?System.Windows.Input;
using?MVVM.Model;
using?MVVMDemo.Commands;
namespace?MVVM.ViewModel
{
????public?class?CaculatorViewModel:ViewModelBase
????{
????????#region?Fields
????????private?int?num1;
????????private?int?num2;
????????private?int?result;
????????private?CaculatorModel?model;
????????#endregion
????????#region?Properties
????????public?int?Num1
????????{
????????????get?
????????????{
????????????????return?num1;
????????????}
????????????set
????????????{
????????????????num1?=?value;
????????????????this.RaisePropertyChanged("Num1");
????????????}
????????}
????????public?int?Num2
????????{
????????????get
????????????{
????????????????return?num2;
????????????}
????????????set
????????????{
????????????????num2?=?value;
????????????????this.RaisePropertyChanged("Num2");
????????????}
????????}
????????public?int?Result
????????{
????????????get
????????????{
????????????????return?result;
????????????}
????????????set
????????????{
????????????????result?=?value;
????????????????this.RaisePropertyChanged("Result");
????????????}
????????}
????????#endregion
????????#region?Commands
????????public?ICommand?CaculateCommand{get;set;}
????????public?ICommand?ClearCommand?{?get;?set;?}
????????#endregion
????????#region?Methods
????????public?void?Add(object?param)
????????{
????????????Result?=?Num1?+?Num2;
????????}
????????public?void?Clear(object?param)
????????{
????????????Result?=?0;
????????????Num1?=?0;
????????????Num2?=?0;
????????}
????????public?void?InitilizeModelData()
????????{
????????????//?In?gernal,?the?data?comes?from?database
????????????var?model?=?new?CaculatorModel()
????????????{
????????????????Num1?=?1,
????????????????Num2?=?1,
????????????????Result?=?2
????????????};
????????????Num1?=?model.Num1;
????????????Num2?=?model.Num2;
????????????Result?=?model.Result;
????????}
????????public?CaculatorViewModel()
????????{
????????????CaculateCommand?=?new?DelegateCommand(Add,?null);
????????????ClearCommand??=?new?DelegateCommand(Clear,?null);
????????????InitilizeModelData();
????????}
????????#endregion
????}
}
6?簡單計算器的UI
?
該View所對應的XAML文件如下:
<Window?x:Class="MVVM.View.CaculatorView"
????????xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
????????xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
????????Title="CaculatorView"?Height="300"?Width="682">
????<Grid?Width="596">
????????<TextBox?Height="23"?HorizontalAlignment="Left"?Margin="41,90,0,0"?Name="txtNum1"?VerticalAlignment="Top"?Width="120"?Text="{Binding?Num1}"/>
????????<TextBox?Height="25"?HorizontalAlignment="Left"?Margin="195,88,0,0"?Name="txtNum2"?VerticalAlignment="Top"?Width="120"?Text="{Binding?Num2}"/>
????????<Label?Content="+"?Height="28"?HorizontalAlignment="Left"?Margin="167,88,0,0"?Name="label1"?VerticalAlignment="Top"?/>
????????<TextBox?Height="25"?HorizontalAlignment="Left"?Margin="364,88,0,0"?Name="textBox5"?VerticalAlignment="Top"?Width="120"??Text="{Binding?Result}"/>
????????<Button?Content"="?Height="23"?HorizontalAlignment="Left"?Margin="328,90,0,0"?Name="button1"?VerticalAlignment="Top"?Width="28"?Command="{Binding?CaculateCommand}"?/>
????????<Button?Content="Clear"?Height="26"?HorizontalAlignment="Left"?Margin="501,88,0,0"?Name="button2"?VerticalAlignment="Top"?Width="45"?Command="{Binding?ClearCommand}"?/>
????</Grid>
</Window>
6.?View的Code-Behind
using?System.Windows;
using?MVVM.ViewModel;
namespace?MVVM.View
{
????///?<summary>
????///?CaculatorView.xaml?的ì?交?互£¤邏?輯-
????///?</summary>
????public?partial?class?CaculatorView?:?Window
????{
????????public?CaculatorView()
????????{
????????????InitializeComponent();
????????????this.DataContext?=?new?CaculatorViewModel();
????????}
????}
}
注:這里講View的DataContext設為CaculatorViewModel實例,至此,View和ViewModel建立關聯。
轉載于:https://www.cnblogs.com/zuochanzi/p/6283007.html
總結
- 上一篇: 光纤猫怎样连接无线路由器 光纤猫如何连接
- 下一篇: 大数加法和乘法