首页 > 其他分享 >WPF 使用 CommunityToolkit.Mvvm

WPF 使用 CommunityToolkit.Mvvm

时间:2023-11-08 13:31:35浏览次数:45  
标签:CommunityToolkit partial Mvvm class MainWindowViewModel user WPF public User

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

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

NuGet安装包

搜索:CommunityToolkit.Mvvm

WPF 使用 CommunityToolkit.Mvvm_User

导入

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


标签:CommunityToolkit,partial,Mvvm,class,MainWindowViewModel,user,WPF,public,User
From: https://blog.51cto.com/u_12072082/8250128

相关文章

  • WPF中的Binding的常见知识点与技巧
    在XAML中,可以绑定到许多不同类型的数据源和属性。以下是一些可以绑定的常见数据源和属性:属性:可以绑定到对象的属性,例如控件的Text、Visibility、IsEnabled等属性。集合:可以绑定到集合数据,如List、ObservableCollection、Array等。在绑定到集合时,还可以使用索引器绑定到特定项。静态......
  • WPF多UI线程
       internalclassSpashWindowManager{privatestaticSpashWindow_spashWindow;privatestaticThreadthread;publicstaticvoidShow(){thread=newThread(()=>{_spashW......
  • [WPF]浅析资源引用(pack URI)
    WPF中我们引用资源时常常提到一个概念:packURI,这是WPF标识和引用资源最常见的方式,但不是唯一的方式。本文将介绍WPF中引用资源的几种方式,并回顾一下packURI标识引用在不同位置的资源文件的写法。WPF中引用资源的几种方式WPF中使用URI标识和加载位于各种位置的文件,包括当前程序......
  • WPF仿VS TreeView
    [TemplatePart(Name="PART_Content",Type=typeof(ToggleButton))][TemplatePart(Name="Expander",Type=typeof(Panel))]publicclassOTreeViewItem:TreeViewItem{Panel?partContent;ToggleButton?pa......
  • WPF开发的小巧、美观桌面快捷工具GeekDesk开源项目--极客桌面
    今天给大家推荐一个基于WPF开发的,专门为程序员定制的桌面快捷工具。项目简介这是基于.Net+WPF开发的,一个小巧、UI美观的快捷工具。此项目发布以来就受到大家的喜欢,代码结构清晰非常适合用来学习。项目还在持续迭代中,有部分小问题,用来学习、体验完全没问题。作者一直在迭代升级......
  • 界面控件DevExpress WPF PDF Viewer,更快实现应用的PDF文档浏览
    DevExpressWPFPDFViewer控件可以轻松地直接在Windows应用程序中显示PDF文档,而无需在最终用户的机器上安装外部PDF查看器。P.S:DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应......
  • wpf 记一次诡异的PreviewMouseLeftButtonDown 无法触发问题
    1、原始代码<Grid><i:Interaction.Triggers><i:EventTriggerEventName="PreviewMouseLeftButtonDown">......
  • 深入理解WPF中的依赖注入和控制反转
    在WPF开发中,依赖注入(DependencyInjection)和控制反转(InversionofControl)是程序解耦的关键,在当今软件工程中占有举足轻重的地位,两者之间有着密不可分的联系。今天就以一个简单的小例子,简述如何在WPF中实现依赖注入和控制反转,仅供学习分享使用,如有不足之处,还请指正。 什么是依......
  • WPF InputHitTest的用法
    一、定义InputHitTest是一个用于检测鼠标指针是否命中某个可视元素的方法。它可以返回命中的最上层元素。 二、用法:①首先,我们需要获取InputHitTest方法所需的参数,即鼠标指针的位置。可以通过Mouse类的GetPosition方法来获取当前鼠标指针的位置:Pointposition=Mouse.Ge......
  • MVVM 和 MVC 区别是什么?
    1、基本定义MVVM基本定义MVVM即Model-View-ViewModel的简写,即模型-视图-视图模型,模型(Model)指的是后端传递的数据,视图(View)指的是所看到的页面,视图模型(ViewModel)是mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:一是将模型(Model)转化成视图(View),即将后......