web-dev-qa-db-ja.com

別のスレッドからObservableCollectionを更新する最良の方法は何ですか?

BackgroundWorkerを使用してObservableCollectionを更新していますが、次のエラーが発生します。

「このタイプのCollectionViewは、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。」

最小限の作業でこれを解決する最良かつ最もエレガントな方法は何ですか。低レベルのロックベースのマルチスレッドコードを記述したくありません。

私はオンラインでいくつかの解決策を見ましたが、それらは数年前のものなので、この問題の解決策についての最新のコンセンサスは何かわかりません。

32
Joan Venge

MVVMの場合

public class MainWindowViewModel : ViewModel {

    private ICommand loadcommand;
    public ICommand LoadCommand { get { return loadcommand ?? (loadcommand = new RelayCommand(param => Load())); } }

    private ObservableCollection<ViewModel> items;
    public ObservableCollection<ViewModel> Items {
        get {
            if (items == null) {
                items = new ObservableCollection<ViewModel>();
            }
            return items;
        }
    }

    public void Load() {
        BackgroundWorker bgworker = new BackgroundWorker();
        bgworker.WorkerReportsProgress = true;
        bgworker.DoWork += (s, e) => {
            for(int i=0; i<10; i++) {
                System.Threading.Thread.Sleep(1000);
                bgworker.ReportProgress(i, new List<ViewModel>());
            }
            e.Result = null;
        };
        bgworker.ProgressChanged += (s, e) => {
            List<ViewModel> partialresult = (List<ViewModel>)e.UserState;
            partialresult.ForEach(i => {
                Items.Add(i);
            });
        };
        bgworker.RunWorkerCompleted += (s, e) => {
            //do anything here
        };
        bgworker.RunWorkerAsync();
    }
}
12
djeeg

コンストラクタでコレクションを初期化すると、デフォルトのアプリケーションスレッドに配置されます。

メインスレッドを呼び出すには、次のようにします。

Application.Current.Dispatcher.Invoke((Action)(() =>
    {
       //Do something here.
    }));

匿名デリゲートをアクションとしてキャストする必要があります。キャストしないと混乱します¯\ O_o /¯

非同期CTPを使用している場合、これを行うことができます

Application.Current.Dispatcher.InvokeAsync(()=>
    {
       //Do something here.
    });
36
ywm

BGWを使用していますが、問題を解決するように設計されています。ただし、適切に使用し、ProgressChangedまたはRunWorkerCompletedイベントハンドラーでコレクションを更新する必要があります。それがあなたがやっていることなら、あなたは間違ったスレッドでBGWインスタンスを作成しました。 UIスレッドで実行する必要があります。

4
Hans Passant

これを試して:

this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{

 //Code

}));
3

シリアルポートクラスによって発生した(DataReceived上の)イベントからのobservableCollectionの再読み込み中に同じ問題が発生しました。 MVVMを使用しました。 BackgroundWorkerでコレクションを更新しようとしましたが、「このタイプのCollectionViewは、Dispatcherスレッドとは異なるスレッドからのSourceCollectionへの変更をサポートしていません。」を引き起こしました。私はオンラインで見つけた他の多くの解決策を試しましたが、私の問題を解決した唯一の解決策は(すぐに!)マルチスレッドの監視可能なコレクションを使用することでした(この投稿で使用したものを使用しました: どこでスレッドセーフを取得できますか? CollectionView?

1