2011-12-10 66 views
5

我一直在玩新的异步CTP和MVVM模式。我一直在转换使用后台工作人员的旧程序,并报告进度以更新模型中的集合。我把它转换成类似这样与MVVM异步Task.Run

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

在我看来,我绑定到我的视图模型暴露出该观察的集合。但是,收集更新时,我会收到以下异常

此类CollectionView不支持从与分派器线程不同的线程更改其SourceCollection。

我不确定什么正确的方法拉回UI线程完成后,像这样。

+0

作为一个侧面说明,有async'的'在Visual Studio 11开发者预览版,您可以下载更新的版本。 – svick

回答

5

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

给定函数是这样的:

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

调用它像这样从UI线程上的一些方法来看,像一个事件处理程序:

f(); 

作品,正是因为它应该。它执行循环的第一次迭代,然后返回。下一次迭代在UI线程上500 ms(或更多,如果UI线程繁忙)后执行。

在另一方面,如果你这样称呼它:

Task.Run(addNames); 

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

所有这些都是使用SynchronizationContext(如果有的话)完成的。

3

您在主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 
}