WPF笔记13——CommunityToolKit.Mvvm
1、[ObservableProperty]标记
private字段上有 [ObservableProperty]标记,CommunityToolkit.Mvvm会自动给它生成一个对应的public属性,并在属性值改变时自动触发属性变更通知。
2、[ObservableObject]标记
ObservableObject类型实现了实现了INotifyPropertyChanged和INotifyPropertyChanging接口,这使得对象的属性变化可以被观察到,从而实现数据绑定和通知机制。
3、[RelayCommand]标记
在 CommunityToolkit.Mvvm 包中,RelayCommand 是一个实现了 ICommand 接口的类,用于在 MVVM 模式中处理命令。RelayCommand 允许你将一个方法绑定到一个按钮或其他触发器上,当触发器被激活时,这个方法就会被调用。RelayCommand 还支持可执行性的检查,即在某些条件下命令可以被执行。
手写mvvm代码:
public partial class MyViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get{return _name;}
set{
_name=value;
this.RaiseNotifiedChanged(nameof(Name));
}
}
//DeletegateCommand是一个继承ICommand类的方法
public DeletegateCommand MyCommand { get; }
private void ExecuteCommand()
{
// 命令执行的逻辑
}
private bool CanExecuteCommand()
{
// 命令是否可以执行的逻辑
return true;
}
public event PropertyChangedEventHandler? PropertyChanged;
public void RaiseNotifiedChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MyViewModel ()
{
MyCommand = new DeletegateCommand(ExecuteCommand, ExecuteCommand);
}
}
使用CommunityToolkit.Mvvm.ComponentModel包后的代码
using CommunityToolkit.Mvvm.ComponentModel;
public partial class MyViewModel : ObservableObject
{
[ObservableProperty]
private string _name;
public ICommand MyCommand { get; }
public MyViewModel()
{
MyCommand = new RelayCommand(ExecuteCommand, CanExecuteCommand);
}
private void ExecuteCommand()
{
// 命令执行的逻辑
}
private bool CanExecuteCommand()
{
// 命令是否可以执行的逻辑
return true;
}
}
在上述代码中,CommunityToolkit.Mvvm会在编译时生成以下等效代码:
using CommunityToolkit.Mvvm.ComponentModel;
public partial class MyViewModel : ObservableObject
{
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
[RelayCommand] //[RelayCommand] 特性直接在方法上,这样可以自动生成一个以方法名加 Command 后缀的 RelayCommand
private void ExecuteCommand()
{
// 命令执行的逻辑
}
}
补充:
1、使用 [ObservableProperty] 和 [NotifyCanExecuteChangedFor] 特性
对于需要根据某个属性的值来决定命令是否可执行的情况,可以使用 [NotifyCanExecuteChangedFor] 特性。
这个特性允许我们指定一个命令,当指定的属性值变化时,会自动通知命令的 CanExecute 方法重新评估。
例如下面代码,当IsEnable属性发生变化时,ButtonClickCommand 的可执行性会自动更新
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ButtonClickCommand))]
private bool isEnable;
[RelayCommand(CanExecute = nameof(CanButtonClick))]
private void ButtonClick()
{
// 命令执行的逻辑
}
private bool CanButtonClick() => IsEnable;
2、异步版本的RelayCommand
如果我们需要执行异步操作,可以使用异步版本的RelayCommand。
这样,我们可以在命令执行的方法中使用await,而不用担心阻塞UI线程。
例如下代码,允许我们在命令中执行异步操作,同时保持UI的响应性
[RelayCommand(CanExecute = nameof(CanButtonClick))]
private async Task ButtonClick()
{
await Task.Delay(1500);
// 命令执行的逻辑
}
3、命令添加参数
3.1、使用 [RelayCommand] 和 [RelayCommandParameter] 特性:
我们可以使用 [RelayCommand] 特性来自动创建命令,并使用 [RelayCommandParameter] 特性来指定命令的参数。
using CommunityToolkit.Mvvm.Input;
public class MyViewModel : ObservableObject
{
[RelayCommand]
[RelayCommandParameter]
private void ExecuteCommand(string parameter)
{
// 使用参数执行命令
Console.WriteLine($"Command executed with parameter: {parameter}");
}
}
//在xaml中绑定命令
//s:RelayCommand 标记扩展来绑定命令,这是 CommunityToolkit.Mvvm 源代码生成器提供的功能。
<Button Command="{s:RelayCommand ExecuteCommand}"
CommandParameter="Hello World"
Content="Click Me" />
**注意,配置 Source Generators: 确保我们的项目文件(.csproj)中包含了启用源代码生成器的配置。**
<PropertyGroup>
<EnableSourceLink>false</EnableSourceLink>
<EnableSourceControlManagerQueries>false</EnableSourceControlManagerQueries>
</PropertyGroup>
3.2、如果Button在DataGrid中,要传递的是DataGrid中对应行的对象
例如:
有一个 DataGrid 绑定到 List
我们可能希望点击按钮时传递当前行对应的 Student 对象到事件处理方法。
以下是实现这一功能的步骤:
step1、定义按钮的命令:
在 ViewModel 中,可以定义一个接受 Student 参数的命令。
public class MyViewModel : ObservableObject
{
public ICommand StudentSelectedCommand { get; }
public MyViewModel()
{
StudentSelectedCommand = new RelayCommand<Student>(OnStudentSelected);
}
private void OnStudentSelected(Student student)
{
// 这里处理学生对象
}
}
step2、在 XAML 中绑定命令:
在 DataGrid 的按钮列中,可以使用 Button 的 Command 属性来绑定 ViewModel 中的命令,并使用 CommandParameter 来传递当前行的 Student 对象。
<DataGrid ItemsSource="{Binding Students}">
<!-- 其他列定义 -->
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.StudentSelectedCommand}"
CommandParameter="{Binding}">
Click Me
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid>
step3、使用 RelativeSource 找到 ViewModel:
RelativeSource 用于找到当前按钮的祖先 DataGrid,然后通过 Path=DataContext 找到 ViewModel。这是因为 Button 本身的 DataContext 是当前行的 Student 对象,而我们需要命令的 DataContext 是 ViewModel。
step4、在 ViewModel 中处理命令:
当按钮被点击时,OnStudentSelected 方法会被调用,并且当前行的 Student 对象作为参数传递
这个过程还可以简化写成下面这样:
step1、定义 ViewModel:
在我们的 ViewModel 中,使用 [RelayCommand] 特性来定义一个命令。
using CommunityToolkit.Mvvm.Input;
public class MyViewModel : ObservableObject
{
[RelayCommand]
private void OnStudentSelected(Student student)
{
// 这里处理学生对象
}
}
step2、在 XAML 中绑定命令:
在 DataGrid 的按钮列中,使用 s:RelayCommand 来绑定命令,并使用 DataGrid 的 DataContext 来传递当前行的 Student 对象。
s:RelayCommand 是一个标记扩展,它会自动为你创建一个 RelayCommand 实例,并绑定到 ViewModel 中的方法。ViewModel=DataContext 指定了命令的 ViewModel,而 s:RelayCommandParameter="{Binding}" 传递了当前行的 DataContext(即 Student 对象)作为命令的参数。
<DataGrid ItemsSource="{Binding Students}">
<!-- 其他列定义 -->
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Click Me"
s:RelayCommand="{s:RelayCommand OnStudentSelected, ViewModel=DataContext}"
s:RelayCommandParameter="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid>
step3、配置 Source Generators:
确保我们的项目文件(.csproj)中包含了启用源代码生成器的配置。
<PropertyGroup>
<EnableSourceLink>false</EnableSourceLink>
<EnableSourceControlManagerQueries>false</EnableSourceControlManagerQueries>
</PropertyGroup>
使用 CommunityToolkit.Mvvm 包后,我们不需要手动创建 RelayCommand 实例,也不需要在 ViewModel 中显式定义命令属性。源代码生成器会自动为你生成所需的命令属性,从而大大简化了代码。这种方法特别适合于大型项目或者需要频繁创建命令的场景。