ICommand 实现静默失败

ICommand implemtation fails silently

更新

我尝试使用的按钮位于 <DataTemplate> 内,这显然导致了问题。一旦我在 <ItemsControl> 区域外的按钮上尝试了代码,它就可以工作。谁能告诉我,为什么它在像 <ItemsControl><DataTemplate> 这样的重复按钮中不起作用?

我正在尝试根据 TutorialsPoints.com 中的一篇文章实现 MVVM 通信模式。我稍微修改了代码,但总的来说它仍然与文章中的代码非常相似。我想要做的是,单击按钮后在控制台中写一行。

通过我的实现(见下面的代码),当我点击按钮时没有任何反应。我也试过在OnClick()函数中加一个断点,看看是不是运行,这个不是。但是 MyICommand() 的构造函数中的断点显示 class 实际上已初始化。那我做错了什么?

按钮

<Button Content="Do stuff!"
        Command="{Binding FakeCommand}"
        Cursor="Hand"
        Background="Red" 
        Foreground="White" 
        BorderThickness="0" 
        Padding="10 0 10 0"  />

视图模型

public class AgreementViewModel : INotifyPropertyChanged
{
    public MyICommand FakeCommand { get; set; }

    public AgreementViewModel ()
    {
        LoadAgreements();
        FakeCommand = new MyICommand(OnClick, CanClick);
        FakeCommand.RaiseCanExecuteChanged();
    }

    private void OnClick()
    {
        Console.WriteLine("Something was clicked...");
    }

    private bool CanClick()
    {
        return true;
    }
}

ICommand的实现

public class MyICommand : ICommand
{
    Action _TargetExecuteMethod;
    Func<bool> _TargetCanExecuteMethod;

    public event EventHandler CanExecuteChanged = delegate {};

    public MyICommand(Action executeMethod)
    {
        _TargetExecuteMethod = executeMethod;
    }

    public MyICommand(Action executeMethod, Func<bool> canExecuteMethod)
    {
        _TargetExecuteMethod = executeMethod;
        _TargetCanExecuteMethod = canExecuteMethod;
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged(this, EventArgs.Empty);
    }

    bool ICommand.CanExecute(object parameter)
    {
        if (_TargetCanExecuteMethod != null)
        {
            return _TargetCanExecuteMethod();
        }

        if (_TargetExecuteMethod != null)
        {
            return true;
        }

        return false;
    }

    void ICommand.Execute(object parameter)
    {
        _TargetExecuteMethod?.Invoke();
    }
}

CanExecuteExecute 方法中删除 ICommand.

如果您有一个 ItemsControl(正如您在更新版本中提到的那样),那么 DataTemplate 的每个实例化的 DataContext 将在ItemsSource。要绑定到父视图模型中的命令,您可以使用 ElementName 获取 ItemsControl

    <ItemsControl ItemsSource="{Binding Data}" x:Name="root">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="Do stuff!"
                    Command="{Binding DataContext.FakeCommand,  ElementName=root}"
                    Cursor="Hand"
                    Background="Red" 
                    Foreground="White" 
                    BorderThickness="0" 
                    Padding="10 0 10 0"  />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

如果您不想使用名称,另一种方法是使用 RelativeSource 访问项目控件:

Command="{Binding DataContext.FakeCommand,  RelativeSource={RelativeSource AncestorType=ItemsControl}}"

请注意,在这两种情况下,数据上下文都是ItemsControl,因此您需要做DataContext.FakeCommandDataContext在这里指的是ItemsControl的数据上下文

您可能还需要为其调用命令的项目,因为它可以为源集合中的任何项目调用。为此,您可以添加一个 CommandParameter={Binding},传递给命令的参数将是项目(您的实现不会将参数传递给委托,但它可以)