クラスにObservableCollection
があります。そしてクラスのさらに先にはスレッドがあります。このスレッドからObservableCollection
に追加したいと思います。しかし、私はこれを行うことができません:
このタイプのCollectionViewは、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。
これはUIスレッドからは発生しないため、ディスパッチャーにアクセスできません。
JaredParのアプローチは有効です。考慮に値する別のアプローチは、組み込みObservableCollection
の代わりにスレッドセーフObservableCollection
を使用することです。そこにはいくつかの実装がありますが、 Sasha Barberの実装 と CLinq継続的コレクションクラス は私の意見ではより良いもののいくつかです。内部的には、これらのクラスは基本的にJaredParで概説されているアプローチを使用しますが、コレクションクラス内にカプセル化します。
これを解決する最良の方法は、Dispatcher
オブジェクトをバックグラウンドスレッドのstartメソッドに渡すことです。
void DoBackgroundOperation(ObservableCollection<SomeType> col) {
var dispatcher = Dispatcher.CurrentDispatcher;
ThreadStart start = () => BackgroundStart(dispatcher, col);
var t = new Thread(start);
t.Start();
}
private static void BackgroundStart(
Dispatcher dispatcher,
ObservableCollection<SomeType> col) {
...
SomeType t = GetSomeTypeObject();
Action del = () => col.Add(t);
dispatcher.Invoke(del);
}
これで、後でコレクションに追加する必要があるときに、UI Dispatcher
オブジェクトを使用できます。
@Reedが指摘したように、SynchronizationContext
を使用することにより、より一般的な解決策が実現されます。以下は、SynchronizationContext
を使用して新しい値を追加するデリゲートを作成する関数型のサンプルです。これには、オブジェクトを作成するコードからコレクションとスレッドモデルの両方を隠すという利点があります。
void DoBackgroundOperation(ObservableCollection<SomeType> col) {
var context = SynchronizationContext.Current;
Action<SomeType> addFunc = (SomeType st) => context.Send(() => col.Add(st), null);
ThreadStart start = () => BackgroundStart(addFunc);
var t = new Thread(start);
t.Start();
}
private static void BackgroundStart(Action<SomeType> addFunc) {
...
SomeType t = GetSomeTypeObject();
addFunc(t);
}
.Net 4.5では、スレッドセーフコレクション、ConcurrentDictionary、ConcurrentBagなど、ニーズに合った方を使用できます。 http://msdn.Microsoft.com/en-us/library/dd997305.aspx
また読んでみてください: http://www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So