ObservableCollection 似乎只支持从 UI 线程添加、删除、清除操作,如果它由 NO UI 线程操作,它会抛出 Not Support Exception。我试图覆盖 ObservableCollection 的方法,不幸的是,我遇到了很多问题。任何人都可以为我提供一个可以由多线程操作的 ObservableCollection 示例吗?非常感谢!
问问题
8773 次
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 片段:
在类的构造函数中,在创建可观察集合时获取当前调度程序。因为,正如您所指出的,需要在原始线程上进行修改,这可能不是主GUI 线程。所以Application.Current.Dispatcher并不总是正确的,并不是所有的类都有this.Dispatcher。
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; _data = new ObservableCollection<MyDataItemClass>();
使用调度程序调用需要原始线程的代码部分。
_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 回答