我需要帮助理解为什么我得到“调用线程无法访问此对象,因为不同的线程拥有它”一个可能的解决方案的错误,而不是在另一个解决方案中。假设我有一个公共的CollectionViewSource和一个带有适当的get / sets的公共List对象。我需要启动一个后台线程来从服务器下载电子邮件地址列表。 (为简洁起见,我将跳过下载逻辑),然后通过将控件编组回UI,将结果集绑定到我视图上的组合框中。我的工作解决方案如下:
Task.Factory.StartNew(() =>
{
// Grab User Email addresses.
EmailPool = SomeDataSource.Select(u => u.Email).ToList();
System.Windows.Application.Current.Dispatcher.BeginInvoke(
new Action(() =>
{
EmailPoolCollectionViewSource.Source = EmailPool;
EmailPoolCollectionViewSource.SortDescriptions.Add(
new SortDescription(String.Empty, ListSortDirection.Ascending));
}
}
现在,由于我对WPF线程相对较新,我开始使用此解决方案来了解如何/为什么我可以打破它。我注意到,如果我删除Dispatcher代码段(由于我失去了对UI线程的访问权限,这是打破此问题的可靠方法)并更改此行:
EmailPool = SomeDataSource.Select(u => u.Email).ToList();
对此:
EmailPoolCollectionViewSource.Source = SomeDataSource.Select(u => u.Email).ToList();
应用程序将抛出“调用线程无法访问此对象,因为不同的线程拥有它”该行的异常。我删除了视图中任一属性的任何绑定,试图找出问题,但我仍然得到相同的结果。为什么会这样? EmailPool
和EmailPoolCollectionViewSource
都在我的ViewModel中声明,如果后台工作线程可以访问其中一个,为什么不能访问另一个呢?
答案 0 :(得分:1)
这是因为它只会在与来自不同线程的UI控件交互时中断。与未绑定到UI的标准变量交互是完全正常的。
这一行
EmailPool = SomeDataSource.Select(u => u.Email).ToList();
不与UI交互,因为您只是将值分配给非UI变量(EmailPool
)。
但是,当您分配CollectionViewSource的Source属性时,Source属性的setter会向UI控件发出信号,指示它需要更新的UI控件和 是UI交互(虽然间接通过约束)。
如果您在UI中直接绑定到EmailPool,则会在分配EmailPool的行上抛出异常。
编辑:看起来,CollectionViewSource不关心你是否已将Source属性绑定到控件,并且无论如何都会抛出异常。检查CollectionViewSource的源代码会很有意思,但看起来无论如何都会检查线程所有权。
编辑2 查看代码here后,我的第一次编辑出错了。 Source只是一个DependencyProperty(CollectionViewSource继承自DependencyObject),因此它必须是任何DependencyProperty的默认行为,以验证创建它的线程与设置值的线程。最重要的是,始终在创建它的同一个线程上编辑DependencyProperty值。