在 DataGrid 中进行数据绑定时,CommandParameter 未传递给 CanExecute(CanExecute 参数为空)

CommandParameter not passed to CanExecute when databound within a DataGrid (CanExecute parameter is null)

我正在使用 Microsoft.Toolkit.Mvvm nuget 包在 .NET Core 3.1 上构建 WPF 应用程序。

我在将按钮的 CommandParameter 绑定到 Datagrid 的 DataGridTemplateColumn 中时遇到问题。

我使用 IRelayCommand 在我的视图模型上定义了一个 RelayCommand,并且定义了一个带有参数的 CanExecute 方法。然后我将按钮绑定的 CommandParameter 绑定到数据网格行的数据。

在运行时,我看到我的数据网格的每一行都调用了 CanExecute 方法,但参数值始终为空。如果我的 ObservableCollection 中有 5 个项目,我会看到 CanExecute 被调用了 5 次,3 个项目...... 3 次。在所有情况下,该值为空。

下面是我的视图模型的代码,它创建了一个 TestModel 类型的 ObservableCollection。 RelayCommand 被创建,CanExecute 的标准简化为只比较数据绑定模型的名称 属性。

我在这里期望的是,对于除名称为“Test2”的行之外的所有行,绑定按钮都将启用。 Test2 行中的按钮将被禁用。

这是我的视图模型和相关模型 class。

public class MainWindowVM : ObservableObject, IViewModel
{
    public MainWindowVM()
    {
        TestData = new ObservableCollection<TestModel>
        {
            new TestModel() {Name = "Test1"},
            new TestModel() {Name = "Test2"},
            new TestModel() {Name = "Test3"}
        };

        DeleteCommand = new RelayCommand<TestModel>(Delete, CanDelete);
    }

    public IRelayCommand<TestModel> DeleteCommand { get; }

    private void Delete(TestModel model)
    {
        //do stuff
    }

    private bool CanDelete(TestModel model)
    {
        if (model.Name == "Test2")
        {
            return false;
        }
        return true;
    }

    public ObservableCollection<TestModel> TestData
    {
        get => _testData;
        set => SetProperty(ref _testData, value);
    }
    private ObservableCollection<TestModel> _testData;
}

public class TestModel : ObservableObject
{
    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }
    private string _name;
}

还有我的 xaml Datagrid

<Grid>
    <DataGrid ItemsSource="{Binding Path=TestData}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Username"
                                Binding="{Binding Path=Name}"
                                MinWidth="180" />

            <DataGridTemplateColumn Header="" Width="160">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">

                            <Button Command="{Binding Path=DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                                    CommandParameter="{Binding}"  Content="Delete"
                                    Width="100"   >
                            </Button> 
                        </StackPanel>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

我在这里错过了什么? 我在其他 WPF 应用程序中无数次使用过这种方法。这是我第一次使用这个工具包(我以前使用过 MVVMLight),也是第一次在 .NET Core 上构建 WPF。 这是 .NET Core 上 WPF 的变化吗?两个 MVVM 工具包之间有什么不同吗?

如果您能提供任何帮助或指导,我们将不胜感激,谢谢。

What am I missing here?

在设置 CommandParameter 之前调用 CanExecute 的事实。

更改对此的绑定似乎对我有用:

CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"

另请注意,除非您将 DataGridCanUserAddRows 属性 设置为 false,否则您的 CanDelete 也将使用 MS.Internal.NamedObject 这将导致应用程序崩溃,除非您更改 RelayCommand:

的类型参数
public class MainWindowVM : ObservableObject
{
    public MainWindowVM()
    {
        TestData = new ObservableCollection<TestModel>
    {
        new TestModel() {Name = "Test1"},
        new TestModel() {Name = "Test2"},
        new TestModel() {Name = "Test3"}
    };

        DeleteCommand = new RelayCommand<object>(Delete, CanDelete);
    }

    public IRelayCommand<object> DeleteCommand { get; }

    private void Delete(object parameter)
    {
        if (parameter is TestModel model)
        {
            //do stuff
        }
    }

    private bool CanDelete(object parameter)
    {
        if (parameter is TestModel model && model.Name == "Test2")
        {
            return false;
        }
        return true;
    }

    public ObservableCollection<TestModel> TestData
    {
        get => _testData;
        set => SetProperty(ref _testData, value);
    }
    private ObservableCollection<TestModel> _testData;
}