WPF 的 MVVM 的分析理解(二)
来源:互联网 发布:深圳市飞扬空间网络 编辑:程序博客网 时间:2024/06/16 22:34
上面的 command 代码中,ViewModel 对象是通过构造函数传递进来。所以 ViewModel 类需要创建一个 command 对象来暴露这个对象的“ICommand”接口。这个“ICommand”接口将被 WPF XAML 使用并调用。下面是一些关于“CustomerViewModel”类使用 command 类的要点:
command 类是“CustomerViewModel”类的私有成员。
在“CustomerViewModel”类的构造函数中将当前对象的实例传递给 command 类。在之前解释 command 类的一节中我们说了 command 类构造函数获取 ViewModel 类的实例。因此在这一节中我们正是将当前实例传递给 command 类。
command 对象是通过以“ICommand”接口的形式暴露出来,这样才可以被 XAML 所使用。
using System.ComponentModel;public class CustomerViewModel {……private ButtonCommand objCommand; // Point 1 public CustomerViewModel() { objCommand = new ButtonCommand(this); // Point 2 } public ICommand btnClick // Point 3 { get { return objCommand; } }….….}
在你的 UI 中添加一个按钮,这样就可以把按钮的执行动作连接到暴露的“ICommand”接口。现在打开 button 的属性栏,选择 command 属性,右击创建一个数据绑定。
然后选择静态资源(Static Resource),并将“ButtonCommand”附加到button上。
当你点击了 Calculate Tax 按钮,它就执行了“CalculateTax”方法。并将税值结果存在“_tax”变量中。关于“CalculateTax”方法代码,可以阅读前面的小节“第三步:添加执行动作和“INotifyPropertyChanged”接口”。
换句话说,税值计算过程并不会自动通知给 UI。所以我们需要从对象发送某种通知给 UI,告诉它税值已经变化了,UI 需要重新载入绑定值。
因此,在 ViewModel 类中我们需要发送 INotify 事件给视图。
为了让你的 ViewModel 类能够实现通知,我们必须做三件事情。这三件事情都在下面的代码注释中指出,例如 Point1, Point2 和 Point3。
Point1: 如下面代码那样实现“INotifyPropertyChanged”接口。一旦你实现了该接口,它就创建了对象的“PropertyChangedEventHandler”事件。
Point2 和 3: 在“Calculate”方法中用“PropertyChanged”对象去触发事件,并在其中指定了某个属性的通知。在这里是“Tax”属性。安全起见,我们同样也要检查“PropertyChanged”是否不为空。
public class CustomerViewModel : INotifyPropertyChanged // Point 1{….…. public void Calculate() { obj.CalculateTax(); if (PropertyChanged != null) // Point 2 { PropertyChanged(this,new PropertyChangedEventArgs("Tax")); // Point 3 } } public event PropertyChangedEventHandler PropertyChanged;}
如果你运行程序,你应该可以看见当点击按钮后“Tax”值被更新了。
第四步:在 ViewModel 中解耦执行动作
到目前为止,我们用 MVVM 框架创建了一个简单的界面。这个界面同时包含了属性和命令实现。我们拥有了一个视图,它的 UI 输入元素(例如 textbox)通过绑定和 ViewModel 连接起来,它的任何执行动作(例如按钮点击)通过命令和 ViewModel 连接起来。ViewModel 和内部的 Model 通讯。
但是在上面的结构中还有一个问题:command 类和 ViewModel 类存在着过度耦合的情况。如果你还记得 command 类代码(我在下面贴出来了)中的构造函数是传递了 ViewModel 对象,这意味着这个 command 类无法被其它的 ViewModel 类所复用。
public class ButtonCommand : ICommand { private CustomerViewModel obj; // Point 1 public ButtonCommand(CustomerViewModel _obj) // Point 2 { obj = _obj; }..................}
!
但是在考虑了所有情况之后,让我们逻辑地思考下“什么是一个动作?”。它是一个事件,可以由用户从鼠标点击(左键或右键),按钮点击,菜单点击,功能键按下等。所以应该有一种方式通用化这些动作,并且让各种 ViewModel 有一种更通用的方法去绑定它。
逻辑上讲,如果你认为任务动作是一些方法和函数的封装逻辑。那有什么是“方法”和“函数”的通用表达方式呢?......努力想想.......再想想.......“委托”,“委托”,没错,还是“委托”。
我们需要两个委托,一个给“CanExecute”,另一个给“Execute”。“CanExecute”返回一个布尔值用来验证以及根据验证来使能(Enable)或者禁用(Disable)用户界面。“Execute”委托则将在“CanExecute”委托返回 true 时执行。
public class ButtonCommand : ICommand { public bool CanExecute(object parameter) // Validations { } public void Execute(object parameter) // Executions { } }
因此,换句话说,我们需要两个委托,一个返回布尔值,另一个执行动作并返回空。所以,创建一个“Func”和一个“Action”如何?“Func”和“Action”都可以用来创建委托。
如果你还不熟悉 Func 和 Action,可以看下下面这个视频。(译注:作者在这里提供了一个 YouTube 的视频链接,大概说的就是 C# 中 Func<> 和 Action<> 这两个委托的区别,前者 Func<> 模版参数包含返回值类型,而 Action<> 表示无返回值的泛型委托,参见这里)
通过使用委托的方法,我们试着创建一个通用的 command 类。我们对 command 类做了三个修改(代码参见下面),同时我也标注了三点 Point 1,2 和 3。
Point1: 我们在构造函数中移除了 ViewModel 对象,改为接受两个委托,一个是“Func”,另一个是“Action”。“Func”委托用作验证(例如验证何时动作将被执行),而“Action”委托用来执行动作。两个委托都是通过构造函数参数传递进来,并赋值给类内部的对应私有成员变量。
Point2 和 3: Func<> 委托(WhentoExecute)被“CanExecute”调用,执行动作的委托 Whattoexecute 则是在“Execute”中被调用。
public class ButtonCommand : ICommand{private Action WhattoExecute;private Func<bool> WhentoExecute; public ButtonCommand(Action What , Func<bool> When) // Point 1 { WhattoExecute = What; WhentoExecute = When; }public bool CanExecute(object parameter) { return WhentoExecute(); // Point 2 }public void Execute(object parameter) { WhattoExecute(); // Point 3 }}
在 Model 类中我们已经知道要执行什么了(例如“CalculateTax”),我们也创建一个简单的函数“IsValid”来验证“Customer”类是否有效。
public class Customer {public void CalculateTax() {if (_Amount > 2000) { _Tax = 20; }else if (_Amount > 1000) { _Tax = 10; }else { _Tax = 5; } }public bool IsValid() {if (_Amount == 0) {return false; }else {return true; } } }
在 ViewModel 类中我们同时传递函数和方法给 command 类的构造函数,一个给“Func”,一个给“Action”。
public class CustomerViewModel : INotifyPropertyChanged{private Customer obj = new Customer();privateButtonCommandobjCommand;publicCustomerViewModel() {objCommand = new ButtonCommand(obj.CalculateTax,obj.IsValid); }}
这样使得框架更好,更解耦, 使得这个 command 类可以以一个通用的方式被其它 ViewModel 引用。下面是改善后的架构, 需要注意 ViewModel 如何通过委托(Func和Action)和 command 类交互。
第五步:利用 PRISM
最后如果有一个框架能帮助实现我们的 MVVM 代码那就更好了。PRISM 就是其中一个可复用的框架。PRISM 的主要用途是为了提供模块化开发,但是它提供了一个很好的“DelegateCommand”类拿来代替我们自己创建的 command 类。
所以,第一件事情就是从这里下载 PRISM,编译这个解决方案,添加“Microsoft.Practices.Prism.Mvvm.dll”和“Microsoft.Practices.Prism.SharedInterfaces.dll”这两个 DLL 库的引用。
你可以去掉自定义的 command 类,导入“Microsoft.Practices.Prism.Commands”名称空间, 然后以下面代码的方式使用 DelegateCommand。
public class CustomerViewModel : INotifyPropertyChanged{private Customer obj = new Customer();private DelegateCommand objCommand;public CustomerViewModel() {objCommand = new DelegateCommand(obj.CalculateTax, obj.IsValid); }…………………………………………} }
WPF MVVM 的视频演示
我同时也在下面的视频中从头演示了如何实现 WPF MVVM(译注:一个 YouTube 链接...)。
- WPF 的 MVVM 的分析理解(二)
- WPF 的 MVVM 的分析理解(一)
- WPF中Mvvm模式的理解
- WPF与MVVM的实现(二)数据绑定
- WPF的MVVM
- WPF的mvvm的一点理解和实现
- WPF与MVVM的实现(一)MVVM简介
- WPF教程:MVVM模式的理解与应用
- [WPF] MVVM的一点资源
- 谈WPF的MVVM模式
- WPF的MVVM DataGrid用法
- wpf之三:WPF的MVVM模式
- 关于MVVM的理解
- 对于MVVM的理解
- MVVM的一些理解
- MVVM的理解
- MVVM 开发的几种模式讨论(WPF)
- WPF与MVVM的实现(四)命令绑定
- php 安全性
- 讨论nullptr和NULL
- android设置图片为圆角
- 6. 矩阵SVD分解
- Spring MVC 自学杂记(六) -- 多视图解析器配置
- WPF 的 MVVM 的分析理解(二)
- android Message机制详解
- The Super Powers (数论,STL(set去重))
- OEL5.5(64bit)安装Oracle 11g R2 RAC教程(图文并茂超详细) 2
- 不得不说的那些坑(一)
- VS2013+QT5.6.2安装笔记
- Centos 6.5 Install rabbitmq 3.6.7
- MarkdownPad2.5 注册码
- 7. 主成分分析 PCA