7

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

4

4 回答 4

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);
于 2008-10-09T12:43:23.120 回答
3

您基本上必须调用或 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
于 2008-10-09T12:47:23.470 回答
3

就个人而言,我发现 Bob 的回答方式比 Mark 的回答方式更容易使用。以下是用于执行此操作的 C# WPF 片段:

  1. 在类的构造函数中,在创建可观察集合时获取当前调度程序。因为,正如您所指出的,需要在原始线程上进行修改,这可能不是GUI 线程。所以Application.Current.Dispatcher并不总是正确的,并不是所有的类都有this.Dispatcher

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

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
    

那应该对你有用。尽管在某些情况下您可能更喜欢.BeginInvoke而不是.Invoke

于 2012-11-14T23:54:22.807 回答
2

您可能想调查我对此的回答——但是please note代码来自这里,不能归功于我。虽然我试图在 VB 中实现它。:原创网站

我已经使用它从一个类中填充了一个 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);
     }
  }
}
}
于 2013-01-12T21:20:51.430 回答