在 MVVM 轻型模式下执行异步命令
本文关键字:执行 异步 命令 模式 MVVM 轻型 | 更新日期: 2023-09-27 18:35:41
我想知道为什么 MVVM 灯缺少异步执行的命令?我相信在很多情况下这可能是有用的,所以让我举一个。
假设我们的 UI 包含一个包含多个屏幕的容器。用户可以关闭特定屏幕或具有多个屏幕的容器。假设用户在容器上发出了关闭命令。作为回报,容器在每个屏幕上调用关闭命令,并且需要等待屏幕关闭。这在实践中可能意味着验证数据。保存等。出于这个原因,我们需要发出一个异步调用来防止 UI 变得无响应,并且我们还需要等待任务完成,以便继续。
所以,如果我们在命令中有这样的东西
public RelayCommand CloseCommand
{
get { return _closeCommand ?? _closeCommand = new RelayCommand( async () =>
{
foreach (var screen in Screens)
{
if (!await screen.CloseCommand.ExecuteAsync(null))
{
// do something
}
}
}) }
}
我们还可以在屏幕上公开其他方法,但在我看来,它应该是 RelayCommand 的任务,因为它已经存在。
还是有不同的方法来处理这种情况?
可能是因为有许多不同的方法可以做到这一点;我在有关该主题的 MSDN 文章中介绍了几种方法。
异步生存期命令尤其棘手。必须仔细考虑诸如"关闭"命令之类的内容。是否有迹象表明正在进行关闭?如果用户多次关闭(特别是"关闭"通常可以由操作系统或其他应用程序启动,即使禁用了"关闭按钮",会发生什么情况)?
我发现这在某种程度上是在MVVM Light中制作异步命令的解决方案。如果事实如此,它会用 Task.Run 过度包装异步方法。我们的包装方法必须验证它是否未执行两次,并从较低的异步执行中捕获错误。
private bool isLoading;
public bool IsLoading
{
get { return isLoading; }
set
{
if (value != isLoading)
{
Set(ref isLoading, value);
//Used to refresh Commands CanExecute laying on IsLoading
CommandManager.InvalidateRequerySuggested();
}
}
}
private RelayCommand loadCommand;
public RelayCommand LoadCommand
{
get
{
return loadCommand ?? (loadCommand = new RelayCommand(
() => Task.Run(LoadAsync),
() => !IsLoading
));
}
}
private async Task LoadAsync()
{
//Prevents double execution in case of many mouse clicks on button
if (IsLoading)
{
return;
}
//Assignments which need to be done on UI tread
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsLoading = true;
});
try
{
list = await service.LoadAsync();
...
}
catch (Exception e)
{
...
}
finally
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
IsLoading = false;
});
}
}