wpf 取消异步中继命令

waxmsbnn  于 2022-11-18  发布在  其他
关注(0)|答案(1)|浏览(148)

这是WPF(.NET Framework 4.8)。我需要在UI中提供两个<Button>来启动和停止长时间运行的后台进程。启动按钮最初应该是启用的,并且在发出命令后将被禁用(以避免第二次单击)。停止按钮最初将被禁用,并且在进程开始运行后将被启用。
我使用Windows Community Toolkit's MVVM library中提供的AsyncRelayCommand。按钮通过普通的XAML绑定绑定到它们的VM。底层命令如下所示:

private AsyncRelayCommand _StartPumpingCommand;
public AsyncRelayCommand StartPumpingCommand
{
  get
  {
    _StartPumpingCommand ??= new AsyncRelayCommand(async (CancellationToken token) =>
      {
        dispatcher.Invoke(() => _CancelPumpingCommand.NotifyCanExecuteChanged());
        await SomeTask(token);
      },
      () => {
        /* SomeTask is not running and a cancellation has not been requested  */
        return !_StartPumpingCommand.IsRunning && !_StartPumpingCommand.IsCancellationRequested;
      });

    return _StartPumpingCommand;
  }
}

private RelayCommand _CancelPumpingCommand;
public RelayCommand CancelPumpingCommand
{
  get
  {
    _CancelPumpingCommand ??= new RelayCommand(() =>
      {
        if(_StartPumpingCommand?.IsRunning??false)
          _StartPumpingCommand.Cancel();
      },
      () => _StartPumpingCommand?.IsRunning??false);

    return _CancelPumpingCommand;
  }
}

问题:
1.我对这两个命令的实现是否正确?

  1. AsyncRelayCommand内部是否包含CancellationTokenSource magic,或者我是否需要自己在VM类中创建一个并在上面的两个命令中使用它?
    1.如何将StartPumpingCommand的完成通知给CancelPumpingCommand,以便UI中的按钮相应地被启用/禁用?我们是否应该在SomeTask()返回后调用CancelPumpingCommand.NotifyCanExecuteChanged()?由于CancelPumpingCommand使用StartPumpingCommand.IsRunning属性来决定其可用性,并且StartPumpingCommand仍将运行,它将如何表现,对吗?
6ie5vjzr

6ie5vjzr1#

下面是在Community Toolkit中执行异步命令的正确方法,以防它对某人有帮助:

private AsyncRelayCommand _StartPumpingCommand;
public AsyncRelayCommand StartPumpingCommand
{
  get
  {
    _StartPumpingCommand ??= new AsyncRelayCommand(async (CancellationToken token) =>
      {
        try
        {
          await Task.Run(() => SomeTask(token), token);
        }
        catch(OperationCanceledException)
        {
          //do whatever u want to do in case of task cancellation
        }
      },
      () => !_StartPumpingCommand.IsRunning);

    return _StartPumpingCommand;
  }
}

private ICommand _CancelPumpingCommand;
public ICommand CancelPumpingCommand
{
  get
  {
    _CancelPumpingCommand ??= StartPumpingCommand.CreateCancelCommand();
    return _CancelPumpingCommand;
  }
}

private async Task SomeTask(CancellationToken token)
{
  //I'm using "await foreach" which requires C# 8 and an Nuget
  //package named `Microsoft.Bcl.AsyncInterfaces` when targeting
  //.NET Framework. You may use some other way of creating a 
  //Task here.
  await foreach(...)
  { 
    token.ThrowIfCancellationRequested();

    //do stuff
  }
}

这是完全正确的。我的UI是完全响应的(没有死锁),我可以取消和重新启动任务任意次数。这表明AsyncRelayCommand内部包含任务取消魔法,只需要等待方法就可以工作。

相关问题