WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍

时间:2022-09-20 12:44:44

WPF自学入门(十一)WPF MVVM模式Command命令

在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新。但是这并不是我们使用MVVM的正确方式。正如上一篇文章中在开始说的,MVVM的目的是为了最大限度地降低了Xaml文件和CS文件的耦合度,分离界面和业务逻辑,所以我们要尽可能的在View后台不写代码。但是这个例子中,我们将更新ViewModel的代码写在了View里,下一个例子中,我们要通过命令(Command)的来将Button的事件分离出来。

因为本文中需要使用Command命令,我们先来简单了解Command命令。在WPF中使用命令的步骤很简单

1.创建命令

2.绑定命令

3.设置命令源

4.设置命令目标

WPF中命令的核心是System.Windows.Input.ICommand接口,所有命令对象都实现了此接口。当创建自己的命令时,不能直接实现ICommand接口,而是要使用System.Windows.Input.RouteCommand类,该类已经实现了ICommand接口,所有WPF命令都是RouteCommand类的实例。在程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand类的实例,它继承自RouteCommand类。

WPF提供了一个很好的方式来解决事件绑定的问题--ICommand。很多控件都有Command属性,如果没有,我们可以将命令绑定到触发器上。接下来我们来先实现一个ICommand接口。ICommand需要用户定义两个方法bool CanExecute和void Execute。第一个方法可以让我们来判断是否可以执行这个命令,第二个方法就是我们具体的命令。

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍
  1 using System;
  2
  3 using System.Collections.Generic;
  4
  5 using System.Linq;
  6
  7 using System.Text;
  8
  9 using System.Windows.Input;
 10
 11
 12
 13 /***********************作者:黄昏前黎明后**********************************
 14
 15 *   作者:黄昏前黎明后
 16
 17 *   CLR版本:4.0.30319.42000
 18
 19 *   创建时间:2018-04-05 22:57:56
 20
 21 *   命名空间:Example3
 22
 23 *   唯一标识:b9043d4c-fdd7-4e0f-a324-00f0f09286d0
 24
 25 *   机器名称:HLPC
 26
 27 *   联系人邮箱:hl@cn-bi.com
 28
 29 *
 30
 31 *   描述说明:
 32
 33 *
 34
 35 *   修改历史:
 36
 37 *
 38
 39 *
 40
 41 *****************************************************************/
 42
 43 namespace Example3
 44
 45 {
 46
 47     public class RelayCommand : ICommand
 48
 49     {
 50
 51         #region 字段
 52
 53         readonly Func<Boolean> _canExecute;
 54
 55         readonly Action _execute;
 56
 57         #endregion
 58
 59
 60
 61         #region 构造函数
 62
 63         public RelayCommand(Action execute)
 64
 65             : this(execute, null)
 66
 67         {
 68
 69         }
 70
 71         public RelayCommand(Action execute, Func<Boolean> canExecute)
 72
 73         {
 74
 75             if (execute == null)
 76
 77                 throw new ArgumentNullException("execute");
 78
 79             _execute = execute;
 80
 81             _canExecute = canExecute;
 82
 83         }
 84
 85         #endregion
 86
 87
 88
 89         #region ICommand的成员
 90
 91         public event EventHandler CanExecuteChanged
 92
 93         {
 94
 95             add
 96
 97             {
 98
 99
100
101                 if (_canExecute != null)
102
103                     CommandManager.RequerySuggested += value;
104
105             }
106
107             remove
108
109             {
110
111
112
113                 if (_canExecute != null)
114
115                     CommandManager.RequerySuggested -= value;
116
117             }
118
119         }
120
121
122
123         [DebuggerStepThrough]
124
125         public Boolean CanExecute(Object parameter)
126
127         {
128
129             return _canExecute == null ? true : _canExecute();
130
131         }
132
133
134
135         public void Execute(Object parameter)
136
137         {
138
139             _execute();
140
141         }
142
143         #endregion
144
145     }
146
147 }
148
149  
WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

我们再在我们的NameViewModel中声明一个ICommand字段:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍
 1 #region 命令
 2
 3         void UpdateNameExecute()
 4
 5         {
 6
 7             this.UserName = "黄昏前黎明后";
 8
 9             this.CompanyName = "中软易通科技";
10
11         }
12
13
14
15         bool CanUpdateNameExecute()
16
17         {
18
19             return true;
20
21         }
22
23
24
25         public ICommand UpdateName { get { return new RelayCommand(UpdateNameExecute, CanUpdateNameExecute); } }
26
27
28
29         #endregion
WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

最后,我们再将事件绑定上这个Command:

<Button Content="更新" Command="{Binding UpdateName}" Margin="20"/>

运行一下,看结果。我们成功将事件分离了出来。

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

看到上面的结果,似乎目前为止我们已经很好的解决了所有的问题。我们看到运行的数据,事件都是绑定的,实现了界面的完美分离。实际在处理问题是好像需要考虑通用性,这时我们能否把MVVM提取出来作为一个框架,来去更好的解决问题。下一次我们一起来看看怎么进行提取成为通用框架。

本文的DEMO下载地址:WPFMVVMDemo2.zip

WPF自学入门(十)WPF MVVM简单介绍

   前面文章中,我们已经知道,WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时,WPF会自动发出通知去更新UI。

  我们不管是NET中还是WPF中使用模式目的就是想达到高内聚低耦合。在WPF开发中,经典的编程模式是MVVM,是为WPF量身定做的模式,该模式充分利用了WPF的数据绑定机制,最大限度地降低了Xmal文件和CS文件的耦合度,也就是UI显示和逻辑代码的耦合度,如需要更换界面时,逻辑代码修改很少,甚至不用修改。与WinForm开发相比,我们一般在后置代码中会使用控件的名字来操作控件的属性来更新UI,而在WPF中通常是通过数据绑定来更新UI;在响应用户操作上,WinForm是通过控件的事件来处理,而WPF可以使用命令绑定的方式来处理,耦合度将降低。

一、MVVM介绍

  MVVM是Model-View-ViewModel(模型-视图-视图模型)的缩写形式,它通常被用于WPF或Silverlight开发。我们可以通过下图来直观的理解MVVM模式:  

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

  1、View就是用xaml实现的界面,负责与用户交互,接收用户输入,把数据展现给用户。

  2、ViewModel是一个C#类,负责收集需要绑定的数据和命令,聚合Model对象,通过View类的DataContext属性绑定到View,同时也可以处理一些UI逻辑。

  3、Model,就是系统中的对象,可包含属性和行为。

  三者之间的关系:View对应一个ViewModel,ViewModel可以聚合N个Model,ViewModel可以对应多个View

二、MVVM的优势

MVVM的根本思想就是界面和业务功能进行分离,View的职责就是负责如何显示数据及发送命令,ViewModel的功能就是如何提供数据和执行命令。各司其职,互不影响。在实际的业务场景中我们经常会遇到客户对界面提出建议要求修改,使用MVVM模式开发,当设计的界面不满足客户时,我们仅仅只需要对View作修改,不会影响到ViewModel中的功能代码,减少了犯错的机会。随着功能地增加,系统越来越复杂,相应地程序中会增加View和ViewModel文件,将复杂的界面分离成局部的View,局部的View对应局部的ViewModel,功能点散落在各个ViewModel中,每个ViewModel只专注自己职能之内的事情。ViewModel包含了View要显示的数据,并且知道View的交互代码,所以ViewModel就像一个无形的View。使用MVVM架构具有以下优势

1、易维护

2、灵活扩展

3、易测试

4、用户界面设计师与程序开发者能更好的合作

三、MVVM简单示例

为了让大家直观地了解MVVM的编程模式,下面会用到前面讲到的数据绑定以及命令等知识。

新建WPF项目,名称WPFMVVMDemo。添加用户类,如下图

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

在WPF术语中,这个叫“模型”,GUI是“视图”。不可思议的是“视图模型”,通过数据绑定将它们绑在一起,它真的是一个很好的适配器能将模型变成某种WPF框架可以使用的东西。所以这个就是“模型”。

接下来我们会非常容易理解创建视图模型:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

请注意这个视图模型不是十分正确的。因为我们在视图模型里暴露了属性,我们显然会想使在代码里改变的用户名和公司名自动的显示在视图上

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

后台代码:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

运行结果:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

这里我们点击更新按钮不会有任何反应,因为还没有实现数据绑定。此时视图不会收到任何的关于属性改变的通知。要解决这个问题我们必须实现名称为INotifyPropertyChanged的接口。任何实现了这个接口的类,当属性发生改变的时候会通知所有监听者,所以我们需要修改视图模型NameViewModel类:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

这里会产生多个事件。首先,我们检查了我们是否真的改变了属性。第二,如果值已经改变,我们向所有监听者注册PropertyChanged事件。现在我们有了一个模型Name和一个视图模型NameViewModel。我们只需要在定义视图。只需要修改视图MainWindow:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

运行结果:

WPF自学入门(十一)WPF MVVM模式Command命令        WPF自学入门(十)WPF MVVM简单介绍

本文的demo下载地址:WPFMVVMDemo1.zip