8

在 ViewModels 通过 INotifyPropertyChanged 事件更新 Views 的 MVVM 中,async/await 的酷特性似乎没有太多空间;在调用者的捕获的同步上下文上执行延续。

那么,如果是这种情况,那么谁会在基于现代 UI 的应用程序中真正使用 async/await 的功能呢?在这种情况下,“谁”也可以表示什么模式,例如 MVC 变体。

我可以认为以下是使用 TAP 的好方法

ViewModel.Age
{
  set {
    await Model.SetAge(value);
    NotifyPropertyChanged("Age");
  }
}

但是,在捕获的 syncContext 上运行它并没有太大帮助。实际上,我们可以将所有这些都放在模型中。

Model.Age
{
  set {
    await SetAge(value);
    NotifyPropertyChanged("Age");
  }
}

现在,我们真的希望 syncContext 不是被捕获的。

4

1 回答 1

30

实际上,数据绑定需要INotifyPropertyChanged.PropertyChanged在 UI 同步上下文中引发。

async/await确实强制您区分属性(表示当前状态并且始终是同步的)和命令(表示动作并且可能是同步的或异步的)。属性 getter 和 setter不能async因此您的示例代码带有“异步集”是不可能的方法。

async启用异步命令。您可以使用命令绑定来异步处理路由命令,或将async委托传递给 a DelegateCommand,或使用您自己的ICommand实现。无论您采用哪种方式,最终都会得到一个async void命令事件处理程序。

一个现实的例子是让 VM 属性在内存中设置 M 属性,并SaveCommand使用一个async处理程序。让async处理程序与额外的 VM 属性(SaveInProgress或者可能Busy与其他async处理程序共享的公共属性)交互是很常见的,这样 UI 可以在命令进行时做出适当的响应(通常至少会导致CanExecute返回false)。

所以你的async处理程序最终看起来像:

private async void SaveCommandExecute()
{
  try
  {
    // Set VM property; updates View appropriately.
    Busy = true;

    // Do the actual saving asynchronously.
    await Model.SaveAsync();
  }
  catch (Exception ex)
  {
    // Update the VM with error information.
    Error = ex.Message;
  }
  finally
  {
    // Let the VM know we're done.
    Busy = false;
  }
}

private void SaveCommandCanExecute()
{
  return !Busy;
}

请注意,VM 属性 (ErrorBusy) 在捕获的 UI 同步上下文中更新。


这说明了asyncMVVM 的中心概念:命令可能是async,但属性(例如Busy)始终代表当前状态。

如果您要添加async到现有的 MVVM 应用程序,您会发现自己具有几个指示业务的附加属性,并且可能还有进度更新(例如,完成百分比)。根据您的应用程序,您可以同时允许多个异步操作。您需要考虑将这些信息添加到您的视图中的好方法;我发现这是asyncMVVM 应用程序中最具挑战性的部分。

于 2012-11-09T15:05:44.620 回答