フォームには 2 つのコントロールがあります。ワーカーのリストを含むリストボックスと、作業に関する詳細 (カード) を表示するためのコンテナーとして機能するパネルです。ユーザーがワーカーの名前をクリックすると、パネルにカードが表示されます。カードは、かなり単純な UI (2 つのグループ ボックス、3 つのテキスト ボックス、およびいくつかのラベル) と単純なロジック (ラベルの前色の設定) を備えたユーザー コントロールです。
カードは実行時に作成されます。以前のカードがパネルから削除され、新しいカードが追加されます。ワーカーごとのカードの数は 1 から 4 です。ここで興味深いことがわかります。約まですべてが正常に機能します。ワーカーの 5 回目のクリック。GC が開始され、古いカード (以前に削除された) が破棄されて新しいカードが表示されるまでに約 2 秒 (以前に削除されたカードの数の 0.3sx) かかるようです。以前はワーカー間の移動がうまく機能していたとしても、その時点で非常に遅くなります。Dispose
いくつかの調査の後、使用済みコントロールのメソッドに配置する問題を特定しました。呼び出しbase.Dispose()
には約 0.3 秒かかります。
これが私のコードです:
private void ShowCards(List<Work> workItems) {
var y = 5;
panelControl1.SuspendLayout();
panelControl1.Controls.Clear();
foreach (var work in workItems) {
var card = new Components.WorkDisplayControl(work);
card.Top = y;
card.Left = 10;
y += card.Height + 5;
panelControl1.Controls.Add(card);
}
panelControl1.ResumeLayout(true);
Application.DoEvents();
}
私がこれまでに試したこと:
- カードを破棄する代わりに隠す - ワーカー間を移動するときはより速く動作しますが、フォームを閉じるときにペナルティが支払われます
- カードを非表示にし、それらを処分する別のスレッドを用意する - 変更なし
- 10 枚のカードを追加してすぐに破棄するテスト - 遅い
- 10 枚のカードを追加して、コンストラクターですぐに破棄してテストします。高速です。
- DevExpress コントロールを「通常」に置き換え – 変更なし
- ワーカーを変更するときに古いカードを削除する代わりに手動で破棄する - ワーカー間のすべての移動が遅くなります:
for (var ix = panelControl1.Controls.Count - 1; ix >= 0; --ix) { panelControl1.Controls[ix].Dispose();}
- それをプロファイリング - それが私が問題を見つけた方法です
Dispose
. 私はそれをたどることができますControl.DestroyHandle
Controls.Clear()
私のコントロールのメソッドを呼び出すDispose
- 非常に奇妙な動作と例外- ユーザーコントロールからすべてのコントロールを削除しました-少し高速ですが、それでも遅いです
panelControl1
カードの削除および追加時に非表示 - 変更なし- バックグラウンド GC をオフにしました - 変更なし
- でカードを追加する
AddRange
同じ機能がコンストラクターから呼び出されたときに高速に動作するため、その理由は (コントロール) ハンドルのどこかにあるに違いないと思います。
この奇妙な動作の理由を見つけることができません。アイデアをいただければ幸いです....
更新:GCとControl.Disposeの間の接続を調査しているときに、この優れた答えを見つけました