MVVM模式构建WPF_第1页
MVVM模式构建WPF_第2页
MVVM模式构建WPF_第3页
MVVM模式构建WPF_第4页
MVVM模式构建WPF_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、使用MVVM设计模式构建WPF应用程序本文是翻译大牛 Josh Smith 的文章,WPF Apps With The Model-View-ViewModel Design Pattern , 译者水平有限,如有什么问题请看原文,或者与译者讨论(非常乐意与你讨论)。本文讨论的内容:WPF与设计模式MVP模式对 WPF来说为什么 MVVM是更好的选择用MVVM 构建 WPF程序本文涉及的技术:WPF、数据绑定内容列表有序与混乱模型-视图-视图模型的演变为什么WPF开发者喜欢MVVM 演示程序中继命令逻辑ViewModel类层级结构ViewModelBase 类CommandViewModel

2、类Mai nWin dowViewModel 类View 对应 ViewModel数据模型和Repository新增客户数据表单所有客户视图总结开发UI,对一个专业软件并不容易。 它需要未知数据、 交互式设计,可视化设计、联通性, 多线程、国际化、验证、单元测试以及其他的一些东西才能完成。考虑到UI要展示开发的系统并且必须满足用户对系统风格不可预知的变更,因此它是很多应用程序最脆弱的地方。有很多的设计模式可以帮助解决UI不断变更这头难缠的野兽,但是恰当的分离和描述多个关注点可能很困难。模式越复杂,之后用到的捷径越可能破坏之前正确的努力。这并不总是设计模式的错。有时使用要写很多的代码复杂设计模式

3、,这是因为我们使用的UI平台并不适合简单是设计模式。UI平台需要做的是很容易使用简单的,久经考验的,开发者认识的设计模式构建UI。庆幸的是,WPF就是这样一个平台。随着是使用 WPF开发的比例不断升高,WPF社区发展了自己的模式与实践生态圈子。在本文,我将讨论一些设计与实现客户端应用程序的WPF最佳实践。利用 WPF和MVVM设计模式衔接的一些核心功能,我将通过一个例子介绍,用“正确”的方式构建一个WPF程序是多么的简单。data templates, comma nds, data binding, the resource system 以及 MVVM 模式怎么揉合至 U 起创建一个简单的

4、、可测试的、健壮的框架,并且任何WPF程序都能使用,到文章最后,这一切都很清晰明了。文中的例程可以作为现实中一个WPF应用程序的模版,并且使用MVVM设计模式作为其核心架构。例程解决方案中的单元测试部分,展示了测试ViewModel 类的功能是很容易的。在深入本文之前,我们首先看一下我们要使用像 MVVM 这样的设计 模式。有序与混乱没有必要在一个” Hello,World! ”的程序中使用设计模式。任何一个合格的开发者看一眼就 指导那几行代码是干什么的。 然而随着程序功能点的增加, 随之代码的数量以及移动部件也 会增多。 最终系统的复杂度以及不断出现问题, 促使开发者组织他们的代码, 以便它

5、们更容 易理解, 讨论、 扩展以及维护。 我们通过给代码中某些实体命以众所周知的名字, 减少复杂 系统认知误区。我们给函数块命名主要依据系统中的功能角色。开发者有意识的根据设计模式组织他们的代码, 而不是根据设计模式自动去组织。 无论哪一 种,都没有什么问题。但是在本文中,我说明在WPF 程序中明确使用 MVVM 模式的好处。某些类的名称,包括 MVVM 模式中著名的术语,如果类是 view 的抽象类就以 ViewModel 结束。这种方式有助于避免之前提到的认知误区。相反,你也可以让那种受控的误区存在, 这正是大部分软件开发项目的自热状态。模型 -视图-视图模型的演变 自从人们开始构建 UI

6、 时,就有很多流行的设计模式让 UI 构建更容易。 比如, MVP 模式在各 种 UI 编程平台中都非常流行。 MVP 是 MVC 模式的一种变体, MVC 模式已经流行了几十年 了。以防你之前从没用过 MVP 模式,这里做一个简单的解释。你在屏幕上看到的是 View , 它显示的数据是 Model ,Presenter 就是把两者联系起来。 View 依赖 Presenter 并通过 Presenter 展示 Model 数据,响应用户输入,提供数据验证(或许委托给 Model 去完成)以及其他的 一些任务。如果你想了解更过关于 MVP 模式,我建议你去读 Jean-Paul Boodhoo

