轉(zhuǎn)帖|其它|編輯:郝浩|2011-07-20 14:18:56.000|閱讀 906 次
概述:作為一個(gè)應(yīng)用程序開發(fā)框架,silverlight越來越流行,有關(guān)設(shè)計(jì)模式的討論的呼聲也越來越高,幸運(yùn)的是,在silverlight的世界中,大部分silverlight開發(fā)人員都逐漸認(rèn)可Model-View-ViewModel (MVVM)模式,MVVM模式把應(yīng)用程序分成幾個(gè)獨(dú)立的層次,這樣的做法有許多好處:更好的代碼重用、增強(qiáng)測(cè)試功能,本文章將解釋MVVM中的關(guān)鍵概念,并且以一種簡(jiǎn)單容易理解的方式來介紹展示。我同時(shí)也會(huì)寫一些代碼來解釋MVVM怎么使用,代碼會(huì)在文章的稍后部分展示,也可以在此下載。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
作為一個(gè)應(yīng)用程序開發(fā)框架,silverlight越來越流行,有關(guān)設(shè)計(jì)模式的討論的呼聲也越來越高,幸運(yùn)的是,在silverlight的世界中,大部分silverlight開發(fā)人員都逐漸認(rèn)可Model-View-ViewModel (MVVM)模式,MVVM模式把應(yīng)用程序分成幾個(gè)獨(dú)立的層次,這樣的做法有許多好處:更好的代碼重用、增強(qiáng)測(cè)試功能,本文章將解釋MVVM中的關(guān)鍵概念,并且以一種簡(jiǎn)單容易理解的方式來介紹展示。我同時(shí)也會(huì)寫一些代碼來解釋MVVM怎么使用,代碼會(huì)在文章的稍后部分展示,也可以在此下載。
MVVM模式入門
MVVM會(huì)定義三部分,包括Model、View和ViewModel,下圖中展示圖片是來自我們silverlight課程中的幻燈片,以一種簡(jiǎn)明的方式總結(jié)了MVVM模式中各個(gè)部分。
通過每一部分的描述,可以看到Model代表了業(yè)務(wù)領(lǐng)域,包括實(shí)體類(如Customer、order等)、數(shù)據(jù)訪問和業(yè)務(wù)規(guī)則,一般說來,你可以把 Model看做服務(wù)端的實(shí)體,也是負(fù)責(zé)與應(yīng)用程序中數(shù)據(jù)交互的對(duì)象和填充實(shí)體的數(shù)據(jù)。也有人認(rèn)為Model只代表了應(yīng)用程序中的實(shí)體類(Customer、order等類),我個(gè)人認(rèn)為它是更加廣泛的,還包括了數(shù)據(jù)訪問和業(yè)務(wù)規(guī)則。silverlight程序通過WCF、ASMX、 REST編寫的服務(wù)甚至自定義的解決方案來調(diào)用Model中的代碼。
View代表了你創(chuàng)建的silverlight頁(yè)面,它包括了XAML文件和后臺(tái)代碼文件,負(fù)責(zé)將數(shù)據(jù)展示給終端用戶。View的功能在于顯示數(shù)據(jù)以及從終端用戶處收集數(shù)據(jù)。View不負(fù)責(zé)檢索數(shù)據(jù),執(zhí)行業(yè)務(wù)規(guī)則和檢驗(yàn)數(shù)據(jù)。
ViewModel可以看做是View和Model的中間人,負(fù)責(zé)聚合存儲(chǔ)數(shù)據(jù),并將會(huì)綁定到View上。例如,一ViewModel可能包含 List屬性和List 屬性,綁定到View中的兩ComboBox控件,ViewModel將會(huì)從Model中檢索出這兩個(gè)屬性值,使用ViewModel,View不必?fù)?dān)心在不知道數(shù)據(jù)來源的情況下檢索數(shù)據(jù)。
附件的成員可能會(huì)被添加到Model-View-ViewModel中以實(shí)現(xiàn)進(jìn)一步的隔離,例如,我通常會(huì)創(chuàng)建一service agent類,service agent初始化服務(wù)調(diào)用、捕獲已返回的數(shù)據(jù)、發(fā)送數(shù)據(jù)到ViewModel。這樣,ViewModel就把聚合數(shù)據(jù)的職責(zé)委托給了service agent。此service agent可以根據(jù)需要在多個(gè)ViewModel中重用。下圖是展示的把service agent集成到MVVM模式中。
當(dāng)開發(fā)人員第一次開始創(chuàng)建silverlight應(yīng)用程序時(shí),一般會(huì)把所有的代碼都添加到后臺(tái)文件中(例如MainPage.xaml.cs),雖然這樣也無可厚非,但使用MVVM模式有很多有利點(diǎn),如代碼重用、易維護(hù)、代碼模塊化以及增強(qiáng)的測(cè)試支持,在以下的文章中,我將會(huì)側(cè)重于通過使用MVVM模式獲得的益處。
Model
有許多不同的方法來創(chuàng)建Model,如Microsoft Entity Framework、LINQ to SQL、nHibernate、PLINQO、SubSonic等,具體選用哪種技術(shù)要根據(jù)公司的開發(fā)規(guī)則,所有在這里我不打算對(duì)每種技術(shù)的優(yōu)劣點(diǎn)進(jìn)行討論,重要的是使用工具或者手寫代碼來創(chuàng)建Model,包括定義類要暴露的所有屬性,例如,下面是一簡(jiǎn)單的Model類:
public class Person
{
public string FirstName { get;set;}
public string LastName { get;set; }
public int Age { get; set; }
}
一旦Model類創(chuàng)建完畢,需要通過編寫自定義代碼或者ORM框架來填充數(shù)據(jù),處理查詢結(jié)果映射到對(duì)象
實(shí)例。然后使用WCF、ASMX或者自定義的REST服務(wù)來編寫Services,來暴露一個(gè)或者多個(gè)Model類,
在Silverlight應(yīng)用程序中使用。
View和ViewModel
在準(zhǔn)備好Model后,View和ViewModel就可以創(chuàng)建了,View依靠ViewModel類來檢索數(shù)據(jù),然后綁定到ViewModel中的屬性,而不是添加所有的代碼到View的后臺(tái)cs代碼中。
ViewModel類需要實(shí)現(xiàn)INotifyPropertyChanged借口,其中定義了一個(gè)事件PropertyChanged,這個(gè)事件用來告知 silverlight綁定,數(shù)據(jù)已經(jīng)改變,然后控件也可以自動(dòng)更新,雖然INotifyPropertyChanged也可以在ViewModel類中直接實(shí)現(xiàn),然而,你的應(yīng)用程序中可能包含多個(gè)ViewModel類,那么就需要寫很多重復(fù)的代碼,創(chuàng)建一個(gè)ViewModel的基類,來實(shí)現(xiàn) INotifyPropertyChanged,來達(dá)到代碼重用的目的。下面的代碼定義了一個(gè)ViewModelBase的類,實(shí)現(xiàn)了 INotifyPropertyChanged接口,該類也提供了OnNotifyPropertyChanged方法,來觸發(fā) PropertyChanged事件。
public class ViewModelBase : INotifyPropertyChanged
{
protected void OnNotifyPropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
public bool IsDesignTime
{
get
{
return (Application.Current ==
null) || (Application.Current.GetType() == typeof(Application));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
ViewModel類繼承自ViewModelBase,下面是一個(gè)叫PeopleViewModel的ViewModel類繼承自ViewModelBase:
public class PeopleViewModel : ViewModelBase
{
IServiceAgent _ServiceAgent;
Person _Person;
ObservableCollection <Person> _People;
public PeopleViewModel() : this(new ServiceAgent()) {}
public PeopleViewModel(IServiceAgent serviceAgent)
{
if (!IsDesignTime)
{
_ServiceAgent = serviceAgent;
GetPeople();
}
}
#region Properties
public Person Person
{
get
{
return _Person;
}
set
{
if (_Person != value)
{
_Person = value;
OnNotifyPropertyChanged( "Person");
}
}
}
public ObservableCollection <Person> People {
get
{
return _People;
}
set
{
if (_People != value)
{
_People = value;
OnNotifyPropertyChanged( "People");
}
}
}
#endregion
public void GetPeople()
{
_ServiceAgent.GetPeople((s,e) = > this.People = e.Result);
}
public void UpdatePerson()
{
_ServiceAgent.UpdatePerson(this.Person, (s, e) = >
{
PeopleEventBus.OnOperationCompleted(this, new OperationCompletedEventArgs
{ OperationStatus = e.Result });
});
}
}
我們可以看到PeopleViewModel定義了兩個(gè)fields、兩個(gè)properties和兩個(gè)methods,每一個(gè)屬性都會(huì)觸發(fā)PropertyChanged事件,因?yàn)樵趕et塊中調(diào)用了在ViewModelBase中定義的OnNotifyPropertyChanged方法,在值改變時(shí),會(huì)通知綁定到此屬性上的控件自動(dòng)更新。在PeopleViewModel中的第一個(gè)構(gòu)造器中,將會(huì)調(diào)用第二個(gè)構(gòu)造器,有一個(gè)IServiceAgent類型的參數(shù)。為什么會(huì)有兩個(gè)構(gòu)造器呢?這樣設(shè)計(jì),測(cè)試框架可以給ViewModel傳入不同類型的service agents,當(dāng)ViewModel在運(yùn)行時(shí)被調(diào)用時(shí),當(dāng)參數(shù)的構(gòu)造器會(huì)被調(diào)用,ServiceAgent的實(shí)例對(duì)象會(huì)作為IServiceAgent類型的參數(shù)傳遞。一旦service agent對(duì)象傳給ViewModel的構(gòu)造器,會(huì)調(diào)用GetPeople方法,最終會(huì)調(diào)用service agent中的方法。service agent會(huì)調(diào)用WCF服務(wù),結(jié)果值分配給People屬性。
public interface IServiceAgent
{
void GetPeople(EventHandler <GetPeopleCompletedEventArgs> callback);
void UpdatePerson(Person p, EventHandler
<UpdatePersonCompletedEventArgs> callback);
}
public class ServiceAgent : IServiceAgent
{
public void GetPeople(EventHandler <GetPeopleCompletedEventArgs> callback)
{
PeopleServiceClient proxy = new PeopleServiceClient();
proxy.GetPeopleCompleted += callback;
proxy.GetPeopleAsync();
}
public void UpdatePerson(Person p, EventHandler
<UpdatePersonCompletedEventArgs> callback)
{
PeopleServiceClient proxy = new PeopleServiceClient();
proxy.UpdatePersonCompleted += callback;
proxy.UpdatePersonAsync(p);
}
}
綁定ViewModel至View
ViewModel可以綁定到View,可以在XAML中聲明,也可以在CS后臺(tái)代碼中實(shí)現(xiàn),分配一個(gè)ViewModel實(shí)例給布局元素根的DataContext屬性
this.LayoutRoot.DataContext = new PeopleViewModel();
下面是一綁定ViewModel到Model的例子:
<UserControl x:Class="ViewModelExample.MainPage"
xmlns="//schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x= "//schemas.microsoft.com/winfx/2006/xaml"
xmlns:d= "//schemas.microsoft.com/expression/blend/2008" xmlns:mc="//schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converter= "clr-namespace:ViewModelExample"
xmlns:viewModel= "clr-namespace:ViewModelExample.ViewModel"
mc:Ignorable= "d" d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<viewModel:PeopleViewModel x:Key="ViewModel" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewModel}}">
</Grid>
</UserControl>
ViewModel命名空間使用ViewModel作為XML命名空間的前綴,然后ViewModel使用關(guān)鍵字ViewModel定義在中,關(guān)鍵字是非常重要的,因?yàn)樗?ldquo;劫持”ViewModel至DataContext,layout的子元素可以綁定在ViewModel上,下面是綁定ListBox、StackPanel至ViewModel的People屬性。
<StackPanel Margin="20"> <TextBlock Text="Binding Controls to a ViewModel" Margin="20,0,0,0" FontWeight="Bold" FontSize="12" /> <ListBox x:Name="lbPeople" Margin="0,10,0,0" Height="250" Width="300" HorizontalAlignment="Left" ItemsSource="{Binding People}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" SelectedItem="{Binding Person, Mode=TwoWay}"> <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Margin="10" Text="{Binding FirstName}" /> <TextBlock Grid.Column="1" Margin="10" Text="{Binding LastName}" /> <TextBlock Grid.Column="2" Margin="10" Text="{Binding Age}" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel DataContext="{Binding Person}"> <TextBlock Text="First Name" Margin="0,10,0,0" /> <TextBox Text="{Binding FirstName,Mode=TwoWay}" Width="100" Height="25" HorizontalAlignment="Left"/> <TextBlock Text="Last Name" /> <TextBox Text="{Binding LastName,Mode=TwoWay}" Width="100" Height="25" HorizontalAlignment="Left"/> <TextBlock Text="Age" /> <TextBox Text="{Binding Age,Mode=TwoWay}" Width="100" Height="25" HorizontalAlignment="Left"/> </StackPanel> <Button Margin="0,10,0,0" Click="Button_Click" Content="Submit" Height="20" Width="100" HorizontalAlignment="Left" /> </StackPanel>
MVVM模式提供了靈活的方式來處理數(shù)據(jù),增加代碼重用、簡(jiǎn)化、易維護(hù)。當(dāng)然MVVM還有更多可討論的地方,例如:event buses、commanding、dependency injection,但我希望這篇文章可以幫助你開發(fā)silverlight應(yīng)用程序。
譯自://weblogs.asp.net/dwahlin/archive/2009/12/08/getting-started-with-the-mvvm-pattern-in-silverlight-applications.aspx
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園