每个Silverlight程序员都有一段痛苦的MVVM经历,MVVM是Silverlight开发永恒的话题。
完美的概念后面是没有标准框架,网络上所能找到的例子也并没有深入介绍,使用过程中一定会接触深层次的问题,并且各个开源框架也是实现方式各异,叫我们Silverlight程序员情何以堪?
网络上一搜MVVM关键字,最常出现的就是MSDN上的一篇:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
但实际这篇只讲述一般应用场景,实际在使用中还有很多问题需要自己解决。
MVVM的概念最早由Josh Smith于2005年提出:http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx
网络上比较完善比较经典的可能是唯一比较全面的教程也是他那本只有54页的《Advanced MVVM》,这本Google上能找到PDF链接。
不过,经仔细阅读和分析之后发现,这个例子仍是讲述一般场景,真正开发仍然还有很多问题留给开发者自己解决。
我的安排是首先我们来总结一下到底哪些问题令我们痛苦,然后分析MVVM的起因,最后我们谈谈怎样消除这些痛苦。
1.MVVM到底有哪些令人痛苦的问题?
我这里总结一下,你也可以一起思考,自己有没有遇到过这些问题,或者正在思考这些问题。
- 设计时和运行时支持?XAML需要设计时支持,很多MVVM框架直接将ViewModel定义在Xaml资源中,这不是View引用了ViewModel吗?
- 如果View不能引用ViewModel,那么View怎样取得ViewModel作为上下文?有很多框架都是直接在CodeBehind中加载ViewModel对象?
- ViewModel的定义好像很乱,标准的ViewModel应该怎样定义?
- ViewModel应该怎样加载数据?
- 在导航的时候,怎样赋予另一个页面一个ViewModel上下文?有View来指定ViewModel实例还是另外的方式
- ViewModel的构造函数应不应该有参数?
- 一个方法有多个参数,View应该怎么样传递这些参数?
- View的CodeBehind中应不应该有代码?
- 很多时候,ViewModel中的一个方法都要对应View中的一个UI操作,或则切换状态,或者修改属性,ViewModel怎样触发UI变化?
- ViewModel怎样调用需要在CodeBehind中执行的方法,比如有些控件的操作不能通过属性修改,而只能在CodeBehind中调用这个控件的某个方法,比如弹出一个ChildWindows,或者MessageBox?比如VisualStateManager.GotoState就只能在CodeBehind中执行?
- 上述问题:ViewModel怎样切换状态?
- 多线程?
- Windows Phone 7上的ViewModel怎样切换多个Application bar?
- Windows Phone 7中ViewModel如何处理回退和导航?
看看吧,你有没有遇到活思考过这些问题?
2.MVVM的由来
其实,这些大多数问题都是有由来的,我们来分析一下。
大多数知道MVVM的人,都可能知道它和MVC有些联系?然而是什么联系和区别?为什么一般人很容易理解和使用ASP.NET MVC,却始终不会应用MVVM?可能都说不清楚。
事实上,拿MVC和MVVM比较并没有多大意义,反而会使人糊涂。在ASP.NET MVC中的View只承担显示数据的作用,它和Controller的交互是通过用户重新发起一起请求,这个请求和View几乎没有关系。并且MVC中得HTML是解释性的,服务器端按View的定义,顺序解释,替换掉相应的数据即可。
然而,Xaml却不是那么简单的,Xaml不仅定义了UI(渲染引擎按照Xaml语言的定义计算和排列元素),Xaml更是一棵对象树,Xaml对象树的概念十分重要,以至于我们应该时时记住Xaml中的每个节点都是一个对象,这些对象由Xaml解析器实例化,并由Silverlight的UI渲染引擎根据布局原理将UI元素绘制到屏幕的相应位置。
可以看到,这和ASP.NET MVC的机制是完全不一样的,虽然都是基于控件事件模型,但是ASP.NET中对象数实例化和显示是分开的,而Silverlight的UI元素对象树就是实际显示在屏幕的UI元素,它们是一个东西,因为Silverlight中这些UI元素和程序的交互和联系就十分重要和强大。
在Silverlight中,可以说最重要的是引入Xaml编程模型,使我们很好和很方便的定义UI元素和对象树,加上它的Xaml解析器,顺序的解析Xaml中的一个一个对象,Xaml解析器通过调用每个节点对应的类型的够构造函数来构造对象树,而Xaml能够识别每个节点的类型,能够识别像INotifyChanged这样的类型,所以Silverlight加入依赖属性,绑定这样一些概念。这都跟Xaml特点是有关的。Xaml是一种强大的编程模型,据悉Xaml团队已经加入Windows 8 Team,所以可以预见Windows 8的应用程序跟Xaml有很大的关系。
因此,MVVM基本上是Xaml这种编程模型催生出来的。它和MVC除了分离的思想外没有什么可比的意义。
3.MVVM痛苦的根节点
然而,不管怎么样,Silverlight终究还是基于事件模型,事件的编程模型是一种极简的编程模式,它让开发者很容易的处理用户的输入(文本或者事件)。不管是Windows Form,Web Form还是Silverlight,都是基于这种编程模型。
可是事件编程模型有一个很大的缺点,开发者很容易将业务逻辑混在UI操作中,这经常被称为Codebehind的文件。这样的后果是容易使开发者将注意力放到一些表现层的细节上,而且,很容易将业务逻辑与一些表现的细节耦合在一起,更严重的是,由于大多数控件对象的不可构造,对程序的可测试性带来很大挑战。它也使美工和开发的分工很不明确。
因此,早期有分层的思想,分层实际上就是将某一抽象级别的业务封装。
而在分层的接近UI层面,也出现分离的思想,它企图将UI彻底分离出来。MVC即是一种方案,可惜的是VS对ASP.BET MVC的设计时支持不够好。
而在Silverlight开发中,Blend能支持很好的设计时。
可是问题是,Xaml并不是为MVVM设计的,它的核心思想任然是控件和事件。所以MVVM试图将View和ViewModel分开,它需要将用户事件转化为ViewModel的方法,它需要在ViewModel中驱动一个UI变换,而View有各种各样的事件和控件,View也有各种各样的变换。因此原本在Codebehind中轻松搞定的事情,在MVVM模式下需要做大量的事情。我承认Codebehind的方式让业务有点混乱,但是MVVM实在是很麻烦,有时候你会觉得MVVM实际上把一件事情搞复杂了。
5.MVVM痛苦级别
MVVM的痛苦级别,取决于你想让Codebehind有多干净,这也有需求方面的考量。
1. 如果你仅仅是为了易于测试,业务清晰,这很简单,你只要注意把业务尽量移到ViewModel即可,View引用ViewModel都无所谓,在Codebehind中调用ViewModel中方法也无所谓,这实际上跟一般的分层没多大区别。
2. 如果View和ViewModel都是你在设计,不需要View和ViewModel严格分离,你可以大部分使用各个MVVM框架的标准做法,但是,你知道的,每个MVVM框架都有很难完全分离的方面,这个时候可以在CodeBehind中处理,因为转换到ViewModel中需要增加很大的工作量和理解上的复杂度。这也是我推荐的,或者说这是一个折中。
3. 如果你想完全将View交给另一个你甚至不认识的家伙去设计。你将挑战最难的MVVM设计--将事件模型完美转化为MVVM模型,你将不得不去解决所有问题,包括我上面列举出的一些问题。这样应该很少。
6.MVVM核心技术和思想
1.附加属性和Binding
附加属性是WPF提出的一个全新的属性系统,它用来支持绑定,变更通知,以及Xaml的样式Style系统,在MVVM中主要是View和ViewModel之间的数据绑定和传输。
2.附加属性
不可否认,Xaml编程模型,最强大和最重要的概念是附加属性。
Blend中最强大的Behavior,Action,Trigger,VisualState等等,绝大部分特性都和附加属性有关。要使View和ViewModel完全分开,必须有一层抽象层作为两者之间的桥梁。
Xaml对于附加属性的支持,使得这个中间层(附加属性所属的类)可以得到一个被附加的对象的引用,这个中间层拿到引用之后就可以做一些翻译,比如一个ButtonCommand就是一个中间层,它拿到一个Button对象之后,订阅这个Button的Click事件到一个方法,并让CanExecute方法执行时,去修改Button的IsEnable属性。
还有比如你要想在ViewModel控制PhoneApplicationPage的导航和回退,Application Bar的切换等,都可以通过类似的思路来实现。
总之,你记住附加属性是Silverlight最重要的知识点。
3.上诉情景有时候也可以用扩展方法来代替,这个扩展方法就充当中间层,但是这种情况下要假设这个中间层能够根据某种规则找到ViewModel对象,并且需要在Codebehind中调用扩展方法。不过这种方式有时候确实有一定的好处。
总之,掌握上述两点你能应付大部分场景,并且在遇到问题之后可以想办法去解决,核心思想就是一个中间层,它要么是利用附加属性,要么是利用扩展方法。
7.MVVM技术分享
9月17号,我们将在微软亚太研发集团举办Windows Phone 7的技术沙龙,将会详细分享MVVM的知识和经验。
详情见这里:http://event.weibo.com/199979
我们官方宣传页:http://www.techappy.net/