- BackgroundWorker の DoWork 内のループで ViewModel オブジェクトを作成しています
- 各反復の進行状況を報告し、新しいオブジェクトを引数として渡し、ProgressChanged ハンドラー (UI スレッドと友達) によって取得されます。
- そのハンドラーで、オブジェクトは、ListBox がバインドされた ObservableCollection に追加されます。
MY ViewModel クラスには 2 つの文字列プロパティ (Filename と ThumbnailPath) が含まれており、その DataTemplate にはこれらのプロパティにバインドされた Label と Image が含まれています。
void bw_DoWork (object sender, DoWorkEventArgs e) {
List<string> files = e.Argument as List<string>;
FileInfo fi; int percent;
for (int i = 0; i < files.Count; i++) {
FileViewModel newItem = new FileViewModel(files[i]);
fi = new FileInfo(files[i]);
percent = i / files.Count * 100;
bwImportBrowserItems.ReportProgress(percent, newItem);
}
}
void bw_ProgressChanged (object sender, ProgressChangedEventArgs e) {
this.observableCollection.Add(e.UserState as FileViewModel);
}
一般的なアイテム数 (30 ~ 50) の場合の一般的な動作: UI が約 2 ~ 3 秒間フリーズします。アイテムの約半分が表示されます。UI が再び短時間フリーズし、残りが追加されます。
これで、ループから UI の更新を呼び出すのは最善のアイデアではないことがわかりました。呼び出しが非常に頻繁に行われ、UI が応答する時間がないため、UI が「グループで」更新され、そのままになっていることがわかりました。その間無反応。
Thread.Sleep(500)
ループの最後の行として追加してみました。これは、すべてが正常に機能していることを示すのに役立ちました。これは、この速度低下により、アイテムが無反応になることなく次々とうまく追加されていたためです。
そこで、さまざまな睡眠値を試して、に落ち着きましたThread.Sleep(25)
。これは理想的ではありませんが、許容範囲内であり、本来あるべき姿にかなり近づいています。
Thread.Sleep がこのような状況での一般的な回避策であるかどうか、また、この状況で人々が使用する一般的な解決策は何かを尋ねたいと思います:まったく無応答にならずにバックグラウンド ループから UI コレクションを更新することです。私もいくつかのアイデアを思いついたので、コメントをいただければ幸いです。
私が考えることができるアイデア:
- ReportProgress はあまり頻繁に行わないでください。10 回、または 10 個の新しい項目ごとに制限してください。
- ループで実行しないでください。作成する必要がある項目のリストを作成します。DoWork 本体で、リストから 1 つのアイテムをデキューし、インスタンス化して ViewModel インスタンスを返します。RonWorkerCompleted で、UI を更新し、リストが空かどうかを確認し、そうでない場合は再度 WorkerAsync を実行します。