无法在多线程中运行ObservableCollection

时间:2008-10-09 12:40:17

标签: wpf multithreading observablecollection

似乎ObservableCollection仅支持从UI线程添加,删除,清除操作,如果它由NO UI线程操作,则抛出Not Support Exception。我试图覆盖ObservableCollection的方法,不幸的是,我遇到了很多问题。 任何人都可以提供一个可由多线程操作的ObservableCollection示例? 非常感谢!

4 个答案:

答案 0 :(得分:7)

使用Kent提供的链接,您可以使用以下代码修改跨线程的集合:

while (!Monitor.TryEnter(_lock, 10))
{
   DoEvents();
}

try
{
   //modify collection
}
finally
{
   Monitor.Exit(_lock);
}

但是,如果您只想修改原始线程上的集合,可以尝试使用UI线程的回调。我通常做这样的事情:

this.Dispatcher.Invoke(new MyDelegate((myParam) =>
{
  this.MyCollection.Add(myParam);
}), state);

答案 1 :(得分:3)

你基本上已经将Invoke或BeginInvoke转到UI线程来执行这些操作。

Public Delegate Sub AddItemDelegate(ByVal item As T)

Public Sub AddItem(ByVal item As T)
    If Application.Current.Dispatcher.CheckAccess() Then
        Me.Add(item)
    Else
        Application.Current.Dispatcher.Invoke(Threading.DispatcherPriority.Normal, New AddItemDelegate(AddressOf AddItem), item)
    End If
End Sub

答案 2 :(得分:3)

就我个人而言,我发现鲍勃的答案风格比马克的答案风格更容易使用。以下是执行此操作的C#WPF代码段:

  1. 在您的类的构造函数中,在创建时获取当前的调度程序 你的可观察的收藏品。因为,正如您所指出的那样,修改需要 可以在原始线程上完成,该线程可能不是 GUI线程。 所以 Application.Current.Dispatcher 不是alwasys对, 并非所有课程都有 this.Dispatcher

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    _data = new ObservableCollection<MyDataItemClass>();
    
  2. 使用调度程序调用代码部分 需要原始线程。

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
    
  3. 那应该为你做的伎俩。虽然在某些情况下您可能更喜欢 .BeginInvoke 而不是 .Invoke

答案 3 :(得分:2)

你可能想调查一下这个问题的答案 - 但please note代码来自这里并且不能归功于我。虽然我试图在VB中实现它。 :Original Site

我已经使用它从一个类中填充WPF列表框,该类具有从访问数据库异步填充的ObservableCollectionEx。它确实有效。

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
   // Override the event so this class can access it
   public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler    
 CollectionChanged;

   protected override void OnCollectionChanged    (System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
   {
  // Be nice - use BlockReentrancy like MSDN said
  using (BlockReentrancy())
  {
     System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
     if (eventHandler == null)
        return;

     Delegate[] delegates = eventHandler.GetInvocationList();
     // Walk thru invocation list
     foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
     {
        DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
        // If the subscriber is a DispatcherObject and different thread
        if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
        {
           // Invoke handler in the target dispatcher's thread
           dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
        }
        else // Execute handler as is
           handler(this, e);
     }
  }
}
}