在单独的线程中更新ObservableCollection

20
在WPF应用程序中,一个ObservableCollection通过LINQ to SQL查询进行填充和更新。然后,UI对象使用来自这个ObservableCollection的值进行更新。
操作通过LINQ to SQL查询更新这个ObservableCollection是否可以在单独的线程中执行,且是合理的?
如果是,那么在这种情况下,这个ObservableCollection实例是否是相同的?(我的意思是,如果从LINQ数据上下文中获取值的不是与向UI更新值的不是同一个ObservableCollection,则我将无法更新UI。)
4个回答

34

.Net 4.5提供了一个解决方案,位于BindingOperations类中。

您现在可以使用BindingOperations.EnableCollectionSynchronization方法,如下所示:

private readonly object _personCollectionLock;
private ObservableCollection<Person> _personCollection;

public ObservableCollection<Person> PersonCollection
{
  get { return _personCollection; }
  set
  { 
    _personCollection = value;
    BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock);
  }

我只是在开发环境中尝试了一下,但现在当我从后台线程更新集合时,一切似乎都正常工作。

有一个更详细的解决方案讨论在这里:http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification

此方法的MSDN条目在这里:https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx


请注意,为了使其正常工作,必须调用“set”方法;如果您在声明时内联初始化支持字段,并且从未显式重新创建集合,则此方法无法正常工作。要么移动初始化,要么也从构造函数中调用“EnableCollectionSynchronization”。 - Geoff

30

使用内置的ObservableCollection<T>类,如果UI绑定到该集合,则无法从单独的线程更改其内容,它会抛出NotSupportedException(但是集合项属性的更改通知可以正常工作)。我编写了一个AsyncObservableCollection<T>来处理这种情况。它通过在UI同步上下文中调用事件处理程序来实现。


一个可能的替代方案 https://dev59.com/hGcs5IYBdhLWcg3waTJD - Narkha
或者您可以尝试这个,它完全是线程安全的,可以从任何线程工作,并且可以被多个UI线程数据绑定:http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection - Anthony
收集得很好,干得漂亮!! - Trae Moore
@Thomas Levesque,如何处理“幽灵”(可能是因为UI正在枚举而内容正在添加)。请参见https://dev59.com/rWvXa4cB1Zd3GeqPJXZq。您有解决方案吗?也许需要一些锁定? - Werner
太棒了!非常容易实现。谢谢! - Smitty-Werben-Jager-Manjenson

2
尝试理解您的问题: 情景1 1. LINQ to SQL从数据库检索数据集并添加到ObservableCollection A中。 2. 周期性地,从数据库检索更多数据并添加到A中。旧数据从A中删除。 情景2 1. LINQ to SQL从数据库检索数据集并添加到ObservableCollection A中。 2. 定期使用来自数据库的新数据更新A中的数据(无添加/删除)。
对于情景1,您将不得不使用UI线程。 UI线程拥有ObservableCollection,如果您尝试在另一个线程中使用它,则会出现异常。
对于情景2,很好。 只要您不尝试添加或删除集合本身中的项,就可以在后台线程中尽可能多地更新该项。

2

我没有收到相同的System.NotSupportedException,但不幸的是,如果我从另一个线程设置了我的ObservableCollectiony<MyType>,我的UI没有正确更新

对我有效的唯一方法是https://www.codeproject.com/Tips/1111432/Update-data-to-WPF-control-from-another-thread中提供的:

在ViewModel中

private SynchronizationContext _syncContext = SynchronizationContext.Current;

private ObservableCollection<PackageModel> _packageModelList;
public ObservableCollection<PackageModel> PackageModelList
{
     get => _packageModelList;
     set
     {
         if (_packageModelList == value)
             return;
         packageModelList = value;
         RaisePropertyChanged("PackageModelList");
     }
}      

在其他线程中

 _syncContext.Send(x => { PackageModelList = OtherThreadPackageModels; },null);

希望这能帮助到其他人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接