在单独的线程中更新ObservableCollection

rem*_*rem 13 .net c# wpf multithreading linq-to-sql

在WPF应用程序中,ObservableCollection由LINQ to SQL查询填充和更新.然后使用此ObservableCollection中的值更新UI对象.

通过LINQ to SQL查询更新此ObservableCollection的操作是否可能并且合理地在单独的线程中执行?

如果是,在这种情况下,它将是这个ObservableCollection的同一个实例吗?(我的意思是,如果从LINQ datacontext获取值并且为更新UI提供值的那个不同,那么我将无法更新UI)

Tho*_*que 26

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


Bri*_*hey 22

.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);
  }
Run Code Online (Sandbox Code Playgroud)

我刚刚在我的开发环境中尝试了这个,但是当我从后台线程更新集合时,一切似乎都正常工作.

有关该解决方案的更深入讨论,请访问: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


Max*_*kin 6

在我们的应用程序中,我们有一个绑定到ObservableCollection的TreeView,我们会定期在后台线程中更新,从我们的存储中请求数据.它完美无缺!

哎呦.我被误导了=))

是的,我们实际上是对子类化ObservableCollection<T>并重写OnCollectionChanged方法以避免UI交叉线程异常.我们正在使用此解决方案:

public class MTObservableCollection<T> : ObservableCollection<T>
{
   public override event NotifyCollectionChangedEventHandler CollectionChanged;
   protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   {
      var eh = CollectionChanged;
      if (eh != null)
      {
         Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                 let dpo = nh.Target as DispatcherObject
                 where dpo != null
                 select dpo.Dispatcher).FirstOrDefault();

        if (dispatcher != null && dispatcher.CheckAccess() == false)
        {
           dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
        }
        else
        {
           foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
              nh.Invoke(this, e);
        }
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

没有那个覆盖,你会得到一个这样的例外

System.NotSupportedException:这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection.

现在我们唯一的问题是选定的项目位置,在某些情况下,如果从集合中删除当前选定的项目,TreeView会将选择移动到下一个项目(这会导致我们的应用程序中的其他一些不必要的UI操作).但这是一个小问题.

  • 您必须创建一个ObservableCollection <T>的子类,它将CollectionChanged事件封送回UI线程.我发现这比使用ViewModel管理更新更容易管理. (3认同)
  • 怎么可能?当然,您正在编组回UI线程以应用数据存储中的更改?否则,你会得到一个例外...... (2认同)