异步命令实现

10

当我尝试在默认情况下禁用asny ICommand实现中的命令(即使没有传递CanExecute谓词时),我遇到了奇怪的行为。

public bool CanExecute(object parameter)
{
  if (CanExecutePredicate == null)
  {
    return !mIsExecuting;
  }

  return !mIsExecuting && CanExecutePredicate(parameter);
}

public async void Execute(object parameter)
{
  mIsExecuting = true;
  await ExecuteAsync(parameter);
  mIsExecuting = false;
}

我尝试引入一个私有的布尔值,我在执行前将其设置为true,在执行后将其设置为false。当执行完成时,该布尔值被设置,但是CanExecute只有在我点击鼠标按钮或移动鼠标等操作后才会被调用。

现在我尝试调用

CanExecute(null);

之后

mIsExecuting = false;

但这也没有帮助。我不知道我缺少了什么。
谢谢你的帮助。
编辑:
为了澄清,我也添加了这个类的构造函数:
 public AsyncRelayCommand(Func<object, Task> execute)
  : this(execute, null)
{
}

public AsyncRelayCommand(Func<object, Task> asyncExecute,
               Predicate<object> canExecutePredicate)
{
  AsyncExecute = asyncExecute;
  CanExecutePredicate = canExecutePredicate;
}

protected virtual async Task ExecuteAsync(object parameter)
{
  await AsyncExecute(parameter);
}

“Execute” 在哪里发挥作用? - BytesOfMetal
@user3292642 抱歉,我不清楚你的问题是什么,我只能看到可能存在 CanExecutePredicate 的竞态条件。 - Alessandro D'Andria
如果您在不同的线程之间共享“CanExecute Predicate”,您可能会发现即使通过了“if”语句,“CanExecutePredicate”仍然为空,但这不是您的问题。您是否在等待对“Execute”的调用? - Alessandro D'Andria
@AlessandroD'Andria 我修改了我的帖子。谓词要么传递给我的命令构造函数,要么为 null。使用这个命令的人可以决定是否要等待它。 - user3292642
你可以查看这个链接,了解另一种方法:https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/AsyncCommand.cs - Fabio
显示剩余3条评论
1个回答

11

在异步场景下,WPF 倾向于不知道何时检查 CanExecute,这就是为什么 ICommand 接口中有“CanExecuteChanged”事件的原因。

您应该在命令实现中编写以下代码:

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

    remove { CommandManager.RequerySuggested -= value; }
}

public void RaiseCanExecuteChanged()
{
    CommandManager.InvalidateRequerySuggested();
}

使用上述代码,现在您可以这样做:

public async void Execute(object parameter)
{
    mIsExecuting = true;

    RaiseCanExecuteChanged ( ); // Not necessary if Execute is not called locally

    await ExecuteAsync(parameter);
    mIsExecuting = false;

    RaiseCanExecuteChanged ( );
}

这将告诉 WPF 您希望刷新命令的 CanExecute 状态。


2
不是与问题相关,但为什么要在这里使用CommandManager - Fabio
1
它用于使wpf了解您的具体命令,并告诉它在需要时刷新所有CanExecute。这是框架Prism使用的,尽管我目前发现它有点笨重。这里有一个更好的解释:https://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/ - Seb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接