有关WPF线程所有权的不一致错误

时间:2014-06-23 19:57:03

标签: c# .net wpf

我需要帮助理解为什么我得到“调用线程无法访问此对象,因为不同的线程拥有它”一个可能的解决方案的错误,而不是在另一个解决方案中。假设我有一个公共的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();

应用程序将抛出“调用线程无法访问此对象,因为不同的线程拥有它”该行的异常。我删除了视图中任一属性的任何绑定,试图找出问题,但我仍然得到相同的结果。为什么会这样? EmailPoolEmailPoolCollectionViewSource都在我的ViewModel中声明,如果后台工作线程可以访问其中一个,为什么不能访问另一个呢?

1 个答案:

答案 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值。