【WPF】Mvvm框架Toolkit.Mvvm 、MvvmLight、Prism8.0比较
前言
本文基于.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 机器翻译的有问题,不容易理解
个人的:https://blog.csdn.net/BadAyase/article/details/125112080 容易理解
备注
我发现这个术语在WPF和Silverl社区更为流行。
与MVP中的Presenter不同,ViewModel不需要实现对View的引用。View将属性绑定到ViewModel中,反向的,ViewModel暴露的属性包含在Model对象和View中特殊的状态。View和ViewModel之间的绑定的构建非常简单只需将ViewModel对象被作为View的上下文(DataContext)来设置。
MVVM Toolkit版本
自定义Mvvm框架:方便自己理解mvvm框架。
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取代。
Nuget:https://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>