CommunityToolkit.Mvvm 包(又名 MVVM 工具包,以前称为 Microsoft.Toolkit.Mvvm)是一个现代、快速和模块化的 MVVM 库。 它是 .NET Community Toolkit 的一部分,并围绕以下原则构建:
- 独立于平台和运行时 - .NET Standard 2.0、.NET Standard 2.1 和 .NET 6 (与 UI 框架无关)
- 易于上手和使用 — 对应用程序结构或编码范例(“MVVM”之外)没有严格要求,即灵活使用。
- 可选 - 自由选择要使用的组件。
- 引用实现 - 精益和高性能,为基类库中包含的接口提供实现,但缺乏直接使用它们的具体类型。
可观察对象 / ObservableObject
在 WPF 中, 写出 MVVM 设计模式的程序, 自然需要进行前台与后台数据的绑定, 而在原生实现中, 编写一个可绑定的类, 并且编写该类中可绑定的成员, 是非常麻烦的一件事情。 你需要手动在值变更的时候, 引发 “PropertyChanged” 事件, 大概的代码会像这样:
public class MainViewModel : INotifyPropertyChanged
{
private string windowTitle = "Toturial App";
public string WindowTitle
{
get => windowTitle;
set
{
windowTitle = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
像这样, 每一个属性都需要编写一个对应的字段, 然后手动编写 getter, setter, 并在 setter 中进行属性变更的通知,显然, 非常麻烦。
在 CommunityToolkit.Mvvm 中, 提供了用于生成这些重复性代码的源生成器, 我们只需要简单的在需要的地方加上一些特性, 就可以实现这些复杂的东西了。 例如, 上述代码在 CommunityToolkit.Mvvm 中, 代码是这样的:
[ObservableObject]
public partial class MainViewModel
{
[ObservableProperty]
private string windowTitle = "Toturial App";
}
为所需的 ViewModel 添加 ObservableObject 特性, 利用分布类, 他会自动生成该类型对 INotifyPropertyChanged 的实现。 而打上了 ObservableProperty 特性的字段, 源生成器会自动生成该字段名称转换为大驼峰之后的一个属性, 并在属性定义中包含对属性变更的通知逻辑。
简单来讲, 我们不需要写那么复杂的逻辑了。 只需要为可观察的类型打上 ObservableObject, 为可观察的属性打上 ObservableProperty, 其余的, 都会自动生成。
RelayCommand / 中继指令
同样的, 在原生 WPF 中, 想要定义一个指令, 也是非常麻烦的一件事。 你需要为指令专门写一个类, 并实现 ICommand 接口。 在这个接口中分别包含了判断指令是否能够执行,执行指令的方法,以及一个用于通知 “指令可执行性变更” 的事件。
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
// 这里写判断命令是否可执行的逻辑
}
public void Execute(object parameter)
{
// 这里是命令执行的逻辑
}
// 这里是用于通知指令可执行性变更的事件
public event EventHandler? CanExecuteChanged;
}
但是很多时候, 我们的指令只是一些简单的逻辑, 为一个简单逻辑编写一个类, 未免有些太麻烦了。 于是 CommunityToolkit.Mvvm 中为我们提供了一个特性:RelayCommand。
当为一个方法打上 RelayCommand 标记的时候, CommunityToolkit.Mvvm 会在该方法所在的类下生成一个对应的 Command, 你可以在界面上直接绑定到这个 Command, 而不需要自己手动编写一个 Command 类。
public partial class MainWindow
{
public MainWindow()
{
// 将绑定上下文设为自身
DataContext = this;
InitalizeComponents();
}
// 这里会生成一个 ShowMessageCommand 属性
[RelayCommand]
public void ShowMessage()
{
MessageBox.Show("Hello world!");
}
}
<Window ...>
<Grid>
<StackPanel>
<!--直接绑定到后台的 ShowMessageCommand-->
<Button Content="Click Me" Command="{Binding ShowMessageCommand}"/>
</StackPanel>
</Grid>
</Window>