【问题标题】:How to use the CommandManager and still be able to trigger the ICommand.CanExecuteChanged event manually i.e. explicitely?如何使用 CommandManager 并且仍然能够手动触发 ICommand.CanExecuteChanged 事件,即显式地?
【发布时间】:2022-03-10 07:35:35
【问题描述】:

我给自己写了一个 SingleExecutionCommand(我还不确定这个名字。请随意建议另一个,但这不是我的问题),它不允许在第一次执行之前再次按下按钮完成的。好吧,至少这是计划。
我想使用CommandManager 来处理我的CanExecuteChanged-Event,但我也需要在我更改ExecutionIsRunning-Flag 时自行触发此事件。

这是我的命令代码:

public class SingleExecutionCommand<T> : DelegateCommand<T>
{

    protected readonly Func<T, Task> AwaitableExecuteDelegate;

    public bool ExecutionIsRunning { get; protected set; }

    public SingleExecutionCommand(Func<T, Task> awaitableExecute, Predicate<T> canExecute = null) :
        base(null, canExecute)
    {
        AwaitableExecuteDelegate = awaitableExecute;
    }

    public SingleExecutionCommand(Action<T> execute, Predicate<T> canExecute = null)
        : base(execute, canExecute)
    {
        AwaitableExecuteDelegate = null;
    }        

    public async override void Execute(object parameter)
    {
        if (parameter != null && !(parameter is T)) throw new ArgumentException("Command Parameter has the wrong type.");

        if (AwaitableExecuteDelegate == null)
        {
            ExecutionIsRunning = true;
            base.Execute(parameter);
            ExecutionIsRunning = false;
        }
        else
        {
            ExecutionIsRunning = true;
            await AwaitableExecuteDelegate((T)parameter);
            ExecutionIsRunning = false;
        }
    }

    public override bool CanExecute(object parameter)
    {
        return (!ExecutionIsRunning) && base.CanExecute(parameter);
    }
}

如果在我的DelegateCommand&lt;T&gt;-Class 中尝试这样做:

protected void FireCanExcuteChangedEvent()
{
    if (CanExecuteChanged == null) return;

    CanExecuteChanged(this, EventArgs.Empty);
}

但这不起作用,因为该事件是一个重新路由的事件(到CommandManager.RequerySuggested),而不是一个真实的事件。那么,如何同时使用CommandManager 和我自己的事件?

编辑:
CanExecuteChanged-事件如下所示:

public event EventHandler CanExecuteChanged
{
    add
    {
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        CommandManager.RequerySuggested -= value;
    }
}

【问题讨论】:

    标签: c# wpf asynchronous c#-5.0 commandbinding


    【解决方案1】:

    由于处理程序被分配/委托给CommandManager.RequerySuggested 事件,我建议调用CommandManager.InvalidateRequerySuggested()。 这将引发CanExecuteChanged 事件以表明有关命令有效性的条件已更改。现在,所有侦听器都将重新评估他们是否可以通过响应CanExecute() 调用来执行命令。如果结果是false,命令源通常会被禁用。

    您的固定FireCanExcuteChangedEvent() 如下所示:

    protected virtual void FireCanExcuteChangedEvent()
    {
      CommandManager.InvalidateRequerySuggested();
    }
    

    请注意,这将在全局范围内引发CanExecuteCHanged 事件,即在每个ICommand 实例上(其中CanExecuteCHanged 事件调用委托给CommandManager)。如果性能是一个问题,请考虑为每个实例显式提高 CanExecuteChanged(参见下面的示例)。

    改进的实现将引入一个专用的内部事件委托。除了将CommandManager.RequerySuggested 事件委托给CanExecuteChanegd 订阅者之外,此委托还将累积CanExecuteChanegd 订阅者的事件处理程序。
    这样您就有两种选择:

    • 根据全局 CommandManager.RequerySuggested 事件通知事件观察者
    • 根据CanExecuteChanged(独立于CommandManager.RequerySuggested)的本地即实例事件版本通知。

    以下示例还允许禁用基于CommandManager 的CanExecuteChanged 事件调用。
    这样,命令必须通过调用公共InvalidateCommand()方法显式失效:

    public class RelayCommand : ICommand
    {   
      // Optional feature:
      // Allow to bypass the CommandManager completely 
      // to use explicit command invalidation only (via the public InvalidateCommand method).
      // Set from constructor.
      public bool IsCommandManagerInvalidateRequeryEnabled { get; }
    
      // Store CanExecuteChanged event handlers 
      // to invoke them independent from the CommandManager
      private EventHandler canExecuteChangedDelegate;
    
      public RelayCommand(bool isCommandManagerInvalidateRequeryEnabled)
      {
        this.IsCommandManagerInvalidateRequeryEnabled = isCommandManagerInvalidateRequeryEnabled ;
      }
    
      // Invalidate this command explicitly.
      // Raises the ICommand.CanExecuteChanged event of this instance.
      public void InvalidateCommand() => OnCanExecuteChanged();
    
      /// Raises the ICommand.CanExecuteChanged event.
      protected virtual void OnCanExecuteChanged() 
        => this.canExecuteChangedDelegate?.Invoke(this, EventArgs.Empty);
    
      // Explicit event implementation
      public event EventHandler CanExecuteChanged
      {
        add
        {
          if (this.IsCommandManagerInvalidateRequeryEnabled)
          {
            // Delegate the CanExecuteChanged event invocation to the CommandManager
            CommandManager.RequerySuggested += value;
          }
    
          // Accumulate handlers on a local delegate 
          // to allow raising the CanExecuteChanged event independent of the CommandManager
          this.canExecuteChangedDelegate += value;
        }
        remove
        {
          CommandManager.RequerySuggested -= value;
          this.canExecuteChangedDelegate -= value;
        }
      }
    }
    

    【讨论】:

    • 现在我觉得有点愚蠢……我查看了CommandManager,但不知何故我错过了这个方法。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 2019-09-07
    • 2014-08-04
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    相关资源
    最近更新 更多