1. 什么是 MVVM Toolkit

模型-视图-视图模型 (MVVM) 是用于解耦 UI 代码和非 UI 代码的 UI 体系结构设计模式。 借助 MVVM,可以在 XAML 中以声明方式定义 UI,并使用数据绑定标记将 UI 链接到包含数据和命令的其他层。

微软虽然提出了 MVVM,但又没有提供一个官方的 MVVM 库(多年前有过 Prism,但已经离家出走了)。每次有人提起 MVVM 库,有些人会推荐 Prism(例如我),有些人会推荐 MVVMLight。可是现在 Prism 已经决定不再支持 UWP, 而 MVVMLight 又不再更新,在这左右为难的时候 Windows Community Toolkit 挺身而出发布了 MVVM Toolkit。 MVVM Toolkit 延续了 MVVMLight 的风格,是一个轻量级的组件,而且它基于 .NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno 等多个平台。相比它的前身 MVVMLight,它有以下特点:

  • 更高:版本号更高,一出手就是 7.0。
  • 更快:速度更快,MVVM Toolkit 从一开始就以高性能为实现目标。
  • 更强:后台更强,MVVM Toolkit 的全程是 'Microsoft.Toolkit.Mvvm',根正苗红。

目前,MVVM Toolkit 已经更新到 '7.0.2',它的详细资料可以参考下面链接:

Nugethttps://www.nuget.org/packages/Microsoft.Toolkit.Mvvm

文档https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction

源码https://github.com/CommunityToolkit/WindowsCommunityToolkit/tree/main/Microsoft.Toolkit.Mvvm

虽然是 Windows Community Toolkit 项目的一部分,但它有独立的 Sample 和文档,可以在这里找到:

https://github.com/CommunityToolkit/MVVM-Samples

这篇文章将简单介绍 MVVM Toolkit 的几个基本组件。

2. 各个组件

2.1 ObservableObject

ObservableObject 实现了 INotifyPropertyChangedINotifyPropertyChanging,并触发 PropertyChangedPropertyChanging 事件。

public class User : ObservableObject
{
private string name; public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}

在这段示例代码中,如果 name 和 value 的值不同,首先触发 PropertyChanging 事件,然后触发 PropertyChanged

2.2 RelayCommand

RelayCommandRelayCommand<T> 实现了 ICommand 接口,INotifyPropertyChangedICommand 是 MVVM 模式的基础。下面的代码使用 ObservableObjectRelayCommand 展示一个基本的 ViewModel:

public class MyViewModel : ObservableObject
{
public MyViewModel()
{
IncrementCounterCommand = new RelayCommand(IncrementCounter);
} private int counter; public int Counter
{
get => counter;
private set => SetProperty(ref counter, value);
} public ICommand IncrementCounterCommand { get; } private void IncrementCounter() => Counter++;
}
<Page
x:Class="MyApp.Views.MyPage"
xmlns:viewModels="using:MyApp.ViewModels">
<Page.DataContext>
<viewModels:MyViewModel x:Name="ViewModel"/>
</Page.DataContext> <StackPanel Spacing="8">
<TextBlock Text="{x:Bind ViewModel.Counter, Mode=OneWay}"/>
<Button
Content="Click me!"
Command="{x:Bind ViewModel.IncrementCounterCommand}"/>
</StackPanel>
</Page>

在这段示例里 IncrementCounterCommand 包装了 IncrementCounter 函数提供给 Button 绑定。IncrementCounter 函数更改 Counter 的值并通过 PropertyChanged 事件通知绑定的 TextBlock。

2.3 AsyncRelayCommand

AsyncRelayCommandAsyncRelayCommand<T> 也实现了 ICommand,不过它们支持异步操作,提供的 ExecutionTaskIsRunning 两个属性对监视任务运行状态十分有用。

例如这个 ViewModel:

public MyViewModel()
{
DownloadTextCommand = new AsyncRelayCommand(DownloadTextAsync);
} public IAsyncRelayCommand DownloadTextCommand { get; } private async Task<string> DownloadTextAsync()
{
await Task.Delay(3000); // Simulate a web request return "Hello world!";
}

使用相关的 UI 代码:

