ObservableCollection は、UI スレッドからの追加、削除、クリア操作のみをサポートしているようです。NO UI スレッドによって操作された場合、Not Support Exception がスローされます。ObservableCollection のメソッドをオーバーライドしようとしましたが、残念ながら多くの問題に遭遇しました。マルチスレッドで操作できる ObservableCollection サンプルを提供してくれる人はいますか? どうもありがとう!
4 に答える
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);
これらの操作を実行するには、基本的にUIスレッドに対してInvokeまたはBeginInvokeを実行する必要があります。
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
個人的には、マークの回答スタイルよりもボブの回答スタイルの方が使いやすいと思います。これを行うための 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); }));
それはあなたのためにトリックをするはずです。.Invokeの代わりに.BeginInvokeを好む状況もありますが。
おそらくこれに対する私の答えを調べたいと思うかもしれませんがplease note
、コードはここから来たものであり、私にクレジットすることはできません. 私はVBでそれを実装しようとしましたが。:元のサイト
これを使用して、ObservableCollectionEx がアクセス データベースから非同期に取り込まれたクラスから WPF リストボックスを取り込んだ。それは機能します。
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);
}
}
}
}