7、 的 August 2006 Design Patterns column 。2004 年晚些时候, Martin Fowler 发表了一篇叫 Presentation Model ( PM)的模式。PM 模式 和 MVP 类似, MVP 是把一个 View 从行为和状态分离出来。 PM 中令人关注的部分是创建 view 的抽象,叫做 Presentation Model 。之后, View 就仅仅是 Presentation Model 的展示了。在 Fowler的论文中,他展示了 Presentation Model经常更新 View,以便两个彼此同步。同步逻 辑组作为代码存在于 Pres

8、entation Model 类中。2005年,John Gossman,目前是微软WPF和 Silverlight架构师,在他的博客上披露了Model-View-ViewModel (MVVM) 模式。 MVVM 和 Fowler 的 Presentation Model 是一致的,两 个模式的特征都是 View的抽象,都包含了 View的行为和状态。Fowler引入Presentation Model 是作为创建独立平台的 View 的抽象,而 Gossman 引入 MVVM 是作为标准化的方法,利用 WPF的核心特点去简化 UI的创建。从这种意义上来讲,我把MVVM作为一般PM模式的一个

9、特例。在 Glenn Block 一遍优秀的文章"Prism: Patterns for Building Composite Applications with WPF", 于2008年9月微软大会发布,他解释了 WPF微软组合程序开发向导。术语ViewModel没有用到,然而 PM 却用来描述 View 的抽象。这篇文章自始至终,都没没有出现我要将 MVVM 模式,以及View的抽象ViewModel。我发现这个术语在 WPF和Silverlight社区中比较流行。 不像 MVP 中的 Presenter, ViewModel 不需要引用 View。 View 绑定 V

10、iewModel 的属性, ViewMode 向 Viewl 暴露 Model 对象的数据以及其他的状态。 View 和 ViewModel 之间的绑定 很容易构造,因为 ViewModel 对象可以设置为 View 的 DataContext 。如果 ViewModel 中的 属性值发生改变,新值将通过绑定自动传送给View。当用户点击View中的按钮时,ViewMode对于的Comma nd将执行请求的动作。ViewModel,绝不是 View,去执行实体对象的修改。View类并不知道 Model类是否存在,同时 ViewModel和Model也不知道 View。实际上, Model 完全

11、不知道 ViewModel 和 View 存在,这是一个非常松耦合的设计,在很多方面都有 好处,这不就你就会看到。为什么 WPF 开发者喜欢 MVVM一旦开发者适应了 WPF和MVVM,就很难区别两者。因为MVVM非常适合 WPF平台,并且WPF被设计使用 MVVM模式更容易构建应用程序, MVVM就成了 WPF开发者的通用语。事实上,微软内部正在用 MVVM开发 WPF应用程序,像Microsoft Expression Blend,然而当 时WPF平台的核心功能依然在开发之中。WPF的很多方面,像控制模型以及数据模版,都利用了 MVVM 推荐的显示状态和行为分离技术。MVVM之所以成为一个

12、伟大设计模式,是因为WPF的一个最重要的特征数据绑定构造。通过把 Viewde 属性绑定到 ViewModel ,你就可以得到两者松耦合的设计,并且完全去除 ViewModel 更新 View 的那部分代码。数据绑定系统支持输入验证,并且输入验证提供了传 递错误给 View 的标准方法。另两个 WPF的特点,数据模版和资源系统让MVVM模式更加可用。数据模版把View应用在 ViewModel 对象上,以便其能够在 UI 上显示。你可以在 Xaml 中声明模版,让资源系统 在系统运行过程中自动定位并应用这些模版。你可以从我 2008 年 7月写的一篇文章 , "Data and WP

13、F: Customize Data Display with Data Binding and WPF." ,获取更多关于绑定和数据模版 的信息。要不是 WPF 对 Command 的支持, MVVM 模式就不会那么强大。本文中,我会为你展示ViewModel怎样把 Comma nds暴露给 View,并且让 View消费它的功能。如果你对 Comma nd 不是很熟悉,我推荐你读一下 2008 年 9 月 Brian Noyes 发布的文章, "Advanced WPF: Understanding Routed Events and Commands in WPF&qu

