繁体   English   中英

动态填充的 TableLayoutPanel 性能下降

[英]Dynamically Populated TableLayoutPanel Performance Degredation

我有一个包含 2 列 TableLayoutPanel 的用户控件,并接受命令以动态添加行以显示在单独控件中选择的项目的详细信息。 因此,用户将 select 在另一个控件(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 并以这种方式处理它们,但它似乎错过了一半的控件(通过在调试器中单步执行来确定)。 这样我就知道我得到了所有。

我会对提高绘图性能的任何建议感兴趣,尤其是导致多项选择下降的原因。

只需使用从 TableLayoutPanel 继承的自定义控件并将 DoubleBuffered 属性设置为 true,效果很好……尤其是当您动态添加或删除行时。

public CustomLayout()
{
   this.DoubleBuffered = true;
   InitializeComponent();
}

我对 TableLayout 也有类似的问题。 如果我使用TableLayout.Controls.Clear()方法,子控件永远不会被释放,但是当我简单地删除 TableLayout 而不清除它时,泄漏就停止了。 回想起来,有趣的是我使用 Clear 方法来防止某种泄漏。

显然,Clear 方法没有显式处理控件(这是有道理的,因为您从 TableLayout 中删除它们并不意味着您已经完成了它们)并且从 TableLayout 中删除子控件会阻止清理例程处理当 LayoutTable 本身被处置时的孩子(它根本不知道他们了)。

我的建议:删除detailTable.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);

我更改了包含表单,以便在每次选择更改时构建我的用户控件的新版本。 它处理旧的并构建一个新的。 这似乎表现得很好。 无论如何,出于性能原因,我最初只重用了一个。 显然这并不能提高性能。 如果我处理旧的并创建一个新的,性能不是问题。

不幸的是,TableLayoutPanel 像那样泄漏。

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();
}

不幸的是,我能提供的唯一建议是自己处理控件的放置。 根据我的经验,.NET TableLayoutPanel 虽然非常有用,但会泄漏一些东西,并且随着它的增长变得异常缓慢(而且达到这一点也不需要不合理数量的单元格)。 这种行为也可以在设计器中看到。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM