1.MVVM设计模式简介
MVVM的设计模式最早于2005年由微软的WPF和Silverlight架构师John Gossman在他的博客中提到。以下是这篇文章的链接:
http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx
MVVM设计模式基于MVC这种将UI和逻辑分离的结构思想。传统的.NET平台下软件开发如ASP.NET和WPF/Silverlight大多数是基于CodeBehind这样的方式,我们往往将所有的代码全部写在后台代码文件中,例如UI操作,业务逻辑操作,IO,数据服务的调用等等。这虽然表面上有利于“开发效率”,实际上项目结构不清晰,各个模块之间紧密耦合,不利于扩展,不利于测试。
MV-X的思想,为.NET平台下的架构提供一种很好的实践。使我们可以构建更利于扩展,结构清晰,职责分明,易测试的软件项目。
但是目前MVVM模式还没有一个标准的实践,微软也还没有给出相对标准的方案。目前社区讨论的主要是MVVM的思想。在实际开发过程中形成了几种不同的风格。其中以Josh Smith的文章影响比较大:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
本篇提到的也主要是参考John Smith的思想。
2.采用MVVM设计模式的好处
在Silverlight或者WPF中采用MVVM的架构可以获得以下好处:
1. 项目可测试更高,从而可以执行单元测试
2. 将UI和业务的设计完全分开,View和UnitTest只是viewmodel的两个不同形式的消费者
3. 有助于我们区别并哪些是UI操作,哪些是业务操作,而不是将他们全混在CodeBehind中
3.项目结构介绍
以下是示例项目的结构截图:
以下是各模块之间的联系:
4.WpfMVVMSample.Foundation
提供一些基础类定义。
5.Model的职责
Model不包含数据的调用,但是可以包含简单的非数据调用的操作,如产生序列号或者合并字段。
对于WCF产生的客户端代理类,Models中应有与之相对应的类结构定义。
Model不依赖于任何项目。
6.IService和Services以及ServiceTest
IService是所有网络数据服务或者IO操作的服务接口。
IService中的数据访问方式以异步为主,见参考示例。
Service是真实的数据服务访问类,是IService的实现。ServiceTest是用于测试viewmodel的IService的实现
7.viewmodel的职责
viewmodel是MVVM架构中最重要的部分,viewmodel中包含属性,命令,方法,事件,属性验证等逻辑。为了与View以及Model更好的交互来满足MVVM架构,viewmodel的设计需要注意一些事项或者约束:
viewmodel的属性:viewmodel的属性是View数据的来源。这些属性可由三部分组成:
一部分是Model的复制属性。
另一部分用于控制UI状态。例如一个弹出窗口的控件可能有一个IsClose的属性,当操作完成时可以通过这个属性更改通知View做相应的UI变换或者后面提到的事件通知。
第三部分是一些方法的参数,可以将这些方法的参数设置成相应的属性绑定到View中的某个控件,然后在执行方法的时候获取这些属性,所以一般方法不含参数。
viewmodel的命令:viewmodel中的命令用于接受View的用户输入,并做相应的处理。我们也可以通过方法实现相同的功能。
viewmodel的事件: viewmodel中的事件主要用来通知View做相应的UI变换。它一般在一个处理完成之后触发,随后需要View做出相应的非业务的操作。所以一般viewmodel中的事件的订阅者只是View,除非其他自定义的非View类之间的交互。
viewmodel的方法:有些事件是没有直接提供命令调用的,如自定义的事件。这时候我们可以通过CallMethodAction来调用viewmodel中的方法来完成相应的操作。
8.View及Codebehind
View中使用Command:View中的Button等控件可以直接绑定Command属性调用viewmodel中的Command
View中使用CallMethodAction :一些不支持Command的控件,可以用一个CallMethodAction触发器来执行viewmodel中的方法。注意的是方法当中往往包含一些参数,这些参数一般可以通过给viewmodel设置相应的属性来绑定到相关的输入控件,如TextBox。
View中使用DataTrigger:除了模型属性,还有一部分是状态属性,这往往是viewmodel通过属性更改的方式通知View做出相关的UI操作,例如触发一段动画,或者切换控件状态等等。这个时候可以使用一些触发器,当状态值不同时做出相应的UI变换。
View的CodeBehind中初始化子View的viewmodel上下文:View一般由父View调用,所以View的viewmodel一般由父View来初始化。比如当点击人脉按钮,需要显示人脉的View的时候,就由主框架初始化人脉的viewmodel,并显示人脉的View。
View的CodeBehind中订阅子View的UI事件:除了通过状态属性的变更触发View中的触发器,看一种选择是在View的CodeBehind中订阅viewmodel的UI事件。
9.View及viewmodel交互模式总结
由以上解析我们可以总结出View和viewmodel的交互模式:
1. 父View在CodeBehind中初始化子viewmodel
2. 父View在CodeBehind中订阅子viewmodel的UI事件
3. 父View将子viewmodel赋值给子View的DataContext,并显示子View
4. 父View调用子viewmodel获取数据的方法,子viewmodel调用数据服务获取数据
5. viewmodel的数据通过Binding传递给View
6. View接受用户输入,并通过Command或者CallMethodAction交给viewmodel做业务处理
7. viewmodel处理完成之后触发UI事件或者更改状态属性通知父View
8. 父View做出View变换至新的界面
10. 依赖注入
viewmodel的职责是提供数据给View,并调用底层的数据服务。
为了解除viewmodel和BP的耦合,增加一层IService的接口定义,这也使得我们可以构造不同的IService实现来测试viewmodel。
但是View 在使用viewmodel的时候,viewmodel必须要使用IService对象,所以这里采用依赖注入。通过依赖注入来完全解除View以及viewmodel对于Service的依赖。
示例项目源代码下载:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。