WPF 使用 CommunityToolkit.Mvvm
  JWGppPCsR1pT 2023年11月09日 61 0

参考文档: Introduction to the MVVM Toolkit - Community Toolkits for .NET | Microsoft Learn

它是一个现代化,快速和模块化的MVVM库, 对应用程序的结构或编译规范没有严格的限制。

NuGet安装包

搜索:CommunityToolkit.Mvvm

WPF 使用 CommunityToolkit.Mvvm_App

导入

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>();
MainWindowViewModel需要一个User, User类型已被注册到容器中,构造MainWindowViewModel时会先创建User对象,然后再将User传入构造函数。

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>();


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月09日 0

暂无评论

推荐阅读
JWGppPCsR1pT