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 数据源,并且每个 DataGrid 行中都有一个按钮;
我们可能希望点击按钮时传递当前行对应的 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 中显式定义命令属性。源代码生成器会自动为你生成所需的命令属性,从而大大简化了代码。这种方法特别适合于大型项目或者需要频繁创建命令的场景。

posted @ 2024-12-30 12:25  青云Zeo  阅读(56)  评论(0编辑  收藏  举报