<Page.Resources>
<converters:TaskResultConverter x:Key="TaskResultConverter"/>
</Page.Resources>
<StackPanel Spacing="8">
<TextBlock>
<Run Text="Task status:"/>
<Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask.Status, Mode=OneWay}"/>
<LineBreak/>
<Run Text="Result:"/>
<Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask, Converter={StaticResource TaskResultConverter}, Mode=OneWay}"/>
</TextBlock>
<Button
Content="Click me!"
Command="{x:Bind ViewModel.DownloadTextCommand}"/>
<muxc:ProgressRing
HorizontalAlignment="Left"
IsActive="{x:Bind ViewModel.DownloadTextCommand.IsRunning, Mode=OneWay}"/>
</StackPanel>

点击 ButtonDownloadTextAsync 开始运行,在 UI 上 TextBlock 和 ProgressRing 绑定到 ExecutionTaskIsRunning 并显示任务运行状态,最后通过 TaskResultConverter 显示任务结果。

2.4 Messenger

对于主要目的是松耦合的 MVVM 框架,提供一个用于消息交换的系统十分有必要。MVVM Toolkit 中用于消息交换的核心是 WeakReferenceMessenger 类。

// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
public LoggedInUserChangedMessage(User user) : base(user)
{
}
} // Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
// Handle the message here, with r being the recipient and m being the
// input messenger. Using the recipient passed as input makes it so that
// the lambda expression doesn't capture "this", improving performance.
}); // Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));

正如这段代码所示,WeakReferenceMessenger 主要通过 RegisterSend 进行信息交换,它的使用方式类似于 MVVMLight 的 messenger 类。MVVM Toolkit 另外还提供了一个 StrongReferenceMessenger 类,更多使用方法可以参考这篇 文档Messenger 功能强大且简单易用,但也由于误用会带来风险而引发了一些争议,有必要更详细地理解它的原理和用法以避免它带来的其它风险,这篇文章只是简单地介绍一下它的用法。

2.5 ObservableRecipient

ObservableRecipient 继承了 ObservableObject 并支持从 Messenger 接收信息,可通过 IsActive 属性激活或停用。它可以用作 ViewModel 的基类,事实上它的作用基本上相遇于 MVVMLight 中的 ViewModelBase

public class MyViewModel : ObservableRecipient, IRecipient<LoggedInUserRequestMessage>
{
public void Receive(LoggedInUserRequestMessage message)
{
// Handle the message here
}
}

3. The 性能

MVVM Toolkit 在开发过程中为了追求卓越的性能做了很多努力,例如提供一个 StrongReferenceMessenger 类,性能如上图所示地有了大幅提升。又例如下面这篇文章所介绍的:

MVVM Toolkit Preview 3 & The Journey of an API

有兴趣的话可以通过源码详细了解一下。

4. 结语

这篇文章简单介绍了 MVVM Toolkit 中的主要功能,更多内容可参考 源码单元测试windows-toolkit/MVVM-Samples 中提供的示例应用:

5. 参考

Sample repo for MVVM package

Microsoft.Toolkit.Mvvm at master

[Feature] Basic MVVM primitives (.NET Standard)

NuGet Gallery _ Microsoft.Toolkit.Mvvm

MVVM Light Toolkit

数据绑定和 MVVM

[Feature] Microsoft.Toolkit.Mvvm package (Preview 5)

MVVM Toolkit Preview 3 & The Journey of an API