14、ot; 。除了 WPF( Silverlight2 )本身让MVVM以一种自然的方式去构建程序之外,造成MVVM模式流行还有一个原因,那就是 ViewModel 类很容易进行单元测试。从某种意义来讲, View 和单元测试只是 ViewModel 两个不同类型的消费者。拥有一套应用程序的单元测试,可以 为提供更自由、快速的回归测试,而回归测试有助于降低之后应用的维护成本。 除了促进创建自动化回归测试外, ViewModel 类的可测试性也有助于设计更容易分离的 UI。 当你设计应用时, 你可以通过想象某些东西是否要创建单元测试消费 ViewModel ,来确定它 们是放到 View 里面还是

15、ViewModel 里面。 如果你可以为 ViewModel 写单元测试而不用创建 任何 UI 控件,你也可以把 ViewModel 剥离出来,因为它不依赖任何具体可视化的组件。 最后,对于要和设计者合作的开发者来说,使用 MVVM 模式使得创建平滑的开发 /设计工作 流更加容易。既然 View 可以是 ViewModel 的任意一个消费者,就很容易去掉一个 View 通 过新增一个 View 去渲染 ViewModel 。这个简单的步骤允许设计师构建快速原型以及评估 UI 设计。这样开发团队可以关注创建健壮的 ViewModel 类,而设计团队可以关注设计界面友好的 View。 要融合两个团

16、队输出只需要在 View 的 xaml 上进行正确的绑定即可。演示程序到此为止,我们回顾了MVVM的历史以及具体操作理论。我也说明了它在WPF开发者中间如此流行的原因。现在是时候继续我们的步伐,看一下MVVM 模式在实际中的应用。这篇文章中的演示程序以各种方式使用 MVVM 设计模式,它提供了丰富的例子,帮助在上下文 中理解 MVVM 的概念。我用 VS2008 SP1 创建的这个演示程序,框架是 Microsoft .NET Framework 3.5 SP1。单元测试是用的 Visual Studio unit testing。应用可以包含任意数量的“Workspace”,每一个都可以由用

