写在章写的话:
本章内容涉及MVVM模式,这个模式虽然我略有耳闻,但绝对算不上精通(好吧可能入门都算不上)。我试图表达ppt原来的意思,所以这里的每一节我都花费很长的时间去完善,包括这里哪些名词需要翻译,哪些不需要。但是可能还有一些地方翻译的不是很准确或者说是有错误,还请大家多多批评指正。另最近比较忙,估计进度不会快。
本节导读:
本节说明了什么是MVVM,也说明了MVVM中的Model, View, ViewModel以及它们的作用和区分方法。
第五章 实现MVVM模式
Model-View-ViewModel模式帮助你清楚的从应用程序的用户界面中分离出业务和表现逻辑。保持清晰的应用程序逻辑和UI的分离有助于处理开发和设计问题也让应用程序更加轻松地进行测试,维护,和发展。也可以极大的提高代码的可重用性也使开发人员和UI设计师在他们分别开发应用程序的某一部分时更方便的沟通和交流。
使用MVVM模式,应用程序的UI和下层的表现层以及业务逻辑层被分到三个独立的“类”中:View,封装了UI和UI逻辑;View Model,封装了表现逻辑和状态;Model,封装了应用程序逻辑和数据。
Prism包含了如何在Silverlight和WPF应用程序下实现MVVM模式的示例和参考实现。Prism库也包含了帮助实现你自己应用程序模式的功能。这些功能都是最常用的实现MVVM模式的方法,并且都设计成为可以测试也与Expression Blend和Visual Studio能够良好的协同工作。
本章提供了MVVM的概述并且描述了如何实现这些基本特征。第六章描述了如何使用Prism库以实现更多高级的MVVM场景。
5.1 “类”的职责与特征
MVVM模式是Presentation Model的一个变种,最大程度上使用了WPF和Silverlight的核心功能,比如数据绑定,数据模版,命令和行为。
在MVVM模式中,View绑定了UI和UI逻辑,View Model封装了表现逻辑和状态,Model封装了业务逻辑和数据。View与View Model通过数据绑定,命令,改变通知事件相交互。View Model查询,监视,并且同步更新Model,转换,验证,并且汇总需要显示在View中的数据。
下图描述了MVVM中的类型和它们间的交互:
MVVM中的类型与它们间的交互
就像所有的表现层分离的模式一样,有效的使用MVVM模式的关键在于理解用适当的途径将应用程序的代码分解到正确的类中,理解这些类在复杂场景下的交互方式。以下的几节讲述了MVVM模式中这些类的职责和特征。
5.1.1 View类
View的职责是定义用户在屏幕上所见到的界面的结构和外表。在理想情况下,View的后置代码应该只包含调用InitializeComponent方法的构造函数。在一些情况下,后置代码也包含一些UI逻辑和实现一些使用对XAML而言很难或者说低效的可视化行为,比如复杂的动画,或者需要代码直接操纵视图中的可视化元素。你不应该在View中放置任何需要单元测试的逻辑代码。通常来说,UI的后置代码是通过UI自动化测试进行的。
在Silverlight和WPF中,视图中的数据绑定表达式是以它的上下文数据为根据的。在MVVM中,View的数据上下文被定义为View Model。View Model实现了视图可以绑定的属性和可以通知View在当前状态中的改变,通过Change Notification事件,的命令。View和View Model之间通常是一种一对一的关系。
通常来说,View是由Control或者UserControl类派生的。当然,在一些情况下,View也会由数据模板来表现,也就是用来指定显示一个对象所用的UI元素。使用数据模版,可视化设计器可以简单的定义一个View Model是如何渲染,或者如何在不改变它的底层类的情况下改变其默认显示形式,或者用以显示的控件的行为。
数据模版可以被看成不包含任何后置代码的视图。它们被设计用来绑定特定的在任何时候可能被显示View Model类型。在运行时,被视图模型所定义的View将自动实例化,并且将它的数据上下文设置为于之相配的View Model。
在WPF中,可以在应用程序级关联数据模版和View Model。WPF会自动在指定View Model对象被UI显示的时候应用数据模版。这被称为隐含数据模版(Implicit data templating)。在Silverlight中,你必需明确指定数据模版中用以显示View Model对象所使用的控件。无论在何种情况下,数据模版都可以与它所使用的控件定义一起以内联的方式在视图定义,或者以资源字典的形式定义在母视图之外,与视图的资源字典整合在一起。
简而言之,View有以下关键特征:
1、View是一个可视化元素,就像Window,Page,用户控件,或者数据模版。View定义了视图中包含的控件和它们的可视化层次以及样式。
2、View通过DataContext属性引用了View Model。View中的控件绑定了View Model暴露出的属性和命令。
3、View会自定义View和View Model之间的数据绑定行为。举例而言,View可能使用Value Converter来格式化显示到UI中的数据,或者可能使用验证规则来保证用户输入数据的验证。
4、View定义并且提交UI可视化行为,比如动画或者可能由用户与UI交互,View Model中的状态改变而引起的转换。
5、View的后置代码可能定义UI逻辑以实现XAML难以实现或者实现效率低下,或者需要直接引用View中的控件的可视化行为。
5.1.2 View Model类
MVVM模式中的View Model封装了View的表现逻辑和数据。它不包含视图的直接引用也不包含任何View的特定实现或者类型。View Model实现了View可以用来数据绑定的属性和通过Change Notification事件通知视图发生状态改变的命令。View Model提供的属性和命令定义了UI需要使用的功能,但视图仅仅关系这些功能是如何被渲染的。
View Model负责用以协调视图与Model之间所可能存在的交互。通常来说,View Model和View之间是一个一对多的关系。View Model也可以选择直接暴露Model类,这样View之中的控件就可以直接将与之绑定。在这种情况下,Model类就需要设计成为支持数据绑定和支持改变通知事件。关于这种场景的更多信息,请参考本章的数据绑定一节。
View Model也可以转换或者操作Model数据以使其更容易被View使用。View Model可以定义View所需要使用的额外属性;这些属性通常是不加到(或者说是不能加到)Model中去的。举例而言,View Model将一个值绑定到两个字段中以使它更容易被View所显示,或者在有输入字符最大值时,计算文本框中还能输入多少字符。View Model也会实现数据验证以保证数据一致性。
View Model也定义了一些UI状态切换所使用的逻辑状态。View定义了层或者样式切换以反应View Model的不同状态。比如,View Model定义了一个指示数据已经异步提交到Web Service上的状态。在这个状态时,View可以提供一个动画反馈给用户。
通常来说,View也会定义UI上用户可以操作的命令或者行为。常用的例子,比如说,当View Model提供一个Submit(泽注:提交)命令让用户提交数据到Web Service或者到数据仓库。View就可以选择将该命令以一个按钮的形式显示,这样用户可以点击此按钮以提交数据。通常来说,当命令不可用时,它所关联的UI表现将禁用。命令提供了一种封装用户行为并且将行为与其在UI中的表现形式明确分离的手段。
简而言之,View Model有以下关键特征:
1、View Model是一个不可视的类,也不由Silverlight或者WPF提供的任何基类所派生。它提供了应用程序中user case(用例)或者user task所必要的表现逻辑。View Model与View和Model是分开测试的。
2、View Model通常不会直接引用View。它实现了View需要进行数据绑定的属性和命令。通过INotifyPropertyChanged和INotifyCollectionChanged接口,View Model通知View它所发生的一切状态转变。
3、View Model会协调View与Model的交互。它会改变或者操作数据以使数据更容易在View上显示,也可能会添加一些在Model在不表达的数据。它也能通过IDataErrorInfo或者INotifyDataErrorInfo接口进行数据校验。
4、View Model可以定义View显示给用户的逻辑状态。
【注意】:
View还是View Model?
一些时候,某些功能到底应该在哪里实现是很不明显的。凭经验来说:任何关注于UI显示或者屏幕上视觉效果,在之后需要调整样式(即使现在没有这个打算)都应该归到View中;任何重点在处理应用程序逻辑行为的内容都应该到View Model中。另外,因为View Model应该不包含任何有关于View中具体UI元素的信息,所以编写有关于操作View中UI元素的代码都应该置于View的后置代码或者封装到行为中。同理,任何有关于操作被绑定到View中的数据项的代码应该被放置到View Model。
举例而言,高亮列表框中被选中的数据项应该放到View中,但是列表中显示的数据,以及被选中数据的引用,都应该在View Model中被定义。
5.1.3 Model类
在MVVM模式中的Model封装了数据和业务逻辑。业务逻辑定义了所有检索和管理应用程序数据的应用程序逻辑,和确保应用数据一致性和校验规则的应用程序逻辑。
Model通常表示了应用程序的客户端域模型。它定义了基于应用程序数据模型的数据结构以支持业务逻辑和验证逻辑。Model也可能包含了一些数据存取和缓冲,虽然一般来说会有一个独立的数据仓库或者服务来支持它。通常,Model和数据接入层一部分是由数据接入或者服务的策略所生成的,比如ADO.NET Entity Framework,WCF Data Service,或者WCF RIA Service。
通常,Model提供了使之更容易被绑定到View的功能。这通常意味着它支持属性或者集合改变通知事件,比如通过INotifyPropertyChanged和INotifyCollectionChanged接口。Model中表现对象集合的类通常继承于实现了INotifyCollectionChanged 接口的ObservableCollection
Model也支持通过IDataErrorInfo (或者INotifyDataErrorInfo)的数据验证或者数据错误报告。这些接口使WPF和Silverlight的数据绑定服务可以在数据改变的时候即使更新到UI上。也可以保证UI层上的数据校验和错误报告。
【注意】:
如果Model不实现必要的接口怎么办?
一些情况下,你需要使用一些不实现INotifyPropertyChanged,INotifyCollectionChanged,IDataErrorInfo,或者INotifyDataErrorInfo的模型对象。在这些情况下,View Model就应该封装这些模型对象并且将需要使用的属性暴露给View。这些属性的值由数据模型直接提供,View Model实现必要的接口并且暴露它,以使其更容易被View绑定。
Model有以下特征:
1、Model是封装了应用程序数据和业务逻辑的不可视类。它负责维护应用程序逻辑,并且通过本身封装的验证逻辑和业务规则保证数据一致性。
2、Model类不直接引用View或者View Model,并且也不依赖于它们的实现。
3、Model类通常提供了属性或者集合的改变事件,即INotifyPropertyChanged和INotifyCollectionChanged接口。它们可以使View的数据绑定更容易进行。Model中表示对象的集合的类通常由ObservableCollection
4、通过IDataErrorInfo 或者INotifyDataErrorInfo接口,Model类提供数据校验和出错报告的服务。
5、Model类通常和数据接入或缓冲服务一起使用。