微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

线程化和创建ObservableCollection

如何解决线程化和创建ObservableCollection

我想对C#(WPF)在代码背后的工作方式进行一些解释。 情况(为简单起见,显然是假设的):

在我们的UI线程上,我们创建Settings对象。它具有一个变量:

public ObservableCollection<Something> collection;

我们从另一个后台线程调用

try{
    Settings.Modify()
}

后执行以下操作:

Settings.Modify()
{
    collection = new ObservableCollection<Something>();
    collection.Add(...)
}

现在,这当然会在Add函数上引发异常:

这种类型的CollectionView不支持对其更改的更改 来自与dispatcher线程不同的线程的SourceCollection。

但是集合= new ObservableCollection();行执行。据我所知,这个新集合是在后台线程的堆栈上创建的。 WPF UI绑定到此变量:

<ItemsControl ItemsSource="{Binding Settings.collection}"

据我了解,UI线程应该不再能够访问此集合。如果尝试,会发生什么情况,由于某种原因会阻塞UI线程吗?

一个问题:为什么编译器让我让一个明显属于UI线程的对象替换为后台线程中的另一个对象,即使它知道我在尝试从另一个线程添加到该集合时也搞砸了

编辑: here已回答此问题的解决方案:

对此我仍然感到好奇:如果

collection = new ObservableCollection<Something>();

函数被单独调用,如果UI线程尝试访问此集合,该怎么办?

解决方法

问题实际上根本不是 ObservableCollection<Something>,而是 <ItemsControl ItemsSource="{Binding Settings.collection}"

ObservableCollection 本身在跨多个线程访问时没有任何问题(只要这些线程一次访问一个)。普通(非DependencyProperty)属性也是如此-它们不继承地属于单个线程。 WPF 绑定系统可以(在大多数情况下)处理来自其他线程的绑定属性的更改(前提是您没有使用 DependencyProperty)。 不能处理来自其他线程的更改的是ItemsControl

让我们逐步完成它,但首先我必须做出一些假设。首先,我必须假设 collection 实际上是一个属性,而不是一个字段(或者至少通过一个属性公开),因为您无法绑定到一个字段。其次,我必须假设涉及 INotifyPropertyChanged 接口,可能在 Settings 类上实现。

所以逐步完成:

  1. Settings.Modify 中,您执行 collection = new ObservableCollection<Something>();。这成功地更新了属性。 WPF 绑定系统会收到有关此更改的通知(通过假定的 INotifyPropertyChanged 接口)并为您处理线程更改。因此 ItemsControl.ItemsSource 在 UI 线程上通过绑定更新为 collection 的新值。
  2. ItemsControl 使用这个新的 ObservableCollection 并创建一个 CollectionView,将其用作其项目的数据源。
  3. 回到Settings.Modify,您调用collection.Add(...)。这成功地向 ObservableCollection<Something> 添加了一个项目,从而触发了它的 INotifyCollectionChanged.CollectionChanged 事件。
  4. CollectionView 创建的 ItemsControl 处理 CollectionChanged 事件,但仍在另一个线程上,在那里引发它。正如异常所说,CollectionView 不“支持从不同于 Dispatcher 线程的线程更改其 SourceCollection”。所以会抛出异常。这个异常看起来来自collection.Add,但是如果您查看调用堆栈,您会发现它实际上来自更深的许多帧。 collection.Add 只是所涉及的代码的最深层次。

当使用 ObservableCollection 和多个线程时,我建议在将完整集合传回 UI 线程进行绑定之前,在后台线程上创建完整集合(如果可能)。在您的示例中,您可以创建一个局部变量,将您的项目添加到该变量中,然后在所有项目就位后将其设置为您的 collection 属性。或者,您可以使用 Dispatcher.InvokeTask<Something> 将单个项目传递回要添加的 UI 线程。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。