17、户点击左侧导航区的命令链接打开。所有的 Workspace 寄宿在主区域 TabControl 中,用户可以通过点击 workspace 的 tab item 上关闭按钮关闭 workspace。应用程序有两个可用的workspace : "All Customers"和"NewCustomer"。运行程序,打开一些workspace, UI看起来如图1所示。勺 MWMSfflwApU ® 口jMl lustrumAll Cwstwnrri 遇J£us(nr«rNjnn*T嵌訓* !rrunj 敏 on)o网一 Qi(8.8

18、33.163An©canTx>so .ram(12.81,73Hdrun CornetalexQ) contaw. com3*7 3,7M.02Knlich E»s«nlllipQCOntMOUQWII1 PeopleGreggregi 低 ontwsommIrmCTaFtQf>9CvntQ>SQbCOrTiHinkson. GrJinthmkson cxxntow 工 omMcCorta txnuedem seco<it »o .com拍.址氧ASjonlji "0 亡 Xi c4o .c omTotdt 昶leciM

19、 tares; i2.e?X2&图 1 Workspaces一次只有一个“ All Customers" Workspace的实例可以打开,但是可以打开多个New CustomerWorkspace。当用户决定创建一个新的客户时,她必须填完图2所示的数据输入表单。All 亡ustornerE | New Custon>er ,x | Kew Custcm-er J£j|Customer type: (Not Spetfi&d)CustOfD&r fype must set&ctMIFirst rarne;F*ret name is mi

20、ssingILast narprLast name rs m<55?ryIE-rnan:E rnffFi missingI图2新客户数据输入表单填完数据输入表单的所有有效值点击“ Save”按钮,新客户的名称将会出现在tab item上面,同时新客户也会增加到客户列表中。应用程序不支持删除或者编辑客户,但是这和其它功能类似,很容易在已有的程序架构上去实现。现在你已经对演示程序有了更深层次的理解了, 接下来我们研究它是如何设计以及实现的。中继命令逻辑(Relayi ng Comma nd Logic)除了类构造器里调用初始化组件标准的样板代码,应用中的每一 View的codebehind文

21、件都是空的。实际上你可以移除View的codebehind文件,程序让人能够争正确的编译和运行。尽管View中没有事件处理方法,但是当用户点击按钮时,程序依然能够响应并满足用户的请求。之所以这样,是因为UI上Hyperli nk、 Button以及Me nultem 控件的Comma nd属性被绑定了。绑定机制确保当用户在控件上点击时,由ViewModel暴露的ICommand对象能够执行。你可以把 comma nd对象看作一个适配器,这个适配器让comma nd对象很容易消费在View中声明的 ViewModel功能。当 ViewModel 暴露 ICommad 类型的实例属性,被暴露的 C

22、ommand 对象使用 ViewModel 中 的对象去完成它的工作。 其中一个可能的实现模式是在 ViewModel 内创建一个私有嵌套类, 以便 command 能够访问包含在 ViewModel 中的私有成员,而不至于污染命名空间。嵌套类 实现了 ICommand 接口,包含在 ViewModel 中对象的引用注入到其构造器中。但是为 ViewModel 暴露的每个 Command 创建实现 ICommad 的嵌套类,会增加 ViewModel 类的大 小。更多的代码意味着存在 BUGS 潜力更大。在演示程序中, RelayCommand 类解决了这个问题。 RelayCommand 允

23、许通过把委托传给其 构造器,以实现对命令逻辑的注入。这种方式允许在 ViewMode 类中可以简单明了的实现 Command。RelayCommand 是 DelegateCommand 的一个简单的变体, DelegateCommand 可以在 Microsoft Composite Application Library 找到。 RelayCommand 类代码如图 3 所示。图 3 RelayCommand 类public class RelayCommand : ICommand#region Fieldsreadonly Action<object> _execute;r

24、eadonly Predicate<object> _canExecute;#endregion / Fields#region Constructorspublic RelayCommand(Action<object> execute): this(execute, null)public RelayCommand(Action<object> execute, Predicate<object> canExecute)if (execute = null)throw new ArgumentNullException("execu

25、te");_execute = execute;_canExecute = canExecute;#endregion / Constructors#region ICommand MembersDebuggerStepThroughpublic bool CanExecute(object parameter)return _canExecute = null ? true : _canExecute(parameter);public event EventHandler CanExecuteChangedadd CommandManager.RequerySuggested +

26、= value; remove CommandManager.RequerySuggested -= value; public void Execute(object parameter)_execute(parameter);#endregion / ICommand Members作为接口 ICommad 实现一部分,事件 CanExecuteChanged 有一些值得关注的特征。它委托 订阅 CommandManager. RequerySuggested 事件。这样以确保无论何时调用内置命令时, WPF 命令架构都能调用所有能够执行的 RelayCommand 对象。RelayCom

27、mand _saveCommand;public ICommand SaveCommandgetif (_saveCommand = null)_saveCommand = new RelayCommand(param => this.Save(),param => this.CanSave );return _saveCommand;ViewModel 类层级图大部分 ViewModel 类有共同的特征,他们要实现 INotifyPropertyChanged 接口,需要显示一 个友好的名字,以之前说道 Workspace 为例,它需要能够关闭(即从 UI 上移除)。要解决这 个

28、问题,自然就需要创建一个或二个 ViewModel 基类,以便新的 ViewModel 类能够从基类 集成通用的功能。所有的 ViewModel 类形成如图 4 的层级图。CwrtiimVirModcl.3:<尿口ft9 i »沱入AUC ut>o«n> uV Jim M odf IDass< WorkipsL-e- / 甘吕 hBeflMaanW indiowV Bew M ocfe-lClass专 Wo rhjspac # i ebd#IOi>pGMb4er i PraptfrtittJi幽r 軍lidPrpcr聊* Method!.:*

29、OnDrapcSeQnP斗 Vsr時PrpfKrfyNHRtk Eveb/ PrcpenyClhtng«lLCDmmdrbdTVirw Model Hittiftractcms * YwWtoiJrBflK-Picpeues* F紹商亍 Conrrind M«h仙Comrrk&ndView McsdelL_J"Properties.呵少 OnReQu«tOciie 丹0>rkip尿凹Events華 RetpuntOose图4继承层级图 为你的ViewModel创建一个基类并不是必须。如果你喜欢在类中通过组合几个小一点的类 以获得那些功能,而

30、不是用继承的方式,这并没有什么问题。就像任何其他的设计模式一样, MVVM是一套指导方针,而不是规则。ViewModelBase 类ViewModelBase是层级中的根类,这就是它要实现通用INotifyPropertyChanged接口以及有一个 DisplayName 属性的原因。INotifyPropertyChanged 接口包含一个叫 PropertyChanged 的 事件。无论何时 ViewModel对象的属性的发生改变时,它都会触发PropertyChanged事件,把新值通知给 WPF绑定系统。根据通知,绑定系统检索属性,UI组件上绑定的属性将接受新值。为了让WPF知道是那

31、一个属性发生了改变,PropertyChangedEventArgs类暴露了一个 string类型的属性 PropertyName。你一定要为事件参数传递正确的属性名,否则WPF将会为新值检索出一个错误的属性。ViewModelBase 一个值得关注的地方就是它为给定的属性名提供了验证,验证属性是否存在ViewModel对象上。重构时,这非常有用。因为通过VS 2008重构功能去改变属性名,不会更新源代码中字符串,而这些字符串正好包含属性名(其实不应该包含)。在事件参数中传 很难追踪, 因此这个细微的特征将会节省大量的时间。 ViewModelBase 中增加了这个有用的 特征,其代码如下:图

32、 5 属性验证/ In ViewModelBase.cspublic event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName)this.VerifyPropertyName(propertyName);PropertyChangedEventHandler handler = this.PropertyChanged;if (handler != null)var e = new PropertyChangedEventArgs(

33、propertyName);handler(this, e);Conditional("DEBUG")DebuggerStepThroughpublic void VerifyPropertyName(string propertyName)/ Verify that the property name matches a real,/ public, instance property on this object.if (TypeDescriptor.GetProperties(this)propertyName = null)string msg = "In

