前言

本文基于.NET 6+VS2022+WPF。

在Wpf下最常使用的就是Mvvm模式了,有自己造轮子构建Mvvm框架的,也有使用现成的开源项目,我之前一直使用的是轻量级的MvvmLight了,这个框架还是非常不错的,使用也简单,不占用太大空间,其中最喜欢的莫过于全局Messenger了,可谓是神器。最近有个项目使用.Net6开发,在NuGet发现MvvmLight已经很久不更新了,上一次还是2018年9月12日,偶然发现微软官方出的Microsoft.Toolkit.Mvvm 完全继承了Messenger的优良传统,这是一个官方社区套件(Windows Community Toolkit),延续了MVVMLight的风格,是一个轻量级的组件,而且它基于.NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno等多个平台。Microsoft.Toolkit.Mvvm也已经过时了。现在微软推出全新的Mvvm框架=》CommunityToolkit.MVVM

链接: https://docs.microsoft.com/zh-cn/windows/communitytoolkit/mvvm/introduction  机器翻译的有问题,不容易理解

个人的:      容易理解

备注

我发现这个术语在WPF和Silverl社区更为流行。
与MVP中的Presenter不同,ViewModel不需要实现对View的引用。View将属性绑定到ViewModel中,反向的,ViewModel暴露的属性包含在Model对象和View中特殊的状态。View和ViewModel之间的绑定的构建非常简单只需将ViewModel对象被作为View的上下文(DataContext)来设置。

 

MVVM Toolkit版本

自定义Mvvm框架:方便自己理解mvvm框架。

wpf mvvm stlyle架构 wpf高性能mvvm框架_WPF

MvvmLight:已经过时,被Microsoft.Toolkit.Mvvm取代。在NuGet发现MvvmLight已经很久不更新了,上一次还是2018年9月12日。

Microsoft.Toolkit.Mvvm:已经过时,停止更新。一发布就是版本7。两大特性RelayCommand和Messenger。延续了MVVMLight的风格,是一个轻量级的组件,而且它基于.NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno等多个平台。将要过          时,将被CommunityToolkit.Mvvm取代。

Nugethttps://www.nuget.org/packages/Microsoft.Toolkit.Mvvm文档https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction源码https://github.com/MartinZikmund/Uno.WindowsCommunityToolkit/blob/uno/Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs

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

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

CommunityToolkit.Mvvm:目前使用中,一发布就是版本8。两大特性RelayCommand和Messenger。延续了MVVMLight的风格。所有可用的 API 都不依赖于任何特定的运行时或框架,因此所有 .NET 开发人员都可以使用它们。这些库是从 .NET Standard 2.0 到 .NET 6 的多目标库,所以它们既可以支持尽可能多的平台,又可以在与较新的运行时一起使用时进行优化以获得最佳性能。主要应该代码生成器的功能简化代码(CommunityToolkit工具包提供了一系列可重用实现,旨在简化各种 .NET 框架的常见开发任务。),应用大量的特性来简化代码编写。详细请查看

Nuget:  https://www.nuget.org/packages/CommunityToolkit.Mvvm源码:https://github.com/CommunityToolkit/MVVM-Samples

Prism8.0:应用与大型的应用程序

Toolkit.Mvvm工具包简介

未来方便理解mvvm的原理这里主要介绍Microsoft.Toolkit.Mvvm,虽然他已经过时了。CommunityToolkit.Mvvm 详细请查看

1、ObservableObject的使用

在MvvmLight框架下,只要在ViewModel继承ViewModelBase即可,在Microsoft.Toolkit.Mvvm中正常继承ObservableObject即可,同时还有ObservableRecipient、ObservableValidator等可被选择,其实在MvvmLight框架下,ViewModelBase也是继承了ObservableObject,相当于官方的这个框架更加的灵活,在不同的场景下继承不同的类,以实现不同的效果,这也跟.Net Core很像,一切都更加原子化,模块化。
ObservableObject:实现了最基本的属性更新:SetProperty 等
ObservableRecipient:继承自ObservableObject,增加了Messenger的相关功能
ObservableValidator:这个就更不用说了,看名字就知道是用于验证属性的

public class MainWindowViewModel : ObservableObject
{
        public MainWindowViewModel()
        {
            Status = "Hello";
        }
        private string _status;

        public string Status
        {
            get => _status;
            set => SetProperty(ref _status, value);
        }
}

前端绑定属性就行了

<TextBlock Margin="10" Text="{Binding Status}" />

2、IRelayCommand和IAsyncRelayCommand

  • RelayCommand 和 RelayCommand<T>:唯一的作用就是充当view 和viewmodel方法之间连接器。
  • AsyncRelayCommand 和 AsyncRelayCommand<T>

RelayCommand 和 RelayCommand<T> 工作原理

RelayCommand和RelayCommand的主要特性如下:
它们提供了ICommand接口的基本实现。
它们还实现了IRelayCommand(和IRelayCommand)接口,该接口公开了一个NotifyCanExecuteChanged方法来引发CanExecuteChanged事件。
它们公开了接受Action和Func等委托的构造函数,这些委托允许包装标准方法和lambda表达式。

 应用一 、同步应用

ViewModel.cs
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>

 应用二、异步应用

要实现按钮的点击事件进行绑定,都需要使用继承自ICommand的接口,IRelayCommand和IAsyncRelayCommand也是如此,同时增加了NotifyCanExecuteChanged来通知ICommand.CanExecute属性发生了改变。
这里以IAsyncRelayCommand为例来实现一个进度条的功能,

public class MainWindowViewModel : ObservableObject
{
        public MainWindowViewModel()
        {
            Status = "Hello";
            ExecCommand = new AsyncRelayCommand(ExecAsync);
        }
        private string _status;

        public string Status
        {
            get => _status;
            set => SetProperty(ref _status, value);
        }
        private int _progressValue;

        public int ProgressValue
        {
            get => _progressValue;
            set => SetProperty(ref _progressValue, value);
        }
        public ICommand ExecCommand { get; }
        private async Task ExecAsync()
        {
            Status = "Processing...";

            await Task.Run(async () =>
            {
                for (int i = 0; i <= 100; i++)
                {
                    ProgressValue = i;

                    await Task.Delay(100);
                }
            });
            Status = "Complete";
        }
}

前端如下

<StackPanel x:Name="sp1">
                <Button Margin="10" Command="{Binding ExecCommand}" Content="Button" />
                <TextBlock Margin="10" Text="{Binding Status}" />
                <ProgressBar Margin="10" Value="{Binding ProgressValue}" Minimum="0" Maximum="100" />
            </StackPanel>

 

编程是个人爱好