在 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 的任务,因为它已经存在。

还是有不同的方法来处理这种情况?

在 MVVM 轻型模式下执行异步命令

可能是因为有许多不同的方法可以做到这一点;我在有关该主题的 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;
            });
        }
    }