34、valid property name: " + propertyName;if (this.ThrowOnInvalidPropertyName)throw new Exception(msg);elseDebug.Fail(msg);CommandViewModel 类CommandViewModel 是最简单的 ViewModelBase 子类,它暴露了一个类型为 ICommad 的 Command 属性。 MainWindowViewModel 通过 Commands 属性暴露了 CommandViewModel 对象的一个集合。主窗口左手侧的导航区域,显示了MainWind

35、owViewModel 暴露每个CommandViewModel 对象链接,像"View all customers ”和"Create new customer ”。当用户 点击链接,将会执行相应的 Command ,在主窗口的 TabControl 中打开一个 workspace 。 CommandViewModel 类的定义如下所示:public class CommandViewModel : ViewModelBasepublic CommandViewModel(string displayName, ICommand command)if (command =

36、 null)throw new ArgumentNullException("command");base.DisplayName = displayName;this.Command = command;public ICommand Command get; private set; 在 MainWindowResources.xaml 文件中存在一个 key 为 CommandsTemplate 的数据模版, 主窗 口( MainWindow )使用这个模版渲染之前提到的 CommandViewModel 对象集合。这个模版 是简单在 ItemsControl 里把每

37、个 CommandViewModel 对象渲染成一个链接,每个链接的 Command 属性绑定到 CommandViewModel 对象的 Command 属性。数据模版 Xaml 如图 6 所示:图6渲染Comma nd列表<!- In MainWindowResources.xaml -><!-This template explains how to render the list of commands onthe left side in the main window (the 'Control Panel' area).-><Data

38、Template x:Key="CommandsTemplate"><ItemsControl ItemsSource="Binding Path=Commands"><ItemsControl.ItemTemplate><DataTemplate><TextBlock Margin="2,6"><Hyperlink Command="Binding Path=Command"><TextBlock Text="Binding Pat

