動的に設定される TableLayoutPanel のパフォーマンス低下
-
21-08-2019 - |
質問
2 列の TableLayoutPanel を含むユーザー コントロールがあり、行を動的に追加して別のコントロールで選択された項目の詳細を表示するコマンドを受け入れます。したがって、ユーザーは他のコントロール (DataGridView) で行を選択し、DataGridView の SelectedItemChanged イベント ハンドラーで詳細コントロールをクリアしてから、新しく選択した項目 (まったく異なる詳細を持つ可能性がある) のすべての行を再生成します。前に選択した項目から表示します)。これはしばらくの間はうまくいきます。しかし、選択した項目から別の項目に長時間移動し続けると、更新が非常に遅くなります (それぞれ 3 ~ 5 秒)。そう考えると、すべてを適切に処分していないように思えますが、何が足りないのかわかりません。TableLayoutPanel をクリアするコードは次のとおりです。
private readonly List<Control> controls;
public void Clear()
{
detailTable.Visible = false;
detailTable.SuspendLayout();
SuspendLayout();
detailTable.RowStyles.Clear();
detailTable.Controls.Clear();
DisposeAndClearControls();
detailTable.RowCount = 0;
detailTable.ColumnCount = 2;
}
private void DisposeAndClearControls()
{
foreach (Control control in controls)
{
control.Dispose();
}
controls.Clear();
}
そして、次の詳細表示のために必要なすべてのコントロールを TableLayoutPanel にロードし終えたら、次のように呼び出します。
public void Render()
{
detailTable.ResumeLayout(false);
detailTable.PerformLayout();
ResumeLayout(false);
detailTable.Visible = true;
}
TableLayoutPanel 内ではラベル (ごくまれに TextBox) 以外は何も使用しておらず、作成時にラベルとテキストボックスをコントロール リスト (DisposeAndClearControls() で参照) に追加します。私はdetailTable.Controlsを反復してそのように処分しようとしましたが、コントロールの半分が欠けているようでした(デバッガーでステップスルーすることで決定されました)。このようにして、すべてを理解していることがわかります。
描画パフォーマンスを向上させるための提案に興味がありますが、特に複数の選択によるパフォーマンスの低下の原因は何ですか。
解決 3
私は、各選択の変更に私のユーザーコントロールの新しいバージョンを構築するために含むフォームを変更しました。これは、古いものを処分し、新しいものを構築します。これがうまく行っているようです。私はもともと、とにかく一つだけのパフォーマンス上の理由を再利用して行ってよ。明らかにそれは、パフォーマンスは向上しません。私は古いものを廃棄して、新しいものを作成した場合、パフォーマンスが問題ではありません。
TableLayoutPanelはしかし、そのようにリークすること。この不幸
他のヒント
ただ、TableLayoutPanelから継承したカスタムコントロールを使用して素晴らしい作品、真の上DoubleBufferedプロパティを設定します。
public CustomLayout()
{
this.DoubleBuffered = true;
InitializeComponent();
}
TableLayout でも同様の問題がありました。私が使っていたら TableLayout.Controls.Clear() このメソッドでは、子コントロールは破棄されませんでしたが、TableLayout をクリアせずにドロップしただけで、リークは止まりました。振り返ってみると、Clear メソッドを使用したのは面白いことです。 防ぐ 何らかの漏れ。
どうやら、Clear メソッドはコントロールを明示的に破棄しません (TableLayout からコントロールを削除したという事実は、コントロールの使用が完了したことを意味するわけではないため、これは当然のことです)。TableLayout から子コントロールを削除すると、クリーンアップ ルーチンによる破棄が妨げられます。 LayoutTable 自体が破棄されたときに子が返されます (LayoutTable 自体は単に子について認識しなくなっただけです)。
私のおすすめ:を削除します 詳細Table.Controls.Clear(); 行で、親テーブルからdetailTable自体を削除します。 コントロール コレクションを削除して破棄し、次のラウンドのために新しい TableLayout を作成します。また、DisposeAndClearControls メソッドは必要ないため、完全に削除します。私の経験では、それはうまくいきました。
こうすることで、ユーザー コントロール全体をリサイクルする必要がなくなり、その中の TableLayout だけをリサイクルするだけになります。
私は同じ問題に直面し、あまり変更することなく、良い方法を見つけます:
VB.netでの
Dim tp As Type = tlpMyPanel.GetType().BaseType
Dim pi As Reflection.PropertyInfo = _
tp.GetProperty("DoubleBuffered", _
Reflection.BindingFlags.Instance _
Or Reflection.BindingFlags.NonPublic)
pi.SetValue(tlpMyPanel, True, Nothing)
やC#でます:
Type tp = tlpMyPanel.GetType().BaseType;
System.Reflection.PropertyInfo pi =
tp.GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic);
pi.SetValue(tlpMyPanel, true, null);
残念ながら、私が提供できる唯一のアドバイスは、あなたのコントロールの配置を自分の世話をすることです。私の経験.NET TableLayoutPanelでは、非常に有用であるが、何かをリークしているし、それが成長して(と、それはどちらか、この時点に到達するために、細胞の無理数を取ることはありません)unusably遅くなります。この動作は、同様に、デザイナーで見ることができます。
TableLayoutPanel.Controls.Clearは()私のために正常に動作します、多分私は、表示されるのとは別のタブからそれをクリアしているので。
List<Control> controls = new List<Control>();
foreach (Control control in tableLayoutPanelEnderecoDetalhes.Controls)
{
controls.Add(control);
}
foreach (Control control in controls)
{
control.Dispose();
}