是否需要relaycommand的异步版本才能正确运行异步方法
本文关键字:运行 异步方法 版本 relaycommand 异步 是否 | 更新日期: 2023-09-27 18:16:02
我在视图模型中定义了以下代码。我认为类型Func<Task>
的SaveAsync正在转换为动作,因为RelayCommand需要一个动作而不是一个Func<Task>
,但我不清楚它的含义。
1) RelayCommand是否需要替换为异步版本(RelayCommandAsync)?2)关于异步,当前代码究竟在做什么?如果我能/应该做些什么来改进/纠正它呢?
private ICommand _saveCommand;
public ICommand SaveCommand
{
get { return _saveCommand ?? (_saveCommand = new RelayCommand(async () => await SaveAsync(), CanSave)); }
}
public bool CanSave()
{
return !IsBusy;
}
private async Task SaveAsync()
{
IsBusy = true;
try
{
await _service.SaveAsync(SomeProperty);
}
catch ( ServiceException ex )
{
Message = "Oops. " + ex.ToString();
}
finally
{
IsBusy = false;
}
}
谢谢!
EDIT:经过一些实验后,似乎async方法本身可以正常工作。并且,在lambda中是否包含async/await以及该方法是否定义为async Task
或async void
都没有区别。
然而,不能正常工作的是canExecute
谓词功能,该功能自动启用/禁用控件绑定到命令。发生的情况是,当async方法运行时,按钮被正确禁用,但之后没有启用。我必须点击窗口上的某个地方一次,然后它再次启用。
所以看起来RelayCommand的异步版本需要完整的功能,也就是说,所以canExecute
可以正确地做它的事情。
1) RelayCommand是否需要替换为异步版本(RelayCommandAsync)?
不必如此,但你应该考虑一下。
2)关于异步,当前代码究竟在做什么?
它正在创建一个async void
lambda。这是有问题的,因为async void
不能很好地处理异常。如果你在异步代码中使用RelayCommand
,那么你肯定会想要使用try
/catch
,就像你的代码中那样。
3)如果我可以/应该改变什么来改进/纠正它?
如果这是你代码中唯一的async命令,我会说它很好。但是,如果您发现应用程序中有几个具有类似语义的async命令,那么您应该考虑编写一个RelayCommandAsync
。
目前还没有标准模式;我在MSDN的一篇文章中概述了几种不同的方法。就我个人而言,我至少在我的应用程序中定义了一个IAsyncCommand
,我从我的vm中公开(很难对异步ICommand
进行单元测试)。
但是,不能正常工作的是canExecute谓词功能,该功能自动启用/禁用控件绑定到命令。
假设RelayCommand.CanExecuteChanged
委托给CommandManager
,则设置IsBusy
后,直接调用CommandManager.InvalidateRequerySuggested
即可。
RelayCommand是否需要替换为异步版本(RelayCommandAsync)?
不,RelayCommand
将在这里工作。
关于异步,当前代码究竟在做什么?
发生的事情是,当重载解析在编译时开始时,它选择接受Action
的重载,这意味着你的方法被翻译成async void
,这就是为什么你的代码编译。
3)如果我可以/应该改变什么来改进/纠正它?
有async委托命令的实现。你可以在这里找到一个需要注意的一个重要事项是异常处理。在绑定到WPF控件的异步ICommand
中出现未处理异常的情况下,该异常将传播到绑定器,并且未被处理且不被注意。
我认为您不需要中继命令的异步版本。您的实现看起来不错。它有用吗?
如果你想测试主体是否在异步运行,添加一个await task.delay(20000),看看当命令运行时UI是否保持响应
No。在绑定上设置IsAsync。像这样:
<Button Command="{Binding DoCommand, IsAsync=True}" />