39、h=DisplayName" /></Hyperlink></TextBlock></DataTemplate></ItemsControl.ItemTemplate></ItemsControl></DataTemplate>MainWindowViewModel 类 如前面看到的类图一样, WorkspaceViewModel 类继承于 ViewModelBase 并增加了“关闭” 的能力。这个“关闭” ,我的意思是在运行的时候能把 workspace 从 UI 上移除。有三个类继 承于 Workspa

40、ceViewModel ,他们分别为 MainWindowViewModel , AllCustomersViewModel 和 CustomerViewModel 。 MainWindowViewModel 的关闭请求是由 App 类处理的,其中 App 类创建了 MainWindow 以及它对应的 ViewModel 对象。创建代码如图7 所示 .图 7 创建 ViewModel/ In App.xaml.csprotected override void OnStartup(StartupEventArgs e)base.OnStartup(e);MainWindow window =

41、new MainWindow();/ Create the ViewModel to which/ the main window binds.string path = "Data/customers.xml"var viewModel = new MainWindowViewModel(path);/ When the ViewModel asks to be closed,/ close the window.viewModel.RequestClose += delegatewindow.Close();/ Allow all controls in the win

42、dow to/ bind to the ViewModel by setting the/ DataContext, which propagates down/ the element tree.window.DataContext = viewModel;window.Show();MainWindow 包含一个菜单项,该菜单项的 Command 属性绑定到 MainWindowViewModel 上的 CloseCommand 属性上。当用户点击该菜单, App 类响应请求,调用窗体的关闭方法。 菜单 Xaml 如下所示:<!- In MainWindow.xaml ->&l

43、t;Menu><MenuItem Header="_File"><MenuItem Header="_Exit" Command="Binding Path=CloseCommand" /></MenuItem><MenuItem Header="_Edit" /><MenuItem Header="_Options" /><MenuItem Header="_Help" /></Menu&g

44、t;MainWindowViewModel 包含了 WorkspaceViewModel 对象一个 observable 类型的集合, 该集 合的名称为 Workspaces。主窗体包含了一个 TabControl,其ItemsSource绑定到上述的集合。 每一个 tab item 都有一个关闭按钮,其 Command 属性绑定到它对应 WorkspaceViewModel 实例的 CloseCommand 上。模版展示了如何渲染一个带关闭按钮的tab item 。配置 tab item模版的简化版会展示在下面代码中,这段代码可以在 MainWindowResources.xaml 文件中找

45、 到。<DataTemplate x:Key="ClosableTabItemTemplate"><DockPanel Width="120"><ButtonCommand="Binding Path=CloseCommand"Content="X"DockPanel.Dock="Right"Width="16" Height="16"/><ContentPresenter Content="Bindin

46、g Path=DisplayName" /></DockPanel></DataTemplate>当用户点击tab item上的关闭按钮时,会执行 WorkspaceViewModel的CloseCommand,触 发它的 Requestclose事件。MainWindowViewModel 会监控 workspace 的 Requestclose事件, 根据请求从 Workspaces 集合中移除相应的workspace 。因为 MainWindow 的 TabControl 的ItemsSource 绑定到 WorkspaceViewModel 的

47、observable 集合, 从集合中移除对象, 会引起从 TabControl 中移除相应的 workspace。 MainWindowViewModel 相应的逻辑如图 8 所示 图 8 从 UI 上移除 workspace/ In MainWindowViewModel.csObservableCollection<WorkspaceViewModel> _workspaces;public ObservableCollection<WorkspaceViewModel> Workspacesgetif (_workspaces = null)_workspace

48、s = new ObservableCollection<WorkspaceViewModel>(); _workspaces.CollectionChanged += this.OnWorkspacesChanged;return _workspaces;void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)if (e.NewItems != null && e.NewItems.Count != 0)foreach (WorkspaceViewModel worksp

49、ace in e.NewItems)workspace.RequestClose += this.OnWorkspaceRequestClose;if (e.OldItems != null && e.OldItems.Count != 0)foreach (WorkspaceViewModel workspace in e.OldItems)workspace.RequestClose -= this.OnWorkspaceRequestClose;void OnWorkspaceRequestClose(object sender, EventArgs e)this.Wor

