MVVM,即Model-View-ViewModel。该设计模式的主要思路是:模型(Model)和视图(View)通过视图模型(View Model)进行交互,以此来剥离逻辑和界面之间的耦合。MVVM设计模式的应用目前就我所知只能建立在WPF技术之上,这是因为WPF提供了几种能够使MVVM成为可能的技术,比如数据绑定,下面将会提到。
使用该设计模式好处:
1. 提高代码的可复用性- 在产品升级换代的时候,我们的大部分逻辑代码仍将能继续使用,而不会因为用户界面的变化而需要修改逻辑的代码。
2. 提高代码可测试性 - 由于逻辑和界面不再有耦合,基本上可以为所有的代码写测试用例,大大提高代码质量。
3. 不再需要Code-Behind代码 - 通过WPF的特定技术,可以不再需要写Code-Behind代码,比如按钮的Click操作,以前的技术,我们需要在Code-Behind注册Click事件的处理函数,而WPF的绑定技术可以避免Code-Behind。
经过一段时间的学习和实践,下面对MVVM模式进行简单的总结,下面的代码使用C#语言实现。
-- 如果你不懂WPF,在阅读下面内容之前,建议你先了解WPF的概念。
Model
即模型,可以理解为数据模型和商业逻辑,它完全不依赖于视图。
View
即视图或者界面。
ViewModel
即视图模型或者界面模型,View会直接访问ViewModel,这种访问可以使用WPF的数据绑定技术来达到。需要特别注意的是ViewModel不会也不应该去访问View,比如在ViewModel中获取一个按钮实例。有些时候我们需要控制View的状态,这种情况可以在ViewModel中设计合适的状态类型,然后和View中特定的控件进行绑定。
我们可以实现一个ViewModel 的抽象类来作为所有ViewModel的基类,代码如下:
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChagned(this, new PropertyChangedEventArgs(propertyName))
}
}
}
如果你不知道接口INotifyPropertyChanged用来干什么的,那就在网上搜索一下吧!
Model 和 ViewModel 之间的关系
一般情况下ViewModel会包含一个Model实例,然后通过自己的接口把Model的数据暴露出来让View来使用。
public class Customer
{
public string Name {get; set;}
}
public class CustomerViewModel : ViewModel
{
public CustomerViewModel(Customer customer)
{
m_Customer = customer;
}
private Customer m_Customer;
public string Name
{
get { return m_Customer.Name; }
set
{
if(m_Customer.Name ! = value)
{
m_Customer.Name = value;
OnPropertyChanged("Name");
}
}
}
}
问题:如果Model中有一个List,并且需要显示到View中,那么ViewModel应该怎么处理呢?比如:
public class Customer
{
public List<string> Habites { get; private set;}
}
最直接的方法就是同样建立一个对应的ObservableCollection<string>。但是你会发现一个问题:怎么同步Model和ViewModel的这两个集合?最直接的方法就是通过事件来通知了,但是你会发现这样的代码并不是很简洁。目前我也没有一个好办法来解决这个问题:(。
ViewModel 和 View 之间的关系
ViewModel和View之间的关系是控制和显示的关系,ViewModel会控制View的显示,包括显示的内容和视图的状态。一般情况我们会为ViewModel定义一个DataTemplate,比如:
<DataTemplate DataType="{x:Type local:CustomerViewModel}">
<Grid>
<TextBlock Text="{Binding Name}"/>
</Grid>
</DataTemplate>
按钮的Click事件的处理等,我们不再通过注册事件来处理,而是把ViewModel中的Command绑定到按钮中,比如
<Button Command={Binding AddCustomerCommand}/>
这样就避免了Code-Behind了,逻辑代码可以不依赖于界面。
常见问题及技术解决:
1. 事件绑定不了Command。
在实际的应用当中,你会发现有些时候很难做到不使用Code-Behind,比如一些事件,例如MouseDoubleClick,这些并不能进行Command的绑定,但是事实上,WPF已经提供了另一种技术 -- Attached Property -- 来解决的。网上有各种各样的Attached Property实现,但你遇到困难时不妨搜索一下。
这是一篇关于Attached Property的简单文章:http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
2. Dialog怎么应用MVVM。
这也是比较常见的问题,网上也有不少讨论,我将会总结一下我是怎么处理这个问题的。
View 和 Model 之间的关系
你会发现我们其实可以直接给Model定义View,而且免去了ViewModel这层,这显得更简单。但是这样做我个人觉得并不合适,这样做有可能会使你的Model变的不是很纯粹,比如:最后你可能会定义一些控件界面状态的字段来控制界面的状态,别忘了我们的目的就是为了让Model和View不再有任何耦合,除非你的觉得你的Model非常的简单,不会有任何的影响,不然最后又会回到原来的很糟糕的状态--逻辑和视图绑定到一块了。
下面是我学习的一片文章,里面还有一个很好的例子。
参考:
1. http://msdn.microsoft.com/en-us/magazine/dd419663.aspx