我一直在玩新的异步CTP和MVVM模式。我一直在使用后台工作程序转换我的旧程序并报告进度以更新模型中的集合。我把它转换成了类似的东西
TaskEx.Run(async () =>
{
while (true)
{
// update ObservableCollection here
}
await TaskEx.Delay(500);
});
在我看来,我绑定到我的viewmodel,它暴露了这个可观察的集合。但是,当我的集合更新时,我得到以下异常
这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。
我不确定在这样做的时候将正确的方法拉回到UI线程是什么。
答案 0 :(得分:6)
您不必使用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
完成的,如果有的话。
答案 1 :(得分:4)
您在主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
}
答案 2 :(得分:3)
虽然您无法从第二个线程更新ObservableCollection
,但可以创建异步可观察集合。这允许您从任务内部或未创建集合的线程更新集合。
我会发布这个例子,但我在这里找到了这些信息。它很漂亮,我发现它是一个非常有用的例子。
http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/