MVVM中的异步命令执行

时间:2014-12-30 03:26:46

标签: mvvm mvvm-light

我想知道为什么MVVM光缺少命令与异步执行?我相信在很多情况下这可能会有用,所以让我说一个。

假设我们的UI包含一个包含多个屏幕的容器。用户可以关闭特定屏幕或具有多个屏幕的容器。假设用户已在容器上发出了关闭命令。返回的容器在每个屏幕上调用close命令,它需要等待屏幕关闭。实际上,这可能意味着验证数据。因此,我们需要发出异步调用以防止UI变得无法响应,我们还需要等待任务完成,才能继续。

所以,如果我们在Command

中有这样的东西
public RelayCommand CloseCommand
{
    get { return _closeCommand ?? _closeCommand = new RelayCommand( async () =>
    {
        foreach (var screen in Screens)
        {
            if (!await screen.CloseCommand.ExecuteAsync(null))
            {
                // do something
            }
        }
    }) }

}

我们还可以在屏幕上公开其他方法,但在我看来它应该是RelayCommand的任务,因为它已经存在于那里。

或者有不同的方法来处理这种情况?

2 个答案:

答案 0 :(得分:2)

可能是因为有很多不同的方法;我在my MSDN article on the subject中描述了一些方法。

异步生命周期命令特别棘手。类似于"关闭"必须仔细考虑命令。是否有迹象表明正在关闭?如果用户多次关闭会发生什么情况("关闭"特别是通常可以由操作系统或其他应用程序启动,即使"关闭按钮"已被禁用)?

答案 1 :(得分:0)

我发现这在某种程度上是在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;
            });
        }
    }