参考文档: Introduction to the MVVM Toolkit - Community Toolkits for .NET | Microsoft Learn
它是一个现代化,快速和模块化的MVVM库, 对应用程序的结构或编译规范没有严格的限制。
NuGet安装包
搜索:CommunityToolkit.Mvvm

导入
using CommunityToolkit.Mvvm;使用
ObservableObject
public abstract class ObservableObject :
INotifyPropertyChanged, 
INotifyPropertyChanging{
}它实现了两个接口,它可以作为需要支持属性更改通知的所有类的基类,主要有以下功能:
- 实现INotifyPropertyChanged 和 INotifyPropertyChanging公开了PropertyChanged 和 PropertyChanging事件
- 提供了一系列的SetProperty方法可以很容易的用来设置属性值,并自动引发相应的事件
- 它提供了SetPropertyAndNotifyOnCompletion方法,该方法类似于SetProperty,但是能够设置Task属性,并在分配的任务完成时自动引发通知事件
- 公开了OnPropertyChanged和OnPropertyChanging方法,这些方法可以在派生类型中重写,以自定义引发通知事件的方式
简单属性
class MainWindowViewModel : ObservableObject
{
    {
        get { return name; }
        set => SetProperty(ref name, value);
    }
} protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
 {
     if (EqualityComparer<T>.Default.Equals(field, newValue))
     {
         return false;
     }
     OnPropertyChanging(propertyName);
     field = newValue;
     OnPropertyChanged(propertyName);
     return true;
 }SetProperty方法内部,设置属性值,并触发两个事件。这两个事件除了给WPF内部框架使用外,我们也可以监听这些事件实现自己的功能。
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            var mainWindowViewModel = new MainWindowViewModel();
            mainWindowViewModel.PropertyChanged += MainWindowViewModel_PropertyChanged;
            mainWindowViewModel.PropertyChanging += MainWindowViewModel_PropertyChanging;
            DataContext = mainWindowViewModel;
            InitializeComponent();
        }
        private void MainWindowViewModel_PropertyChanging(object? sender, System.ComponentModel.PropertyChangingEventArgs e)
        {
            Console.WriteLine(e.PropertyName + " changing");
        }
        private void MainWindowViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Console.WriteLine(e.PropertyName + " changed");
        }
    }包装Model对象
Model对象一般从数据库或其它地方获取,不会继承ObservableObject
public class User
{
    public String Name { get; set; } = "";
}
 class MainWindowViewModel : ObservableObject
 {
    public MainWindowViewModel(User user)
    {
        this.user = user;
     }
     public string Name
     {
         get => user.Name;
         set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
     }
}使用特性自动生成代码
 internal partial class MainWindowViewModel : ObservableObject
 {
     [ObservableProperty]
     String title = "hello";
 }必须是partial class, 因为在后台会生成代码对 title 属性进行包装
 partial class MainWindowViewModel
 {
    public string Title
     {
         get => title;
         [global::System.Diagnostics.CodeAnalysis.MemberNotNull("title")]
         set
         {
             if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(title, value))
             {
                 OnTitleChanging(value);
                 OnTitleChanging(default, value);
                 OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Title);
                 title = value;
                 OnTitleChanged(value);
                 OnTitleChanged(default, value);
                 OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Title);
             }
         }
     }
 }
...
 partial void OnTitleChanging(string value);
...关于 partial 函数的说明:partial method - C# Reference - C# | Microsoft Learn
在一个 partial class 代码中声明函数partial void OnTitleChanging(string value);在另一个partial class 代码中实现该函数,如果没有实现,则在编译时将会删除相关的声明和调用。
RelayCommand
命令转播,将方法或委托转换成命令提供给View, 主要有以下功能:
- 实现了 ICommand 接口
- 实现 IRelayCommand(和 IRelayCommand<T>)接口, 暴露了NotifyCanExecuteChanged方法用于触发CanExecuteChanged事件
- 它们公开了接受Action和Func<T>等委托的构造函数,这些委托允许包装标准方法和lambda表达式
internal partial class MainWindowViewModel : ObservableObject
{
    [ObservableProperty]
    int age = 18;
    public RelayCommand<String> CommandAddAge => new RelayCommand<string>((v) =>
    {
        Age += int.Parse(v);
    });
}<Button Width="120" Height="48" Margin="0 300 0 0" 
        Command="{Binding CommandAddAge}" 
        CommandParameter="5"
        >AddAge</Button>IOC
工作原理:先将一个类型或对象注册到一指定容器中,在需要时可以通过这个容器获取(或构造对象)。
需要安装Microsoft.Extensions.DependencyInjection, 详细信息请看Dependency injection in ASP.NET Core | Microsoft Learn
配置和注册类型
    
 public class User
 {
     public String Name { get; set; } = "";
 }
 internal partial class MainWindowViewModel : ObservableObject
 {
     [ObservableProperty]
     String title = "hello";
     private readonly User user;
     public MainWindowViewModel(User user)
     {
         this.user = user;
     }
     public string Name
     {
         get => user.Name;
         set => SetProperty(user.Name, value, user, (u, n) => u.Name = n);
     }
     [ObservableProperty]
     int age = 18;
     public RelayCommand<String> CommandAddAge => new RelayCommand<string>((v) =>
     {
         Age += int.Parse(v);
     });
 }
 
public partial class App : Application
    {
        public IServiceProvider Services { get; }
        public new static App Current => (App)Application.Current;
        public App()
        {
            Services = ConfigureServices();
            this.InitializeComponent();
        }
        private IServiceProvider ConfigureServices()
        {
            var services = new ServiceCollection();
            services.AddTransient<User>();
            services.AddTransient<MainWindowViewModel>();
            return services.BuildServiceProvider();
        }
    }
    // 通过Services 获取对象
     var mainWindowViewModel = App.Current.Services.GetService<MainWindowViewModel>();Messenger
IMessenger 是两个不同对象间传递消息的接口。实现IMessenger的类型负责维护接收者(消息接收者)与其注册的消息类型之间的链接,以及相关的消息处理程序。
有两种方法注册消息:
- IReceiver <TMessage>
- MessageHandler< receiver, TMessage>
发送和接收消息
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
    public LoggedInUserChangedMessage(User user) : base(user)
    {
    }
}
// 注册
public MainWindowViewModel(User user)
{
    this.user = user;
    WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
    {
        Console.WriteLine(r.GetType()); // 接收器,也就是this
        Console.WriteLine(m.GetType()); // 消息
    });
}
// 发送
 private void Button_Click(object sender, RoutedEventArgs e)
 {
     WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(new User()));
 }请求消息
// Create a message
public class LoggedInUserRequestMessage : RequestMessage<User>
{
}
// Register the receiver in a module
WeakReferenceMessenger.Default.Register<MyViewModel, LoggedInUserRequestMessage>(this, (r, m) =>
{
    // Assume that "CurrentUser" is a private member in our viewmodel.
    // As before, we're accessing it through the recipient passed as
    // input to the handler, to avoid capturing "this" in the delegate.
    m.Reply(r.CurrentUser);
});
// Request the value from another module
User user = WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();









