CommunityToolkit.Mvvm
(MVVM 工具包
)是微软把之前的Microsoft.Toolkit.Mvvm
复活后的一个项目,最早的时候还在研究ReactiveX
,不过在发现微软复活了这个老项目,并且还有源生成器
的这种新特性后,直接转投研究MVVM 工具包
了
代码
先来个简单的例子演示一下这个包的几个小功能
比如我现在又个小项目,下面是它的部分源码
MainWindow.xaml
的Window
里,只添加一个StackPanel
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding Text}" />
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
新建个MainWindowModel.cs
拿来放要被绑定的数据
internal class MainWindowModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private string _text = "Hello World!";
public string Text
{
get => _text;
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
MainWindow.xaml.cs
的InitializeComponent();
下面加一行绑定
this.DataContext = new MainWindowModel();
实现了什么功能
这一小段代码,简单的将一个TextBlock
和TextBox
的Text
属性的值,都绑定到了Text
这个变量上面:
TextBox
的Text
属性的值被更改Text
被赋予新的值,调用set
访问器set
访问器在更新_text
值后,触发PropertyChanged.Invoke
TextBlock
的Text
属性的值被更新
在开始之前,首先要在
nuget
里装上CommunityToolkit.Mvvm
这个包
要使用这个功能,我们先要把MainWindowModel
类改成分布类,只需要在声明的地方加个partial
前缀就好
[INotifyPropertyChanged]
internal partial class MainWindowModel
在最上方,引用一下CommunityToolkit.Mvvm
的类
//using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
ObservableProperty自动生成可观察属性
我们可以直接用ObservableProperty
,于是MainWindowModel
类的代码变成了这样:
[INotifyPropertyChanged]
internal partial class MainWindowModel
{
[ObservableProperty]
private string _text = "Hello World!";
}
这里的源生成器会自动按大驼峰命名规范来转换你的变量名,如
lowerCamel
、 、_lowerCamel
或m_lowerCamel
都会被转换为UpperCamel
,并且将被声明为public
以便使用
可以看到代码量大大减少了,当代码编译时,会自动编译成下面的样子(来自ILSpy
的反编译结果,我精简掉了很多代码,以便大家可以清晰看懂):
internal class MainWindowModel : INotifyPropertyChanged
{
private string _text = "Hello World!";
public string Text
{
get
{
return _text;
}
set
{
if (!EqualityComparer<string>.Default.Equals(_text, value))
{
_text = value;
OnPropertyChanged(__KnownINotifyPropertyChangedArgs.Text);
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
this.PropertyChanged?.Invoke(this, e);
}
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
可以看到,会精简掉很大部分的代码,让逻辑更清晰,同时减轻工作量
依赖其他值的属性
比如我们在MainWindowModel
类添加另一个属性OtherText
:
public string OtherText
{
get
{
return "Other:" + _text;
}
}
然后让TextBlock
的绑定更改为它:
<TextBlock Text="{Binding OtherText}" />
这时我们知道,当Text
变化后,OtherText
也应该变化,那么如何触发OtherText
的更新呢?
我们可以使用NotifyPropertyChangedFor
,像这样:
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(OtherText))]
private string _text = "Hello World!";
这样写之后,当Text
变化后,会通知OtherText
也变化了,从而实现及时的更改:
绑定命令
MVVM 工具包
的源生成器同样可以绑定命令,比如我们在MainWindowModel
类添加一个函数Hello
,使用RelayCommand
即可简便的实现该功能
[RelayCommand]
private void Hello()
{
Text = "Hello!";
}
RelayCommand
会自动生成一个名为HelloCommand
的函数用于绑定,我们可以在StackPanel
内添加一个Button
来测试这个功能
<Button Command="{Binding HelloCommand}" Content="123" />
点击按钮后,可以看到功能正常
绑定异步命令
同时我们绑定的命令也可以是异步的,可以把函数Hello
改成下面这样,继续测试
[RelayCommand]
private async void Hello()
{
for (int i = 1; i <= 10; i++)
{
Text = $"wait {i}";
await Task.Delay(500);
}
}
这样写不会卡住UI线程,效果如下:
其他功能
其他还有很多功能,这里就不一一列举了,大家可以前往MVVM 工具包官方文档查看