跳转至

中继指令(RelayCommand)

WPF 等框架中,如果希望为一个按钮控件绑定一个命令,正确的做法是为它的 Command 属性绑定一个 ICommand 接口类型的对象。但是,ICommand 接口的实现类通常需要自己实现 CanExecuteExecute 方法,这样会导致代码冗余。为了解决这个问题,于是有了“中继指令”(RelayCommand)这个概念。

Note

“中继指令”这个概念并没有一个官方的名称。常见的名称有 RelayCommandDelegateCommand 等。在 ReactiveUI 中,它的名称为 ReactiveCommand

目前主流的方式为使用一个类,并将实现使用方法委托传给这个类。但也有其他的方式,比如 SingletonSean 在他的 MVVM 相关的教学视频中为每个中继指令都单独写了一个实现。

ICommand 接口的定义如下:

public interface ICommand
{
    bool CanExecute(object parameter);
    void Execute(object parameter);
    event EventHandler CanExecuteChanged;
}

但是工具包并不满足于此,而是又给出了几个额外的接口,包括:

  • IRelayCommand
  • IRelayCommand<T>
  • IAsyncRelayCommand
  • IAsyncRelayCommand<T>

其中,泛型版本为表示该中继指令可以传入一个参数,通常也就是按钮等控件的 CommandParameter 属性。

IRelayCommand 接口

IRelayCommandIRelayCommmand<T> 接口的定义分别为:

public interface IRelayCommand : ICommand
{
    void NotifyCanExecuteChanged();
}

public interface IRelayCommand<in T> : IRelayCommand
{
    bool CanExecute(T? parameter);

    void Execute(T? parameter);
}

它在 ICommand 的基础上增加了一个 NotifyCanExecuteChanged 方法,用于通知命令的可执行状态发生了变化。这样,我们就可以更便捷地实现这一功能了。比如我们可以在某个属性的 setter 中调用这个方法,这样当属性值发生变化时,命令的可执行状态也会及时得到更新。

Info

IRelayCommand<in T> 这里的 in 表示 泛型逆变,这样我们就可以将一个 IRelayCommand<object> 赋值给一个 IRelayCommand<string> 类型的变量。

IAsyncRelayCommand

这个接口的定义为:

public interface IAsyncRelayCommand : IRelayCommand, INotifyPropertyChanged
{
    Task? ExecutionTask { get; }

    bool CanBeCanceled { get; }

    bool IsCancellationRequested { get; }

    bool IsRunning { get; }

    Task ExecuteAsync(object? parameter);

    void Cancel();
}

它在 IRelayCommand 的基础上增加了一些异步操作相关的属性和方法,比如 ExecutionTask 属性用于获取当前的执行任务,Cancel 方法用于取消当前的执行任务,IsRunning 属性用于判断当前异步任务是否正在执行等。


工具包当然不可能只为我们提供了一些接口,它还为我们提供了一些实现类,甚至还提供了非常好用的 源生成器。关于这些内容,我们将在后续的文章中进行介绍。

评论