轉帖|其它|編輯:郝浩|2011-07-20 14:18:56.000|閱讀 906 次
概述:作為一個應用程序開發框架,silverlight越來越流行,有關設計模式的討論的呼聲也越來越高,幸運的是,在silverlight的世界中,大部分silverlight開發人員都逐漸認可Model-View-ViewModel (MVVM)模式,MVVM模式把應用程序分成幾個獨立的層次,這樣的做法有許多好處:更好的代碼重用、增強測試功能,本文章將解釋MVVM中的關鍵概念,并且以一種簡單容易理解的方式來介紹展示。我同時也會寫一些代碼來解釋MVVM怎么使用,代碼會在文章的稍后部分展示,也可以在此下載。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
作為一個應用程序開發框架,silverlight越來越流行,有關設計模式的討論的呼聲也越來越高,幸運的是,在silverlight的世界中,大部分silverlight開發人員都逐漸認可Model-View-ViewModel (MVVM)模式,MVVM模式把應用程序分成幾個獨立的層次,這樣的做法有許多好處:更好的代碼重用、增強測試功能,本文章將解釋MVVM中的關鍵概念,并且以一種簡單容易理解的方式來介紹展示。我同時也會寫一些代碼來解釋MVVM怎么使用,代碼會在文章的稍后部分展示,也可以在此下載。
MVVM模式入門
MVVM會定義三部分,包括Model、View和ViewModel,下圖中展示圖片是來自我們silverlight課程中的幻燈片,以一種簡明的方式總結了MVVM模式中各個部分。
通過每一部分的描述,可以看到Model代表了業務領域,包括實體類(如Customer、order等)、數據訪問和業務規則,一般說來,你可以把 Model看做服務端的實體,也是負責與應用程序中數據交互的對象和填充實體的數據。也有人認為Model只代表了應用程序中的實體類(Customer、order等類),我個人認為它是更加廣泛的,還包括了數據訪問和業務規則。silverlight程序通過WCF、ASMX、 REST編寫的服務甚至自定義的解決方案來調用Model中的代碼。
View代表了你創建的silverlight頁面,它包括了XAML文件和后臺代碼文件,負責將數據展示給終端用戶。View的功能在于顯示數據以及從終端用戶處收集數據。View不負責檢索數據,執行業務規則和檢驗數據。
ViewModel可以看做是View和Model的中間人,負責聚合存儲數據,并將會綁定到View上。例如,一ViewModel可能包含 List屬性和List 屬性,綁定到View中的兩ComboBox控件,ViewModel將會從Model中檢索出這兩個屬性值,使用ViewModel,View不必擔心在不知道數據來源的情況下檢索數據。
附件的成員可能會被添加到Model-View-ViewModel中以實現進一步的隔離,例如,我通常會創建一service agent類,service agent初始化服務調用、捕獲已返回的數據、發送數據到ViewModel。這樣,ViewModel就把聚合數據的職責委托給了service agent。此service agent可以根據需要在多個ViewModel中重用。下圖是展示的把service agent集成到MVVM模式中。
當開發人員第一次開始創建silverlight應用程序時,一般會把所有的代碼都添加到后臺文件中(例如MainPage.xaml.cs),雖然這樣也無可厚非,但使用MVVM模式有很多有利點,如代碼重用、易維護、代碼模塊化以及增強的測試支持,在以下的文章中,我將會側重于通過使用MVVM模式獲得的益處。
Model
有許多不同的方法來創建Model,如Microsoft Entity Framework、LINQ to SQL、nHibernate、PLINQO、SubSonic等,具體選用哪種技術要根據公司的開發規則,所有在這里我不打算對每種技術的優劣點進行討論,重要的是使用工具或者手寫代碼來創建Model,包括定義類要暴露的所有屬性,例如,下面是一簡單的Model類:
public class Person
{
public string FirstName { get;set;}
public string LastName { get;set; }
public int Age { get; set; }
}
一旦Model類創建完畢,需要通過編寫自定義代碼或者ORM框架來填充數據,處理查詢結果映射到對象
實例。然后使用WCF、ASMX或者自定義的REST服務來編寫Services,來暴露一個或者多個Model類,
在Silverlight應用程序中使用。
View和ViewModel
在準備好Model后,View和ViewModel就可以創建了,View依靠ViewModel類來檢索數據,然后綁定到ViewModel中的屬性,而不是添加所有的代碼到View的后臺cs代碼中。
ViewModel類需要實現INotifyPropertyChanged借口,其中定義了一個事件PropertyChanged,這個事件用來告知 silverlight綁定,數據已經改變,然后控件也可以自動更新,雖然INotifyPropertyChanged也可以在ViewModel類中直接實現,然而,你的應用程序中可能包含多個ViewModel類,那么就需要寫很多重復的代碼,創建一個ViewModel的基類,來實現 INotifyPropertyChanged,來達到代碼重用的目的。下面的代碼定義了一個ViewModelBase的類,實現了 INotifyPropertyChanged接口,該類也提供了OnNotifyPropertyChanged方法,來觸發 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,下面是一個叫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定義了兩個fields、兩個properties和兩個methods,每一個屬性都會觸發PropertyChanged事件,因為在set塊中調用了在ViewModelBase中定義的OnNotifyPropertyChanged方法,在值改變時,會通知綁定到此屬性上的控件自動更新。在PeopleViewModel中的第一個構造器中,將會調用第二個構造器,有一個IServiceAgent類型的參數。為什么會有兩個構造器呢?這樣設計,測試框架可以給ViewModel傳入不同類型的service agents,當ViewModel在運行時被調用時,當參數的構造器會被調用,ServiceAgent的實例對象會作為IServiceAgent類型的參數傳遞。一旦service agent對象傳給ViewModel的構造器,會調用GetPeople方法,最終會調用service agent中的方法。service agent會調用WCF服務,結果值分配給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后臺代碼中實現,分配一個ViewModel實例給布局元素根的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使用關鍵字ViewModel定義在中,關鍵字是非常重要的,因為它“劫持”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模式提供了靈活的方式來處理數據,增加代碼重用、簡化、易維護。當然MVVM還有更多可討論的地方,例如:event buses、commanding、dependency injection,但我希望這篇文章可以幫助你開發silverlight應用程序。
譯自://weblogs.asp.net/dwahlin/archive/2009/12/08/getting-started-with-the-mvvm-pattern-in-silverlight-applications.aspx
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:博客園