tnblog
首页
视频
资源
登录

WPF CommunityToolkit.Mvvm初探

516人阅读 2025/4/15 13:11 总访问:3659284 评论:0 收藏:0 手机
分类: .net后台框架

.netcore

WPF CommunityToolkit.Mvvm初探

什么是 CommunityToolkit.Mvvm?


CommunityToolkit.Mvvm 是一个现代化的 MVVM 框架,用于 .NET 应用程序。它提供了一系列工具和特性,帮助开发者更高效地实现 MVVM 模式,减少样板代码,并提升代码的可维护性。

为什么选择 CommunityToolkit.Mvvm?


简化 MVVM 实现:自动实现 INotifyPropertyChanged 接口,减少样板代码。
跨平台支持:适用于 WPF、UWP、MAUI 等多种 .NET 应用。
功能丰富:提供命令处理、消息传递、数据验证等功能。
社区支持:不断更新和改进,确保工具包的稳定性和易用性

安装 CommunityToolkit.Mvvm


通过执行如下命令可以进行安装。

  1. Install-Package CommunityToolkit.Mvvm
  2. Install-Package MvvmLightLibs


ObservableObject 是一个基类,用于实现 INotifyPropertyChangedINotifyPropertyChanging 接口,从而支持属性更改通知。
AsyncRelayCommand 是一个 ICommand 实现,扩展了 RelayCommand 的功能,支持异步操作。
接下来我们的定义一个MainViewModel类,创建一个按钮的命令,让它暂停3秒后输出"Hello CommunityToolkit",内容如下:

  1. public class MainViewModel:ObservableObject
  2. {
  3. public MainViewModel()
  4. {
  5. BtnCommand = new AsyncRelayCommand(DoCommand);
  6. }
  7. private async Task<string> DoCommand()
  8. {
  9. await Task.Delay(3000);
  10. return "Hello CommunityToolkit";
  11. }
  12. public ICommand BtnCommand { get; }
  13. }


然后我们进行IOC注册,在ViewModelLocator类中编辑如下内容:

  1. public class ViewModelLocator
  2. {
  3. public ViewModelLocator()
  4. {
  5. ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  6. SimpleIoc.Default.Register<MainViewModel>();
  7. }
  8. public MainViewModel MainViewModel { get => ServiceLocator.Current.GetInstance<MainViewModel>(); }
  9. }


另外我们添加一个Task的扩展类,用于在数据传输的时候进行转换。

  1. public static class TaskExtensions
  2. {
  3. [Pure]
  4. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  5. public static object? GetResultOrDefault(this Task task)
  6. {
  7. // Check if the instance is a completed Task
  8. if (
  9. #if NETSTANDARD2_1
  10. task.IsCompletedSuccessfully
  11. #else
  12. task.Status == TaskStatus.RanToCompletion
  13. #endif
  14. )
  15. {
  16. if (task != Task.CompletedTask)
  17. {
  18. PropertyInfo? propertyInfo =
  19. #if NETSTANDARD1_4
  20. task.GetType().GetRuntimeProperty(nameof(Task<object>.Result));
  21. #else
  22. task.GetType().GetProperty(nameof(Task<object>.Result));
  23. #endif
  24. // Return the result, if possible
  25. return propertyInfo?.GetValue(task);
  26. }
  27. }
  28. return null;
  29. }
  30. [Pure]
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. public static T? GetResultOrDefault<T>(this Task<T?> task)
  33. {
  34. #if NETSTANDARD2_1
  35. return task.IsCompletedSuccessfully ? task.Result : default;
  36. #else
  37. return task.Status == TaskStatus.RanToCompletion ? task.Result : default;
  38. #endif
  39. }
  40. }


然后我们定义一个Task转换类型TaskResultConverter类。

  1. public class TaskResultConverter : IValueConverter
  2. {
  3. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  4. {
  5. if (value is Task task)
  6. {
  7. return task.GetResultOrDefault();
  8. }
  9. return null;
  10. }
  11. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  12. {
  13. throw new NotImplementedException();
  14. }
  15. }


接下来我们编辑MainWindow.xaml代码,对其进行绑定。

  1. <Window x:Class="Learning_mvvmlight.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:localmodel="clr-namespace:Learning_mvvmlight.ViewModel"
  7. xmlns:local="clr-namespace:Learning_mvvmlight"
  8. mc:Ignorable="d"
  9. Title="MainWindow" Height="450" Width="800">
  10. <Window.DataContext>
  11. <localmodel:MainViewModel/>
  12. </Window.DataContext>
  13. <Window.Resources>
  14. <local:TaskResultConverter x:Key="TaskResultConverter"/>
  15. </Window.Resources>
  16. <Grid>
  17. <StackPanel>
  18. <!-- 绑定 TextBlock 的 Text 属性到 BtnCommand.ExecutionTask,通过 TaskResultConverter 转换显示结果 -->
  19. <TextBlock Text="{Binding BtnCommand.ExecutionTask,Converter={StaticResource TaskResultConverter}}"/>
  20. <!-- 绑定 TextBlock 的 Text 属性到 BtnCommand.ExecutionTask 的 Status 属性,直接显示任务状态 -->
  21. <TextBlock Text="{Binding BtnCommand.ExecutionTask.Status}"/>
  22. <!-- 将 Button 的 Command 属性绑定到 BtnCommand,点击按钮时触发 BtnCommand 对应的命令逻辑 -->
  23. <Button Command="{Binding BtnCommand}" Content="Button"/>
  24. </StackPanel>
  25. </Grid>
  26. </Window>


开始点击3秒前的Task状态是:WaitingForActivation


点击了3秒后的Task状态:RanToCompletion,并输出如下内容:

消息传递

什么是 WeakReferenceMessenger?


WeakReferenceMessenger 是一个基于弱引用的消息传递系统,它允许在应用程序的不同部分之间进行松耦合的通信。与传统的强引用不同,弱引用不会阻止对象被垃圾回收器回收,这使得 WeakReferenceMessenger 能有效避免内存泄漏问题,尤其在 MVVM 架构中显得尤为重要

举例


首先我们使用简单的string类型进行传输,然后通过消息框进行弹出。
首先在MainViewModel添加一个新的ICommand对象取名为BtnMsgCommand,进行发送一个Hello的消息。

  1. public ICommand BtnMsgCommand {
  2. get=>new RelayCommand(() =>
  3. {
  4. WeakReferenceMessenger.Default.Send<string>("Hello");
  5. });
  6. }


然后在MainWindow中注册该方法,并进行弹出一个消息框进行处理.

  1. public partial class MainWindow : Window
  2. {
  3. public MainWindow()
  4. {
  5. InitializeComponent();
  6. WeakReferenceMessenger.Default.Register<string>(this,DoSomThing);
  7. }
  8. /// <summary>
  9. /// 处理消息
  10. /// </summary>
  11. /// <param name="recipient">这里是当前控件</param>
  12. /// <param name="message">这是消息</param>
  13. private void DoSomThing(object recipient, string message)
  14. {
  15. MessageBox.Show(message);
  16. }
  17. }


然后我们在前端页面可以添加一个新的按钮进行绑定命令。

  1. <Button Command="{Binding BtnMsgCommand}" Content="Msg Button"/>

ValueChangedMessage


ValueChangedMessage<T> 是一个通用类,用于表示某个值已更改的消息。
它通常与WeakReferenceMessenger一起使用,以便在应用程序的不同组件之间传递数据变化的通知。
首先我们定义一个MessageObject用于实现消息传输的类。

  1. public class MessageObject:ValueChangedMessage<string>
  2. {
  3. public MessageObject(string message)
  4. : base(message)
  5. {
  6. }
  7. }


修改一下BtnMsgCommand,发送的时候我们将发送一个MessageObject对象。

  1. public ICommand BtnMsgCommand {
  2. get=>new RelayCommand(() =>
  3. {
  4. WeakReferenceMessenger.Default.Send<MessageObject>(new MessageObject("Hello"));
  5. });
  6. }


然后在窗体后台进行增重。

  1. public partial class MainWindow : Window
  2. {
  3. public MainWindow()
  4. {
  5. InitializeComponent();
  6. WeakReferenceMessenger.Default.Register<string>(this,DoSomThing);
  7. WeakReferenceMessenger.Default.Register<MessageObject>(this, DoSomThing);
  8. }
  9. private void DoSomThing(object recipient, MessageObject message)
  10. {
  11. MessageBox.Show("MessageObject:" + message.Value);
  12. }
  13. /// <summary>
  14. /// 处理消息
  15. /// </summary>
  16. /// <param name="recipient">这里是当前控件</param>
  17. /// <param name="message">这是消息</param>
  18. private void DoSomThing(object recipient, string message)
  19. {
  20. MessageBox.Show(message);
  21. }
  22. }

IRecipient


IRecipient<TMessage> 是一个泛型接口,用于标识一个对象可以接收特定类型的消息。
在使用 WeakReferenceMessenger 时,如果一个类实现了 IRecipient<TMessage> 接口,那么它就可以接收所有通过 WeakReferenceMessenger 发送的 TMessage 类型的消息。
修改MainViewModel中的实现:

  1. public class MainViewModel:ObservableRecipient,IRecipient<string>
  2. {
  3. public MainViewModel()
  4. {
  5. BtnCommand = new AsyncRelayCommand(DoCommand);
  6. // 消息开关
  7. this.IsActive = true;
  8. }
  9. private async Task<string> DoCommand()
  10. {
  11. await Task.Delay(3000);
  12. return "Hello CommunityToolkit";
  13. }
  14. public void Receive(string message)
  15. {
  16. }
  17. public ICommand BtnCommand { get; }
  18. public ICommand BtnMsgCommand {
  19. get=>new RelayCommand(() =>
  20. {
  21. WeakReferenceMessenger.Default.Send<string>("Hello");
  22. //WeakReferenceMessenger.Default.Send<MessageObject>(new MessageObject("Hello"));
  23. });
  24. }
  25. }


当我们进行发送string类型的参数的方法时候,Receive方法会先接收到,然后再到达前端窗体。

替换IOC容器


在 MVVM 架构模式下,依赖注入(IoC)容器是实现解耦和管理对象生命周期的关键组件。MvvmLight 是一个广受欢迎的 MVVM 框架,它提供了自己的 IoC 容器 SimpleIoc,但随着 .NET 生态系统的发展,Microsoft.Extensions.DependencyInjection(.NET Core 中的依赖注入框架)凭借其简洁性和灵活性,逐渐成为许多开发者的首选。

代码示例

  1. public class ViewModelLocator
  2. {
  3. public static IServiceProvider ServiceProvider { get; set; }
  4. public ViewModelLocator()
  5. {
  6. //ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  7. //SimpleIoc.Default.Register<MainViewModel>();
  8. ServiceProvider = GetService();
  9. }
  10. private IServiceProvider GetService()
  11. {
  12. var service = new ServiceCollection();
  13. service.AddSingleton<MainViewModel>();
  14. return service.BuildServiceProvider();
  15. }
  16. //public MainViewModel MainViewModel { get => ServiceLocator.Current.GetInstance<MainViewModel>(); }
  17. public MainViewModel MainViewModel { get => ServiceProvider.GetService<MainViewModel>(); }
  18. }

欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价

初探k8s

概念补充Deployment 是在 Pod 这个抽象上更为上层的一个抽象层,它可以定义一组 Pod 的副本数量,以及这个 Pod 的版本。一...

初探Argo

初探Argo[TOC] 什么是 Argo CD? Argo CD 是 Kubernetes 的声明性 GitOps 持续交付工具。应用程序定义、配置和环境应该是...

BGP RR路由反射与Calico的初探

BGP RR路由反射与Calico的初探[TOC] BGP RR路由反射介绍由于IBGP水平分割的存在,为了保证所有的BGP路由器都能学习到完整...

Flannel CNI初探

Flannel CNI初探[TOC] Flannel 简介Flannel是由CoreOs提出的跨主通信容器网络解决方案,通过分配和管理全局唯一容器IP以及...

python 初探神经网络(一元一次函数)学习笔记

python 初探神经网络(一元一次函数)学习笔记[TOC] B站学习课程 豆豆实验有个小蓝的生物,想吃豆豆,豆豆越大豆豆的毒性...

Outlook WebAddin初探

Outlook WebAddin初探[TOC] 安装前提首先,确保你的计算机上已安装Node.js和npm。你可以在官方网站下载并安装它们。 VS C...

HugginFace 初探

HugginFace 初探[TOC] 安装环境python环境是3.6。import sys sys.version 安装torch,简单起见,避免环境问题,并且计...

Pytorch 初探

Pytorch 初探[TOC] Pytorch简介PyTorch由 Facebook 的 AI 研究团队开发的一个开源的机器学习库,它提供了张量(tensor)计...

Cmake 初探

Cmake 初探[TOC] CMake简介CMake 是一个开源的、跨平台的构建系统(编译工具),它主要用于管理项目的构建过程。它通过使用...

WPF Mvvmlight初探

WPF Mvvmlight初探[TOC] 什么是 Mvvmlight?MVVM Light 是一个轻量级的 MVVM 框架,适用于 WPF、UWP、Xamarin 等多个平台...
这一世以无限游戏为使命!
排名
7
文章
192
粉丝
15
评论
16
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术