代码之家  ›  专栏  ›  技术社区  ›  Cory McCarty

动态填充的TableLayoutPanel性能下降

  •  4
  • Cory McCarty  · 技术社区  · 16 年前

    我有一个用户控件,它包含一个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中使用标签(和很少使用的文本框),我什么都不使用,在创建标签和文本框时,我会将它们添加到控件列表(在DisposeAndClearControls()中引用)。我试图迭代detailable.controls并以这种方式处理它们,但它似乎错过了一半的控件(通过在调试器中单步执行来确定)。这样我就知道我把它们都弄到手了。

    我对提高绘图性能的任何建议都感兴趣,尤其是什么导致了多个选择的性能下降。

    7 回复  |  直到 16 年前
        1
  •  10
  •   Jorge L. Fatta    16 年前

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

    public CustomLayout()
    {
       this.DoubleBuffered = true;
       InitializeComponent();
    }
    
        2
  •  4
  •   Ishmaeel    15 年前

    我对TableLayout也有类似的问题。如果我使用 tablelayout.controls.clear()。 方法,子控件从未被释放,但当我简单地丢弃TableLayout而不清除它时,泄漏停止。回想起来,我用清晰的方法 防止 某种泄漏。

    显然,clear方法不会显式地释放控件(这是有意义的,因为从TableLayout中删除控件并不意味着已经完成了这些操作),并且从TableLayout中删除子控件会阻止清理例程在释放LayoutTable本身时释放子控件(它只是已经不知道了)。

    我的建议:删除 detailTable.controls.clear(); 行,从父级的 控制 收集并处理它,然后为下一轮创建一个全新的TableLayout。同时完全失去DisposeAndClearControls方法,因为您不需要它。以我的经验来看,它工作得很好。

    这样,您就不必再回收整个用户控件,而只需要内部的TableLayout。

        3
  •  1
  •   Cory McCarty    16 年前

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

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

        4
  •  0
  •   Adam Robinson    16 年前

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

        5
  •  0
  •   Jay    13 年前

    tablelayoutpanel.controls.clear()对我来说很好,可能是因为我从不同的选项卡中清除了它。

        6
  •  0
  •   shytikov Cascabel    10 年前
    List<Control> controls = new List<Control>();
    foreach (Control control in tableLayoutPanelEnderecoDetalhes.Controls)
    {
        controls.Add(control);
    }
    
    foreach (Control control in controls)
    {
        control.Dispose();
    }
    
        7
  •  0
  •   David    8 年前

    我也遇到了同样的问题,找到了一个不改变太多的好方法:

    在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);
    
    推荐文章