【翻译】使用MVVM模式构建WPF应用程序介绍

原文:Introduction to Model/View/ViewModel pattern for building WPF apps

http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx

作者:John Gossman(http://blogs.msdn.com/b/johngossman)

译者:winter(http://winter-cn.cnblogs.com)

 

MVVM模式是MVC的一个变体,它主要为那些设计人员比传统开发人员需要为视图承担更多责任的现代UI开发平台量身定制。总的来说,设计人员是那种更关注图形和美感,并且不会像传统程序员那样写那么多程序的开发者。设计多数情况会用像HTML或者XAML这样声明式语言给出,而且很多时候都是用Dreamweaver, Flash或则Sparkle这样的所见即所得工具生成出来的。一言以蔽之,就是应用程序的UI部分跟后台数据和业务逻辑部分是使用的是不一样的工具,不一样的语言,并且由不同的人完成的。MVC原本的设计针对的是SmallTalk这样的整个程序用同一种环境和语言构建的系统,Model/View/ViewModel是一个对MVC的改进,用以适应众所周知的Web环境以及现在的Avalon开发(译注:Avalon是WPF的codename,这篇blog发布较早,还没有确定WPF这个名称)。

 

Model/View/ViewModel还依赖一件事情:一个整体的数据绑定机制。稍后详述。

Model的如MVC中所定义,就是数据或者业务逻辑,完完全全与UI无关,它存储了状态并且做问题领域中的处理。Model可以写在代码里面或者用写在关系表或者XML中的纯数据来表示。

Model/View/ViewModel中的View表示可见元素,按钮,窗体,图形或者GUI中更复杂的控件,它会对快捷键进行编码,并且控件自身会管理跟输入设备的交互——这在MVC中本该是Controller负责的(现代GUI环境中发生在Controller上的事情是很长的题外话……我倾向于认为它只是隐藏到后台了,它仍然存在,但是我们不需要像是1979年那样考虑那么多事情了)。View几乎总是以声明的方式去定义的,而且通常是某种工具生成的。因为这些工具和声明式语言的天然特性,MVC编码进它的View类里面的某些视图状态不是很容易表示。例如,UI可能有多个交互的模式如"显示状态"和"编辑状态",这些模式会改变控件的行为或者可见的外观,但是这些模式并不总是能够用XAML来表达的(尽管触发器是个不错的起点)。我们稍后会解决这个问题。

这个时候就需要数据绑定登场了。在简单得例子中,View被直接数据绑定到到Model。Model的一些部分只是简单地通过单向绑定显示到view当中。Model的另一些部分可以用双向绑定数据的控件编辑。例如,一个Model中的布尔值可以被数据绑定到一个CheckBox,或者TextBox的字符串字段。

然而在实践中,仅仅非常少数的程序UI可以直接数据绑定到Model,特别是当Model是个已经存在的类或者数据格式,应用程序开发者还没法控制它的时候。UI可能希望做必须在代码中才能完成的复杂操作,放在我们严格意义上的View的中不合理,而放在包含在Model中又显得过度针对具体问题(也可能根本没法弄进已经存在的Model)。最终我们需要一个地方放视图状态,像选择和模式这些。

ViewModel就是专门用于处理这些任务的。这个名词表示"视图的模型",并且可以看做视图的抽象,但是还提供了Model专用于给View做数据绑定的特定形态。在后一种角色上,ViewModel包含了能够将Model类型转换到View类型的数据转换器,而且它包含了View可以用以跟模型交互的命令。

我将会拓展这些思路,并且在接下来的文章中重点描述如何绑定视图到ViewModel的命令。但是理清这个模式最快捷的方式是提供一些实例:

上面的图片展示了Sparkle UI的三个编辑面板。每个都是用Model/View/ViewModel模式开发的。最简单的是最上的Library面板。Model是一个程序集的列表(每个都是System.Reflection.Assembly的实例),而每个程序集对应的是一个控件的列表。View是我们我们的面板控件以及一系列的Style和DataTemplate,它们把程序集列表显示到一个ComboBox中,把控件列表显示在一个ListBox中。我们把ComboBox的标题直接数据绑定到程序集对象的name,让ListBox中的列表项从Control的name中取出它们要的文本。ViewModel有像是当前选中的程序集并且暴露出向场景中插入一个控件的命令。Selection是ViewModel中几乎最常用的组件,你可能会觉得奇怪为什么selection没有放在View中。这样做的原因是很多view中的控件需要基于单选来协作。比起与view中所有不同的控件协作,ViewModel中很容易绑定到一个单项选择的表示。在Library面板中,被选中的程序集决定了ComboBox中什么被选中以及ListBox中显示什么样的数据。此外,设计师可以自由决定切换到用ListBox显示程序集,用ComboBox显示控件列表,而完全无须从原来的view里面复制同选择相关的逻辑。

Appearance面板则以Sparkle编辑区域中被选择的形状或者控件为它的Model。View中有一个ListBox,它用来显示当前选择中有趣的属性(至少有所有的Pen和Brush属性),还有用来决定Brush或者Pen是简单还是渐变等的按钮,以及用来编辑颜色组件的色谱。ViewModel 包含了哪个属性被选中,编辑渐变的时候哪个渐变过渡点被选中,映射颜色到文本值和色谱的数据转换器,以及用于更换正在编辑的Pen和Brush的命令。在这种情况下,Model是Avalon提供给我们的,View可以被轻易被改为完全不同的别的东西,而ViewModel则提供了UI中可复用组件的完全不同表示。

最后一个例子是我们的Project面板,这里Model是个MSBuild项目……又一次遇到了Model类是预先存在的情况。View是一个树型控件,滚动区域,并且包含内容菜单。ViewModel适配了并非为Avalon专门设计的MSBuild中的概念(且从命令行可以完美工作),这样我们可以对它们数据绑定,ViewModel中仍然包含了选择信息和命令

一旦你对Model/View/ViewModel有所了解,任何UI问题都可以以它的名词来表述。事实上,整个Sparkle UI都是用这个模式定义的。“编辑区域中被选择的形状或者控件”是Appearance面板的的Model,它自己本身又是我们Scene editor的ViewModel中的概念。Sparkle内部Panel的布局,注册的panel列表是它的Model,View则是以带有splitter的Grid来定位视图,以及ViewModel用以决定哪些panel当前可见,以及它们在哪个逻辑容器里(编辑区域,左,右,底边)。

posted @ 2012-08-08 14:22 winter-cn 阅读(...) 评论(...) 编辑 收藏