[WPF] 使用 MVVM Toolkit 构建 MVVM 程序的更多相关文章

  1. 使用MVVM设计模式构建WPF应用程序

    使用MVVM设计模式构建WPF应用程序 本文是翻译大牛Josh Smith的文章,WPF Apps With The Model-View-ViewModel Design Pattern,译者水平有 ...

  2. UWP应用程序使用Prism框架构建MVVM

    在我们创建的UWP解决方案中选择引用->管理NuGet包程序包 NuGet管理包 2. 搜索Prism.Core,并安装 搜索Prism.Core 3. 搜索Prism.Unity,并安装 搜索 ...

  3. 使用 MVVM Toolkit Source Generators

    关于 MVVM Toolkit 最近 .NET Community Toolkit 发布了 8.0.0 preview1,它包含了从 Windows Community Toolkit 迁移过来的以下 ...

  4. WPF学习12:基于MVVM Light 制作图形编辑工具(3)

    本文是WPF学习11:基于MVVM Light 制作图形编辑工具(2)的后续 这一次的目标是完成 两个任务. 本节完成后的效果: 本文分为三个部分: 1.对之前代码不合理的地方重新设计. 2.图形可选 ...

  5. WPF学习11:基于MVVM Light 制作图形编辑工具(2)

    本文是WPF学习10:基于MVVM Light 制作图形编辑工具(1)的后续 这一次的目标是完成 两个任务. 画布 效果: 画布上,选择的方案是:直接以Image作为画布,使用RenderTarget ...

  6. WPF 线程:使用调度程序构建反应速度更快的应用程序

    原文:WPF 线程:使用调度程序构建反应速度更快的应用程序 作者:Shawn Wildermuth 原文:http://msdn.microsoft.com/msdnmag/issues/07/10/ ...

  7. 说不尽的MVVM(1) – Why MVVM

    最近学的一篇课文<说不尽的狗>竟让我有了写<说不尽的MVVM>这一想法,事非亵渎,实出无奈.我在刚学WPF不久时听说有MVVM这种东西,做了下尝试,发现他能给程序的设计带来很大 ...

  8. iOS-马上着手开发iOS应用应用程序-第二部分构建应用程序

    第二部分构建应用程序 1,应用程序开发过程 2,设计用户界面 3,定义交互 4,教程:串联图 1,应用程序开发过程 定义概念 设计用户界面 定义交互 实现行为整合数据 对象是应用程序的基石 类是对象的 ...

  9. 实战案例--Grunt构建Web程序

    GruntJS构建Web程序.使用Gruntjs来搭建一个前端项目,然后使用grunt合并,压缩JS文件,熟练了node.js安装和grunt.js安装后,接下来来实战一个案例,案例是根据snandy ...

随机推荐

  1. CentOS-搭建MinIO集群

    一.基础环境 操作系统:CentOS 7.x Minio在线演示 Minio下载 二.准备工作 2.1.机器资源 192.168.1.101 /data1 192.168.1.102 /data2 1 ...

  2. Java:HttpPost 传输Json数据过长使用HttpServletRequest解析

    直接上代码 /** * 测试生成json数据 */ @Test public void synYxGoodsInfoTest() { try { String url = "http://1 ...

  3. SpringMVC(12)完结篇 基于Hibernate+Spring+Spring MVC+Bootstrap的管理系统实现

    到这里已经写到第12篇了,前11篇基本上把Spring MVC主要的内容都讲了,现在就直接上一个项目吧,希望能对有需要的朋友有一些帮助. 一.首先看一下项目结构: InfrastructureProj ...

  4. 《PHP设计模式大全》系列分享专栏

    <PHP设计模式大全>已整理成PDF文档,点击可直接下载至本地查阅https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第 ...

  5. NAT444技术简介

    嘛,最近老师布置了一道题目与NAT444技术相关,遂收集一波相关资料. 首先来一波名词解释: ICP:网络内容服务商(Internet Content Provider) BRAS:宽带远程接入服务( ...

  6. 技能篇:docker的简易教程

    虚拟机技术每家公司发展到一定规模都必须考虑的,更好的环境隔离,更好的事故排查,更好的服务部署 docker的原理 docker更换阿里源 docker容器的相关命令 Dockerfile文件编写 do ...

  7. Java 给PDF签名时添加可信时间戳

    一.程序运行环境 编译环境:IntelliJ IDEA 所需测试文件:PDF..pfx数字证书及密钥.PDF Jar包(Free Spire.PDF for Java).签名图片(.png格式) 可信 ...

  8. C++11标准特性的一些理解

    (1)auto 和 decltype 关键字 在C++11之前,auto关键字用来指定存储期(C++98中指的是自动生命周期).在新标准中,它的功能变为类型推断.C++11引入auto关键词与之前C语 ...

  9. C语言 c++区别

    C语言是C89标准,C++是C++99标准的.C89就是在1989年制定的标准,如今最新的是C11和C++11标准.根据不同的标准,它们的功能也会有所不同,但是越新的版本支持的编译器越少

  10. SQL_之 递归_START WITH id ='102' CONNECT BY PRIOR pid=id

    oracle 递归用法 SELECT * FROM menu START WITH id ='102' CONNECT BY PRIOR pid=id 一种应用 SELECT * FROM menu ...