似乎ObservableCollection仅支持从UI线程添加,删除,清除操作,如果它由NO UI线程操作,则抛出Not Support Exception。我试图覆盖ObservableCollection的方法,不幸的是,我遇到了很多问题。 任何人都可以提供一个可由多线程操作的ObservableCollection示例? 非常感谢!
答案 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代码段:
在您的类的构造函数中,在创建时获取当前的调度程序 你的可观察的收藏品。因为,正如您所指出的那样,修改需要 可以在原始线程上完成,该线程可能不是主 GUI线程。 所以 Application.Current.Dispatcher 不是alwasys对, 并非所有课程都有 this.Dispatcher 。
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_data = new ObservableCollection<MyDataItemClass>();
使用调度程序调用代码部分 需要原始线程。
_dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
那应该为你做的伎俩。虽然在某些情况下您可能更喜欢 .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);
}
}
}
}