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


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',它的详细资料可以参考下面链接:

Nuget:https://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 实现了 INotifyPropertyChanged 和INotifyPropertyChanging,并触发 PropertyChanged 和 PropertyChanging 事件。

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

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

2.2 RelayCommand#

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

Copy
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++; } 
Copy
<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#

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

例如这个 ViewModel:

Copy
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 代码:

Copy
<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> 

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

2.4 Messenger#

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

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

2.5 ObservableRecipient#

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

Copy
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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM