WPF MVVM Community Toolkit. Mvvm 社区框架_communitytoolkit.mvvm
Community Toolkit. Mvvm 社区框架
微软官方文档
主要内容:CommunityToolkit.Mvvm 框架
- 概念,安装,使用(重要API:ObservableObject,RelayCommand)
- 源生成器([ObservableProperty],[RelayCommand],[INotifyPropertyChanged])
- 命令详解(异步命令:AsyncRelayCommand 和 AsyncRelayCommand)
- 依赖注入(IServiceProvider,ServiceCollection)
- 信使(IMessenger,WeakReferenceMessenger、StrongReferenceMessenger 等)
ViewModel 支持通知:继承 ObservableObject
属性支持通知:SetProperty(ref name, value) 或 OnPropertyChanged( )
创建命令:直接使用类型 RelayCommand
核心功能
Community Toolkit. Mvvm 内置源生成器可以使用特性生成代码
1. [INotifyPropertyChanged] 和 [ObservableProperty]
ObservableObject
是 ViewModel 的基类,支持 INotifyPropertyChanged
和 INotifyPropertyChanged
要 .Net 8.0 版本以后才可以使用源生成器这种语法
[INotifyPropertyChanged] //相当于 类继承 :ObservableObjectpublic partial class PersonViewModel //🔴!需要 partial 部分类{ [ObservableProperty] //[ObservableProperty] 会生成一个: 基于 name的字段的属性Name private string name; //🔴调用时,要调:Name 大写// 相当于: public string Name// {// get => name;// set => SetProperty(ref name, value);// } [ObservableProperty] private int age;}
这段代码会自动生成 Name 和 Age 的属性,同时自动实现
INotifyPropertyChanged
的通知。
SetProperty(ref name, value):支持通知
SetProperty(ref name, value) //通知的方法,相当于: // ⬇⬇⬇ 相当于if (_name != value){ _name = value; OnPropertyChanged(); // 或 OnPropertyChanged(nameof(Name));}
2. RelayCommand 命令
你可以轻松创建命令而不需要手动实现 ICommand
接口。
using CommunityToolkit.Mvvm.Input;[INotifyPropertyChanged]public partial class PersonViewModel //🔴需要 partial 部分类{ 1️⃣//同步命令 [RelayCommand] //在编译时会自动在方法后面加:Command private void SayHello() //🔴调用时要调用:SayHelloCommand { MessageBox.Show(\"Hello!\"); } 2️⃣//异步命令 [RelayCommand] private async Task GreetUserAsync() //🔴编译时会变成:GreetUserCommand { await Task.Delay(3000); MessageBox.Show($\"Hello 源生器!\"); } 3️⃣//带 CanExecute 命令 [RelayCommand(CanExecute = nameof(CanGreetUser))] //加入 CanExecute 判断执行 private void SayHello() { MessageBox.Show(\"Hello!\"); } private bool CanGreetUser(User? user) { return user is not null; } }
使用 [RelayCommand] 会自动生成一个 SayHelloCommand 属性,供 XAML 使用。
异步方法会 去掉 Async ,添加 Command ,变成 SayHelloCommand
3. ObservableValidator 验证
如果你需要验证输入数据(比如表单),可以继承 ObservableValidator
:
public partial class FormViewModel : ObservableValidator{ [ObservableProperty] [Required] [MinLength(3)] private string name;}
XAML 示例绑定命令
<Button Content=\"Say Hello\" Command=\"{Binding SayHelloCommand}\" /><TextBox Text=\"{Binding Name, UpdateSourceTrigger=PropertyChanged}\" />
信使
“信使”,在 MVVM 架构中指的是 Messenger(消息通信机制),用于实现 ViewModel 之间的解耦通信。
MVVM 工具包提供两种现用的实现:
- WeakReferenceMessenger
- 在内部使用弱引用,为收件人提供自动内存管理
- StrongReferenceMessenger
- 使用强引用,并要求开发人员在不再需要收件人时手动取消订阅收件人
使用场景
- 不同 ViewModel 之间发送和接收消息
- 让 ViewModel 通知其他模块某件事发生了
- 解耦:不通过事件或引用直接交互
示例:发送与接收消息
1. 创建消息类(推荐继承 ValueChangedMessage
)
public class NameChangedMessage : ValueChangedMessage<string>{ public NameChangedMessage(string value) : base(value) { }}
2. 接收消息的 ViewModel
public class ReceiverViewModel : ObservableRecipient{ public ReceiverViewModel() { // 必须开启 IsActive 才能自动注册接收器 IsActive = true; 1️⃣ Messenger.Register<ReceiverViewModel, NameChangedMessage>(this, (r, m) => { // 处理消息 //在这里处理消息,r为接收方,m为发送方 //输入消息。使用传递的接收者作为输入使得 // lambda表达式不会捕获“this”,从而提高性能。 Console.WriteLine($\"接收到新名字:{m.Value}\"); }); }}2️⃣ //不继承ObservableRecipient 的方案WeakReferenceMessenger.Default.Register<ReceiverViewModel>(this, (r, m) =>{});
Messenger 基类自带的属性,
不继承 ObservableRecipient 也可以直接用 WeakReferenceMessenger.Default.Register
3. 发送消息的 ViewModel
public class SenderViewModel{ public void SendMessage() { WeakReferenceMessenger.Default.Send(new NameChangedMessage(\"新名字张三\")); }}
使用 DI 注入 IMessenger
(可选)
你也可以将 Messenger 注入进来,而不是使用静态默认实例:
public class SenderViewModel{ private readonly IMessenger _messenger; public SenderViewModel(IMessenger messenger) { _messenger = messenger; } public void SendMessage() { _messenger.Send(new NameChangedMessage(\"李四\")); }}
在 App.xaml.cs
中注册:
services.AddSingleton<IMessenger>(WeakReferenceMessenger.Default);
注销收件人
当不再需要收件人时,应将其注销,以便停止接收消息。 可以按消息类型、注册令牌或收件人取消注册:
//取消对消息类型的接收者的注册WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage>(this);//取消指定通道中消息类型的接收者的注册WeakReferenceMessenger.Default.Unregister<LoggedInUserChangedMessage, int>(this, 42);//取消所有通道中所有消息的接收方注册WeakReferenceMessenger.Default.UnregisterAll(this);
ObservableRecipient
IsActive = true
WeakReferenceMessenger
Register
Send
总结:
Messenger.Register
和 Send
IMessenger
IServiceProvider 服务提供者接口
IServiceProvider:服务提供者接口
在 WPF + MVVM 应用中,IServiceProvider
常用于实现 依赖注入(DI),尤其是在使用 CommunityToolkit.Mvvm
配合 .NET Core/5+/6+ 的 WPF 应用时,这个接口是核心组成之一。
什么是 IServiceProvider:
- IServiceProvider服务器提供者接口,主要给VM提供各种服务(数据库操作服务,日志服务,定位服务等)
- IServiceProvider接口中只有一个GetService()方法,用来获取服务
- 它通常与
Microsoft.Extensions.DependencyInjection
一起使用,用于解析依赖项,比如 ViewModel、服务类等。
使用示例
需要安装 Microsoft.Extensions.DependencyInjection 包
后引用:using Microsoft.Extensions.DependencyInjection;
通用依赖注入容器库,可以:
- 注册服务:
services.AddSingleton()
/AddTransient()
- 构建服务提供器:
services.BuildServiceProvider()
- 获取服务:
provider.GetRequiredService()
- 与 MVVM 模式完美配合
1. 注册依赖(Startup)
你需要在 App.xaml.cs 中设置依赖注入容器:
using Microsoft.Extensions.DependencyInjection;public partial class App : Application{ public App() { Services = ConfigureServices(); this.InitializeComponent(); } //获取当前应用程序实例并转换为App类型 //在全项目范围通过 App.Current 安全访问自定义的 App 对象 public new static App Current => (App)Application.Current; public IServiceProvider Services { get; } //用来接收 服务提供器 的字段 // 配置服务 public static IServiceProvider ConfigureServices() { //1️⃣ 定义一个服务集合,服务集合编译之后,产生一个ServiceProvider // 而ServiceProvider类型实现了IServiceProvider接口 var services = new ServiceCollection(); //2️⃣ 注册 ViewModel 和服务 // services.AddTransient(); // Transient 瞬间模式; // Singleton 单例模式 services.AddSingleton<MainWindowViewModel>(); services.AddSingleton<AnimalWindowViewModel>(); // 注册服务 //services.AddSingleton(); //AddSingleton 使用时会自动创建一个唯一的实现实例,注入后直接调用接口就可以使用其实现类实现的方法 services.AddSingleton<IMyService, MyService>(); services.AddSingleton<IPerson<Person,ViewPerson>, PersonService>(); services.AddSingleton<IAnimal<Animal,ViewAnimal>, AnimalService>(); //3️⃣ 构建服务提供器 services.BuildServiceProvider() 并返回 return services.BuildServiceProvider(); }}
-
你注册的时候:
✅//这表示 MainViewModel 需要的服务(IMyService)会自动注入。services.AddSingleton<IMyService, MyService>();services.AddSingleton<MainViewModel>();
-
在上面代码中:
✅//是 WPF 应用中为了更方便地获取当前应用程序实例的一种做法。public new static App Current => (App)Application.Current;
-
Application.Current
是一个静态属性,返回当前运行的 WPF 应用实例,其类型Application
。 -
你自定义的
App
类一般是继承自Application
的 -
为了能够在程序其他地方方便地访问你自定义的
App
类型,而不是基类Application
,你需要做一次类型转换。
-
2. 如:在构造函数中使用依赖注入
比如在 MainViewModel 中注入一个服务:
public class MainViewModel : ObservableObject{ private readonly IMyService _service; public MainViewModel(IMyService service) { _service = service; }}
3. 获取服务
你可以随时通过 App.Services.GetService() 获取已注册的对象:
var vm = App.Services.GetRequiredService<MainViewModel>();
//1. 如: App.xaml.cs 中启动主窗口var mainWindow = Services.GetRequiredService<MainWindow>();mainWindow.Show();
//2. 如:View 构造函数中注入 ViewModelpublic partial class MainWindow : Window{ public MainWindow(MainViewModel viewModel) { InitializeComponent(); DataContext = viewModel; }}
//3. ViewModel 构造函数中注入服务public class MainViewModel : ObservableObject{ private readonly IMyService _myService; public MainViewModel(IMyService myService) { _myService = myService; } // 使用 _myService 来执行操作}
//4. 绑定上下文// MainWindowViewModel视图模型和MainWindow窗体耦合了。//this.DataContext = new MainWindowViewModel();// 依赖注入,但没有完全解耦。//this.DataContext = App.Current.Services.GetService(typeof(MainWindowViewModel));// 完全解耦了Type type = Type.GetType(\"_1.CommunityToolkit.Mvvm基本使用.ViewModels.MainWindowViewModel\");this.DataContext = App.Current.Services.GetService(type);//this.DataContext = new MainWindowViewModel(new PersonService(), new AnimalService());
搭配 MVVM Toolkit 的建议:
虽然 CommunityToolkit.Mvvm
本身没有内建 DI 容器,但它 完全支持 DI 的设计理念。只要你配合 .NET Generic Host
或 Microsoft.Extensions.DependencyInjection
,就能构建清晰的 MVVM 架构。
- 注册
IServiceProvider
- 自动注入 ViewModel
- 使用 RelayCommand 和 ObservableProperty
📁 附:推荐的文件夹命名约定
推荐的文件夹命名结构(基于 MVVM 模式):
MyWpfApp/├── Models/ # 业务模型、数据结构类├── ViewModels/ # 视图模型(含 ObservableObject / RelayCommand 等)├── Views/ # XAML 视图文件(*.xaml + *.xaml.cs)├── Services/ # 服务类(如导航、API 调用、本地存储等)├── Converters/ # IValueConverter 实现├── Behaviors/ # 附加行为(例如交互逻辑)├── Helpers/ # 帮助类、扩展方法├── Resources/ # 资源文件(样式、模板、图片等)└── App.xaml # 应用入口
各文件夹用途说明:
User.cs
、Order.cs
等MainViewModel.cs
MainView.xaml
和 MainView.xaml.cs
INavigationService
, IDataService
等BoolToVisibilityConverter.cs
Styles.xaml
, Themes
, 图片等资源