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. 『无为则无心』Python函数 — 26、Python函数参数的传递方式

    目录 1.位置参数 2.关键字参数 3.缺省参数(默认参数) 4.不定长参数(可变参数) (1)包裹位置传递 (2)包裹关键字传递 5.位置参数.默认参数.可变参数的混合使用 6.拓展:参数解包 提示 ...

  2. Auto update Python 2.x to 3.x

    1, How to check the python version import sys if sys.version_info < (3.0)     print ("python ...

  3. Vector ArrayList LinkedList

    三者都实现了List接口! Vector与ArrayList:采用顺序存储的方式,但是Vector是线程安全的,ArrayList是线程不安全的,按需使用: 当存储空间不足的时候,ArrayList默 ...

  4. C++实现KDTree

    简介   k-d树(k-dimensional),是一种分割k维数据空间的数据结构(对数据点在k维空间中划分的一种数据结构),主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索). 举例    ...

  5. 「ARC103D」 Distance Sums

    「ARC103D」 Distance Sums 传送门 水题. 首先如果让你求树上的节点 \(i\) 到其它所有节点的距离和,这是非常简单的,这就是非常常规的换根 \(\texttt{DP}\). 那 ...

  6. C++ 标准模板库(STL)——算法(Algorithms)的用法及理解

    C++ STL中的算法(Algorithms)作用于容器.它们提供了执行各种操作的方式,包括对容器内容执行初始化.排序.搜索和转换等操作.按照对容器内容的操作可将STL 中的算法大致分为四类: (1) ...

  7. 安卓源码默认开启USB调试

    找到\frameworks\base\services\usb\java\com\android\server\usb\UsbDeviceManager.java下的 Settings.Global. ...

  8. css颜色介绍和背景设置

    现在美丽网页的设计图中颜色五花八门的,网页模块中漂亮背景图也很多,网页中颜色和背景设置必不可少,接下来我们就先学颜色是如何表达的,要知其然,知其所以然. 颜色表达形式 1.RGB:rgb( red, ...

  9. 三、k8s集群可用性验证与调参(第一章、k8s高可用集群安装)

    作者:北京小远 出处:http://www.cnblogs.com/bj-xy/ 参考课程:Kubernetes全栈架构师(电脑端购买优惠) 文档禁止转载,转载需标明出处,否则保留追究法律责任的权利! ...

  10. Day8 方法详解及递归思想.

    何为方法 Java方法是语句的集合,它们在一起执行一个功能. 方法是解决一类问题步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 设计方法的原则: 方法的本意是功能块,就是实 ...