50、kspaces.Remove(sender as WorkspaceViewModel);在 UnitTests 项目中, MainWindowViewModelTests.cs 文件包含了一个测试方法,该方法验证 上述功能是否正确执行。 很容易为 ViewModel 类创建单元测试是 MVVM 模式的一个大卖点, 因为它只需测试应用程序的功能,而不用写和 UI 交互的代码。上述测试方法图 9 所示 图 9 测试方法/ In MainWindowViewModelTests.csTestMethodpublic void TestCloseAllCustomersWorkspace()/ Cr

51、eate the MainWindowViewModel, but not the MainWindow.MainWindowViewModel target =new MainWindowViewModel(Constants.CUSTOMER_DATA_FILE);Assert.AreEqual(0, target.Workspaces.Count, "Workspaces isn't empty.");/ Find the command that opens the "All Customers" workspace.CommandVie

52、wModel commandVM = target.Commands.First(cvm => cvm.DisplayName = "View all customers");/ Open the "All Customers" workspace. commandVM.Command.Execute(null);Assert.AreEqual(1, target.Workspaces.Count, "Did not create viewmodel.");/ Ensure the correct type of workspa

53、ce was created.var allCustomersVM = target.Workspaces0 as AllCustomersViewModel; Assert.IsNotNull(allCustomersVM, "Wrong viewmodel type created.");/ Tell the "All Customers" workspace to close. allCustomersVM.CloseCommand.Execute(null); Assert.AreEqual(0, target.Workspaces.Count,

54、 "Did not close viewmodel."); 把 View 应用到 ViewModel 上MainWindowViewModel 间接从主窗体的 TabControl 控件中增加移除 WorkspaceViewModel 对象。通过数据绑定 , TabItem 的 Content 属性显示继承 于 ViewModelBase 的对象。ViewModelBase并不是一个 UI元件,因此他并不支持渲染它自己。 默认在TextBlock中,WPF 的一个非可视化对象通过调用 ToString 方法以显示该对象。 很明显这不是你想要的, 除非你 的用户迫切的想知道

55、ViewModel 的类型名。我们通过强类型数据模版很容易告诉 WPF如何渲染ViewModel对象。强类型数据模版 key 属性名没有赋值,但是其DataType属性要赋以类型类的实例。如果WPF要去渲染ViewModel 对象,它会检查在资源系统范围内是否有一个强类型数据模版的DataType 和 ViewModel 对象(或者其基类) 的类型一样。 如果找到一个这样的模版的话, 他会用该模版去渲染被 TabItem Content 属性绑定的 ViewModel 对象。MainWindowResources.xaml 文件中有一个 ResourceDictionary (资源字典) ,该

56、字典被增加到 主窗体的资源层级中,这意味着文件包含的资源在正窗体范围内有效。当一个 TabItem 的 Content属性设置ViewModel对象时,该字典中的强类型数据模版会提供一个View (即用户自定义控件)去渲染 Tabitem Content。具体如图10所示图 10 提供 View<!-This resource dictionary is used by the MainWindow.-><ResourceDictionary xmlns=" xmlns:x="xmlns:vm="clr-namespace:DemoApp.Vie

57、wModel"xmlns:vw="clr-namespace:DemoApp.View"><!-This template applies an AllCustomersView to an instanceof the AllCustomersViewModel class shown in the main window.-><DataTemplate DataType="x:Type vm:AllCustomersViewModel"><vw:AllCustomersView /></Dat

58、aTemplate><!-This template applies a CustomerView to an instanceof the CustomerViewModel class shown in the main window.-><DataTemplate DataType="x:Type vm:CustomerViewModel"><vw:CustomerView /></DataTemplate><!- Other resources omitted for clarity. -></ResourceDictionary>你不需要写任何代码去决定哪一个View去展示ViewModel对象。WPF资源系统把你从繁重的工作解脱出来,让你去关注更重要的事情。在复杂的场景中,可能需要通过编程去选择View,但是在大部分情况下,通过编程选择View是不必要的。The Data Model and Repository 数据模型和存储库 你已经知道应用程序如何去加载, 显示以及关闭一个 ViewModel 对象。现在一切已经就位, 你可以在整个应用程序范围内,回顾一下具体实现的细节。在深入理解应用程序的两个

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论