[WPF] 使用 MVVM Toolkit 构建 MVVM 程序

  • A+
所属分类:.NET技术
摘要

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


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 显示任务结果。

[WPF] 使用 MVVM Toolkit 构建 MVVM 程序

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 性能

[WPF] 使用 MVVM Toolkit 构建 MVVM 程序

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

MVVM Toolkit Preview 3 & The Journey of an API

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

4. 结语

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

[WPF] 使用 MVVM Toolkit 构建 MVVM 程序

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