5

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

 2 years ago
source link: https://www.cnblogs.com/dino623/p/building_MVVM_programs_using_MVVM_Toolkit.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK