精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品原创,禁止全文或局部转载,禁止任何形式的非法使用,侵权必究。点名“简易百科”和闲暇巴盗用锐英源原创内容。
锐英源软件在使用MVVMLight开发了企业信息化软件MIS软件,对于MVVMLight有一套自己的使用模式,但是在英文网站上找到了个超详细用法步骤说明,还包含服务数据接口,一时心喜,翻译下供自己提高,也提供给同行给共同提高。
一直想找个自动生成Model类的工具,看本文里讲到VS向导生成新 ADO.NET 实体数据模型是功能类似的,后面研究下。
MVVMLight工具包可以生成视图,我以前是手工拷贝的,后面可以注意下MVMMLight工具包功能。
下面是正文:
MVVM Light 工具包提供了大量样板代码来快速实现基于模型-视图-视图模型的应用程序,并为用户提供了自定义和设计应用程序的自由。本文介绍如何开始为 WPF 应用程序进行 MVVM 开发。
关注点分离(解耦)或 SoC 是促进软件架构最佳实践的原则。可以将“关注”视为软件功能的一个方面。例如:UI 可以是一个关注点,业务逻辑可以是另一个关注点,等等。这个想法是确保每个关注点都有一个单一的、明确定义的目的,并找到一种平衡的方式将这些特性和概念分成不同的模块。这最终减少了重复代码并保持模块相互解耦,使我们的应用程序可维护和可管理。作为 SoC 的一个非常基本的例子,想想 HTML、CSS 和 JavaScript,所有这些技术都有一个明确的用途。HTML 定义内容结构,CSS 定义内容呈现,JavaScript 定义内容如何与用户交互和行为。
本文发表自DotNetCurry .NET 杂志 - 面向 .NET 专业人士的免费高质量数字杂志,每两个月出版一次。免费订阅此电子杂志并获得专家提供的数百个免费 .NET 教程
为了实现 SoC 原理,多年来出现了许多设计模式。例如,Model-View-Presenter (MVP) 适用于 Windows 窗体;用于 ASP.NET MVC 的模型-视图-控制器 (MVC);Model-View-ViewModel (MVVM) 可以很好地与 WPF 等配合使用。对于那些感兴趣的人,Martin Fowler 有一篇很好的文章解释了这些模式的差异。
XAML通过将应用程序的 GUI 与编程逻辑(用 C#或 VB.NET 编码)分离,在 WPF、Silverlight、Windows Phone 和 Windows 8 应用程序中启用SoC。Model-View-ViewModel (MVVM) 是一种设计模式,通过允许将业务逻辑与视图 (UI) 分离来解决SoC,这最终使编写单元测试和实现并行开发和设计变得更加容易。它利用 XAML 平台的丰富数据绑定功能通过视图的DataContext属性向 UI 公开视图模型。业务层也称为模型,而视图模型层负责使用 DataBinding 将模型中的数据对象公开给 UI。这ViewModel包含 View 显示逻辑,其中 UI 上的操作可以使用ViewModel中声明的 Commands 属性来处理。
为了实现 MVVM,您需要首先了解命令、消息传递和绑定。然后,您需要了解 MVVM 原则并实施这些原则,以在您的开发中保持功能和简单性的良好平衡。您还需要提供单元测试支持。总而言之,这需要相当多的时间和精力。幸运的是,有一些不错的 MVVM 框架可供选择,例如 Prism、Cailburn、nRoute 和 Galasoft 的 MVVM Light Toolkit。我们将探索如何使用 Laurent Bugnion 的 MVVM Light Toolkit 在 WPF 应用程序中实现 MVVM。
MVVM Light 工具包的主要目的是加速在 WPF、Silverlight、Windows Store (RT) 和 Windows Phone 中创建和开发 MVVM 应用程序
本文中解释的步骤专门针对那些想要开始 MVVM 开发并需要现成工具包来开发其 WPF 应用程序的人。
MVVM Light 工具包可以从https://mvvmlight.codeplex.com/下载。
Visual Studio 2012 和 2013 的项目模板可以在http://mvvmlight.codeplex.com/releases/view/115541下载。目前,模板仅为 Visual Studio 2012 和 2013 Pro、Premium 和 Ultimate 版提供。MvvmLight.VS2012.vsix 适用于 Visual Studio 2012,MvvmLight.VS2013.vsix 适用于 Visual Studio 2013。根据您的 Visual Studio 版本,安装相应模板后,项目模板将可用,如图 1 所示:
图 1:VS 2013 中的 MVVM Light Toolkit 模板
这些项目模板默认为 MVVM Light Framework 提供必要的库,项目结构包含 ViewModel 类、模型,如图 2 所示:
图 2:Visual Studio 中的 MVVM Light 库
这些库提供了用于实现具有可通知属性、命令等的 ViewModel 的类。
如果我们需要在已经存在的项目中添加 MVVM Light 库,那么我们可以使用 NuGet 包来获取这些库。为此,请在 Visual Studio 中打开一个现有 WPF 项目 > 右键单击该项目 > 选择Manage NuGet Package > 并从 NuGet 窗口中选择 MVVM Light 库,如图 3 所示:
图 3:MVVM Light NuGet 包
在接下来的步骤中,我们将使用 MVVM Light 来实现一个 WPF 应用程序,该应用程序执行一些基本的数据库操作。在这些步骤中,我们将利用 MVVM Light 的以下功能:
对于此应用程序,我们将在示例 SQL Server Company 数据库中使用下表:
图 4:SQL Server 员工表
第 1 步:打开 Visual Studio 并创建一个 WPF 应用程序并将其命名为“WPF_MVVMLight_CRUD”。到此项目中,使用 NuGet 包添加 MVVM Light Libraries,如安装部分所述。该项目将添加必要的库和具有以下两个类的“ViewModel”文件夹:
- MainViewModel.cs - 此类继承自 ViewModelBase 类,它提供对RaisedPropertyChanged方法的访问权限,用于通知属性。
- ViewModelLocator.cs - 此类用于包含应用程序中所有视图模型的静态引用。此类的构造函数提供了一个非常简单的 IOC 容器,用于注册和解析实例。代码如下所示:
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
可以在此处找到有关服务定位器的信息:http: //msdn.microsoft.com/en-us/library/ff648968.aspx
该类在其构造函数的 IOC 容器中注册MainViewModel类:
SimpleIoc.Default.Register();
它还使用只读属性提供了 MainViewModel 的实例,如下所示:
public MainViewModel Main { get { return ServiceLocator.Current.GetInstance(); } }
ViewModelLocator实例将在 App.Xaml 资源中注册:
这将用于应用程序中跨视图的 DataBinding。
第 2 步:在项目中,添加一个名为“Model”的新文件夹。在此文件夹中,添加一个名为“CompanyEDMX”的新 ADO.NET 实体数据模型 > 选择 SQL Server 数据库和 EmployeeInfo 表。向导完成后,将显示下表映射:
图 5:员工表映射
第 3 步:现在添加一个名为“Services”的新文件夹,并在此文件夹中添加一个包含以下代码的类文件:
using System.Collections.ObjectModel; using WPF_MVVMLight_CRUD.Model; namespace WPF_MVVMLight_CRUD.Services { /// /// The Interface defining methods for Create Employee and Read All Employees /// public interface IDataAccessService { ObservableCollection GetEmployees(); int CreateEmployee(EmployeeInfo Emp); } /// /// Class implementing IDataAccessService interface and implementing /// its methods by making call to the Entities using CompanyEntities object /// public class DataAccessService : IDataAccessService { CompanyEntities context; public DataAccessService() { context = new CompanyEntities(); } public ObservableCollection GetEmployees() { ObservableCollection Employees = new ObservableCollection() ; foreach (var item in context.EmployeeInfoes) { Employees.Add(item); } return Employees; } public int CreateEmployee(EmployeeInfo Emp) { context.EmployeeInfoes.Add(Emp); context.SaveChanges(); return Emp.EmpNo; } } }
此代码定义了一个接口,用于使用实体框架访问数据库中的数据。
注:这是结合ADO.NET和SQLServer数据库的形式,用MySQL和MySQL的连接库可能要修改。
第 4 步:要在 IoC 中注册数据访问服务,我们需要在其中注册DataAccessService类。为此,请打开ViewModelLocator类,并添加以下行:
SimpleIoc.Default.Register();
DataAccessService 类的命名空间必须在 ViewModelLocator 类中使用。
第 5 步:让我们实现从表中读取所有员工的逻辑。
在 MainViewModel 类中,添加以下 Public 通知属性:
ObservableCollection _Employees; public ObservableCollection Employees { get { return _Employees; } set { _Employees = value; RaisePropertyChanged("Employees"); } }
此属性将向 UI 公开。属性的设置器调用RaisedPropertyChanged方法,该方法将在集合中的数据发生更改时在内部引发PropertyChanged事件。
在 ViewModel 类级别定义IDataAccessService对象,如下所示:
IDataAccessService _serviceProxy;
在 ViewModel 类中,声明以下方法来获取员工数据:
/// /// Method to Read All Employees /// void GetEmployees() { Employees.Clear(); foreach (var item in _serviceProxy.GetEmployees()) { Employees.Add(item); } }
上述方法从DataAccessService类中调用GetEmployees()方法,并将所有员工放入员工可观察集合中。
在 ViewModel 类中,现在定义RelayCommand对象如下:
public RelayCommand ReadAllCommand { get; set; }
使用构造函数依赖项将IDataAccessService传递给 ViewModel 的构造函数。DataAccessService 的对象可以从我们在第 4 步中注册的 IOC 获得。同时实例化 Employees 可观察集合和ReadAllCommand对象:
public MainViewModel(IDataAccessService servPxy) { _serviceProxy = servPxy; Employees = new ObservableCollection(); ReadAllCommand = new RelayCommand(GetEmployees); }
ReadAllCommand与 GetEmployees() 方法一起传递。
第 6 步:在项目中,添加由 MVVM Light 工具包提供的新 MVVM 视图,如下所示:
图 6:MvvmView 模板
将此视图命名为“EmployeeInfoView.xaml”。
注意:默认情况下,MVVM Light View 添加 WPF 窗口,因此对于此应用程序,我们将用 UserControl 替换 Window。将视图的根标记从 Window 更改为 UserControl 后,在视图后面的代码中,将基类从 Window 更改为 UserControl。
第 7 步:在此视图中,添加一个 DataGrid、TextBlock 和一个 Button,如图所示:
图 7:我们应用程序的 UI 设计
在 XAML 部分中,将 UserControl 的DataContext属性设置为ViewModelLocator类公开的“Main”属性:
DataContext="{Binding Main, Source={StaticResource Locator}}"
“定位器”在 App.Xaml 资源中声明。Main是 ViewModelLocator 类公开的公共属性,它返回 MainViewModel 类的对象。上面的表达式意味着 MainViewModel 现在与 UserControl 绑定。这意味着所有公共声明(可通知的属性和命令)都可以与视图上的 XAML 元素绑定。
将 MainViewModel 的ReadAllCommand命令和Employees 集合属性分别绑定到Button 和DataGrid :
第 8 步:打开 MainWindow.xaml 并将其宽度更改为 1300。将 Grid 元素的宽度设置为 1290,并在其中创建两列,每列宽度为 640。在第零 (0) 列中,添加EmployeesInfo视图。
要在 MainWindow.xaml 中添加视图,必须在 Window 标记中注册视图的命名空间:
xmlns:Views="clr-namespace:WPF_MVVMLight_CRUD.Views"
现在将视图添加到网格:
第 9 步:运行项目并显示一个视图。点击“List All Employees”,结果如图8所示:
图 8:列出所有员工
在前面的步骤中,我们讨论了如何创建 ViewModel、定义可通知属性和 RelayCommand。在本节中,我们将讨论如何将数据从 View 发送到 View Model 并写入我们的数据库表中。
第 1 步:在 MainViewModel 中,声明以下属性:
EmployeeInfo _EmpInfo;
public EmployeeInfo EmpInfo
{
get { return _EmpInfo; }
set
{
_EmpInfo = value;
RaisePropertyChanged("EmpInfo");
}
}
EmployeeInfo对象将用于添加新的员工记录。
定义以下方法,该方法接受 EmployeeInfo 对象,并通过调用DataAccessService 类的CreateEmployee()方法将其保存到表中。
void SaveEmployee(EmployeeInfo emp)
{
EmpInfo.EmpNo = _serviceProxy.CreateEmployee(emp);
if(EmpInfo.EmpNo!=0)
{
Employees.Add(EmpInfo);
RaisePropertyChanged("EmpInfo");
}
}
CreateEmployee 方法返回 EmpNo。如果这不为零,那么 emp 对象将被添加到 Employees 可观察集合中。
现在在 ViewModel 类中定义 RelayCommand 对象属性:
public RelayCommand SaveCommand { get; set; }
中继命令声明泛型类型属性,其中T表示输入参数;在我们的例子中,T 属于 EmployeeInfo 类型。
在 ViewModel 构造函数中实例化 EmpInfo 和 RelayCommand:
EmpInfo = new EmployeeInfo(); SaveCommand = new RelayCommand(SaveEmployee);
RelayCommand 通过SaveEmployee()方法传递。这是可能的,因为 SaveEmployee 方法接受 EmployeeInfo 作为其输入参数。这与通用 RelayCommand 属性的声明中定义的对象相同。
第 2 步:打开 App.Xaml 并在resources中,为 TextBlock 和 TextBoxes 添加样式:
由于这些样式是在 App.Xam 中定义的,因此没有任何键,它们将应用于应用程序中的所有 TextBlock 和 TextBox。
第 3 步:在项目的 Views 文件夹中,添加一个新的 UserControl(您也可以使用 MVVM Light View),并将其命名为“SaveEmployeeView.xaml”。在视图中,添加文本块、文本框和一个按钮。将 UserControl 的 DataContext 设置为 MainViewModel 的 Main 属性。
DataContext="{Binding Main, Source={StaticResource Locator}}"
对于所有的 TextBox,将它们的Text属性绑定到EmpInfo属性,并将Button的command属性绑定到Main ViewModel公开的SaveCommand 。同时,将 EmpInfo 属性绑定到按钮的CommandParameter属性。这是将从 View 传递到 ViewModel 的实际参数。XAML 如下所示:
该视图现在看起来如图 9 所示:
第 4 步:在第二列(列索引 1)中添加我们刚刚在 MainWindow.xaml 中创建的 UserControl:
第 5 步:运行应用程序,单击“列出所有员工”以在 DataGrid 中显示所有员工。
图 10:列出所有员工视图
我没有添加任何验证来保持本文范围的简洁,但你应该这样做。输入员工数据(EmpNo 除外)并单击“保存员工”按钮。记录被添加。现在滚动到 DataGrid 的底部,你会发现我们新添加的记录:
图 11:新增记录
我们已经看到了如何使用通用命令将参数从 UI 传递到 ViewModel。
在没有 Command 属性的 UI 元素上定义命令
UI 元素(如 Button、RadioButton 等)公开了 command 属性,使用 ViewModel 中的哪些方法可以执行。但是如果我们需要在 UI 上有一个搜索文本框,当输入数据时,文本框中的匹配数据会从集合中搜索并显示在 UI 上。在这种情况下,我们需要更改文本框的行为以支持命令。
当我们在项目中添加 MVVM Light 库时,项目也添加了System.Windows.Interactivity.dll程序集。这个程序集允许我们定义 UI 元素的行为。
MVVM Light 库在GalaSoft.MvvmLight.Command命名空间下提供了一个EventToCommand类。此类允许我们将任何 FrameworkElement 的任何事件绑定到 ICommand。在我们的例子中,我们将使用EventToCommand通过在 TextBox 的 TextChanged 事件上定义命令来执行 ViewModel 类的方法。
第 1 步:打开 MainViewModel 并在其中添加以下属性、方法和命令:
public string EmpName
{
get { return _EmpName; }
set
{
_EmpName = value;
RaisePropertyChanged("EmpName");
}
}
字符串属性将与视图中的文本框绑定。在文本框中输入文本时将设置此属性。
void SearchEmployee() { Employees.Clear(); var Res = from e in _serviceProxy.GetEmployees() where e.EmpName.StartsWith(EmpName) select e; foreach (var item in Res) { Employees.Add(item); } }
该方法根据 EmpName 从集合中过滤员工。现在在 ViewModel 中定义 RelayCommand 对象:
public RelayCommand SearchCommand { get; set; }
通过将 SearchEmployee 方法传递给它,在 MainViewModel 的构造函数中实例化 Command 对象:
SearchCommand = new RelayCommand(SearchEmployee);
第 2 步:打开 EmployeeInfoView.xaml 并向其中添加一个 TextBlock 和 TextBox。要将 Interactivity 和 EventToObject 注册到 XAML,我们需要 UserControl 标记中的以下程序集:
xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
定义 DataBinding,在 TextBox 上进行命令的交互性如下:
在 XAML 中,TextBox 与 MainViewModel 的 EmpName 属性绑定。Binding 类的UpdateSourceTrigger属性设置为PropertyChanged 事件;这意味着当在文本框中输入文本时,将使用文本框中的文本设置 EmpName 属性。事件触发器在TextChanged事件的文本框中定义。这意味着当触发 TextChanged 事件时,EventToCommand 将执行 MainViewModel 中定义的 SearchCommand 方法。
第 3 步:运行应用程序,单击“列出所有员工”按钮,所有员工将显示在 DataGrid 中:
图 12:列出所有员工
在 EmpName 中输入一些文本以搜索 TextBox。DataGrid 将由所有具有 EmpName 的 Employee 记录填充,该记录以文本框中输入的文本开头:
图 13:搜索 DataGrid
因此使用EventToCommand,我们可以轻松地将命令绑定到 FrameworkElement。
通常,当开发阶段涉及多个团队时,他们可能会设计单独的视图,并且这些视图存在于同一个容器中。例如,以显示所有员工列表的EmployeeInfoView和执行 Create、Update 等操作的SaveEmployeeView 为例。现在的要求是,当从 EmployeeInfoView 中选择一个 Employee 时,它应该显示在 SaveEmployeeView 中以进行更新。由于这两个视图都有自己的 ViewModel,因此可能会通过这些 ViewModel 发送数据。
由于两个视图是分开的,我们如何将选定的员工从一个视图发送到另一个视图?传统上,这可以使用事件处理机制来实现。选择员工时,请提高事件并将员工信息传递给本事件,然后在其他观点中收听此事件。这种方法要求两个视图都应该通过访问彼此的对象来直接相互通信。这是紧耦合的场景。那么现在的问题是如何以松散耦合的方式实现相同的功能?
答案是使用MVVM Light Messenger。这个信使提供了一种松散绑定的方式来将消息(数据)从一个 ViewModel 发送到另一个 ViewModel。
从图表上看,信使可以解释如下:
图 14:MVVMLight信使
Messenger 是一个存在于整个应用程序中的单例对象。发送方 ViewModel 只需调用静态的“发送”方法。接收者 ViewModel 需要向 messenger 注册才能接收对象。它提供了一个回调函数,在收到新消息时调用该函数。让我们看看这是如何做到的。
第 1 步:在项目中添加一个新文件夹并将其命名为“MessageInfrastructure”。在此文件夹中,添加一个新的类文件:
using WPF_MVVMLight_CRUD.Model; namespace WPF_MVVMLight_CRUD.MessageInfrastructure { public class MessageCommunicator { public EmployeeInfo Emp { get; set; } } }
上面的类定义了 EmployeeInfo 类型的Emp属性。这将用作从一个视图传递到另一个视图的消息(数据)。
第 2 步:在 MainViewModel 中添加以下方法:
void SendEmployeeInfo(EmployeeInfo emp) { if(emp!=null) { Messenger.Default.Send(new MessageCommunicator() { Emp = emp }); } }
注意:请在 MainViewModel 中添加“GalaSoft.MvvmLight.Messaging”命名空间。
上述方法接受一个EmployeeInfo 对象并调用Messenger 的Send() 方法,该方法被键入到MessageCommunicator 类中。这意味着调用上述方法的 View 必须传递 EmployeeInfo 对象。
在 ViewModel 中定义以下RelayCommand对象:
public RelayCommand SendEmployeeCommand { get; set; }
RelayCommand 使用EmployeeInfo 类型的参数定义。这意味着它将执行具有 EmployeeInfo 类型的输入参数的方法。
在 ViewModel 的构造函数中,定义 RelayCommand 的实例,如下所示:
SendEmployeeCommand = new RelayCommand(SendEmployeeInfo);
第 2 步:打开 EmployeeInfoView 并为 DataGrid 定义 EventToCommand。由于DataGrid与员工收藏集约束,因此选择数据杂志时,它将选择员工Iinfo对象。我们将 DataGrid 的 SelectionChanged 事件映射到 EventToCommand,如下所示:
上面的 XAML 显示 Command 属性与 ViewModel 中声明的SendEmployeeCommand绑定。从 UI 发送到 ViewModel 的参数是SelectedItem,它是一个 EmployeeInfo 对象。由于 SendEmployeeCommand 执行SendEmployeeInfo方法,因此 EmployeeInfo 对象将被传递给该方法。
第3步:现在我们需要注册信使。为此,在 MainViewModel 添加以下方法:
void ReceiveEmployeeInfo() { if (EmpInfo != null) { Messenger.Default.Register(this,(emp)=>{ this.EmpInfo = emp.Emp; }); } }
上述方法注册到 messenger 并接受接收到的 Emp 消息。然后将此消息设置为 ViewModel 类中定义的 EmpInfo 通知属性。在 ViewModel 的构造函数中调用此方法。由于 EmpInfo 属性与 SaveEmployeeView 绑定,因此将在其中显示 Employee 数据。
第 4 步:运行应用程序,单击“加载所有员工”按钮,DataGrid 将显示所有员工。从 DataGrid 中选择 Row,选中的员工信息将显示在 SaveEmployeeView 中,如下所示:
这表明我们可以轻松地在两个单独的视图中建立 Messenger 基础通信。
MVVM 有很多优点,但它可能需要你自己设置很多东西。MVVM Light 工具包提供了大量样板代码来快速实现基于 MVVM 的应用程序,并为用户提供了自定义和设计应用程序的自由。使用 MVVM Light 工具包中的 ViewModelBase,我们不再需要实现 INotifyPropertyChanged。此外,MVVM 轻工具包提供了 Visual Studio 模板、信使、IoC 容器和一些有用的代码片段,可以让我们的 WPF 应用程序大放异彩!