异步任务.使用MVVM运行

本文关键字:运行 MVVM 使用 任务 异步 | 更新日期: 2023-09-27 18:17:41

我一直在尝试新的异步CTP和MVVM模式。我一直在转换我的一个旧程序,它使用后台工作器和报告进度来更新我模型中的集合。我把它转换成如下格式

TaskEx.Run(async () =>
{
  while (true)
  {
    // update ObservableCollection here
  }
  await TaskEx.Delay(500);
});

在我的视图中,我绑定到我的viewmodel,它公开了这个可观察集合。然而,当我的集合更新时,我得到以下异常

这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。

我不确定什么是正确的方式拉回UI线程时这样做的

异步任务.使用MVVM运行

您不必使用Task.Run()或任何其他特殊方式运行异步方法,只需调用它们。而在你的情况下,这正是导致问题的原因。

给定如下函数:

Action f = async () =>
{
    while (true)
    {
        // modify the observable collection here
        await Task.Delay(500);
    }
};

从UI线程上运行的某个方法中像这样调用它,比如事件处理程序:

f();

完全按照它应该的方式工作。它执行循环的第一次迭代,然后返回。下一个迭代将在UI线程上的500毫秒(如果UI线程繁忙,则更长)后执行。

另一方面,如果你这样调用它:

Task.Run(addNames);

不能正常工作。这样做的原因是async方法试图在它们启动时的相同上下文中继续运行(除非您显式地另行指定)。第一个版本是在UI线程上启动的,所以它在UI线程上继续。第二个版本开始于一个ThreadPool线程(多亏了Task.Run()),并继续在那里。这就是为什么它会导致你的错误。

所有这些都是使用SynchronizationContext完成的,如果存在的话。

您在主UI线程上创建了一个ObservableCollection,并试图在异步后台线程上更新它,这在WPF中是无法做到的。

作为一种替代方法,从后台线程获取结果,然后将它们添加到主UI线程的ObservableCollection中。

通常我在后台线程上更新ObservableCollection的代码看起来像这样:

private async void LoadItems()
{
    Task<List<MyItem>> getItemsTask = Task.Factory.StartNew(GetItems);
    foreach(MyItem item in await getItemsTask)
        MyCollection.Add(item);
}
private List<MyItem> GetItems()
{
    // Make database call to get items
}

虽然您无法从第二个线程更新ObservableCollection,但可以创建异步可观察集合。这允许您从任务内部或未创建集合的线程中更新集合。

我将张贴的例子,但我发现的信息在这里。它非常灵巧,我发现它是一个非常有用的例子。

http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/