C# 动态填充的 TableLayoutPanel 性能下降

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/678805/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 13:07:22  来源:igfitidea点击:

Dynamically Populated TableLayoutPanel Performance Degredation

c#.netwinformstablelayoutpanel

提问by Cory McCarty

I have a user control that contains a 2-column TableLayoutPanel and accepts commands to dynamically add rows to display details of an item selected in a separate control. So, the user will select a row in the other control (a DataGridView), and in the SelectedItemChanged event handler for the DataGridView I clear the detail control and then regenerate all the rows for the new selected item (which may have a totally different detail display from the previously selected item). This works great for a while. But if I keep moving from one selected item to another for quite a long time, the refreshes become VERY slow (3-5 seconds each). That makes it sound like I'm not disposing everything properly, but I can't figure out what I'm missing. Here's my code for clearing the TableLayoutPanel:

我有一个包含 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();
}

And once I get finished loading up all the controls I want into the TableLayoutPanel for the next detail display here's what I call:

一旦我完成将我想要的所有控件加载到 TableLayoutPanel 中以进行下一个细节显示,这就是我所说的:

public void Render()
{
    detailTable.ResumeLayout(false);
    detailTable.PerformLayout();
    ResumeLayout(false);
    detailTable.Visible = true;
}

I'm not using anything but labels (and a TextBox very rarely) inside the TableLayoutPanel, and I add the Labels and TextBoxes to the controls list (referenced in DisposeAndClearControls()) when I create them. I tried just iterating over detailTable.Controls and disposing them that way, but it seemed to miss half the controls (determined by stepping through it in the debugger). This way I know I get them all.

除了在 TableLayoutPanel 中使用标签(很少使用 TextBox),我不使用任何东西,并且在创建它们时将标签和文本框添加到控件列表(在 DisposeAndClearControls() 中引用)。我尝试只迭代 detailTable.Controls 并以这种方式处理它们,但它似乎错过了一半的控件(通过在调试器中逐步执行它来确定)。这样我就知道我得到了它们。

I'd be interested in any suggestions to improve drawing performance, but particularly what's causing the degradation over multiple selections.

我对提高绘图性能的任何建议感兴趣,但特别是导致多重选择性能下降的原因。

采纳答案by Cory McCarty

I changed the containing form to just construct a new version of my user control on each selection change. It disposes the old one and constructs a new one. This seems to perform just fine. I'd originally gone with reusing just one for performance reasons anyway. Clearly that doesn't improve the performance. And the performance isn't a problem if I dispose the old one and create a new one.

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

Unfortunate that the TableLayoutPanel leaks like that, though.

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

回答by Adam Robinson

Unfortunately, the only advice I can offer is to take care of the placement of your controls yourself. In my experience the .NET TableLayoutPanel, while very useful, is leaking SOMETHING and becomes unusably slow as it grows (and it doesn't take an unreasonable number of cells to get to this point, either). This behavior can be seen in the designer as well.

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

回答by Jorge L. Fatta

Just use a custom control that inherits from TableLayoutPanel and set the DoubleBuffered property on true, works great... especially when you dynamically add or remove rows.

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

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

回答by Ishmaeel

I had a similar issue with TableLayout. If I used TableLayout.Controls.Clear()method, the child controls never got disposed but when I simply dropped the TableLayout without clearing it, the leak stopped. In retrospect, it's funny I used the Clear method to preventsome kind of leak.

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

Apparently, Clear method does not explicitly dispose of the controls (which makes sense, because the fact that you removed them from the TableLayout does not mean you are done with them) and removing the child controls from the TableLayout prevents the cleanup routine to dispose of the children when the LayoutTable itself gets disposed (it simply does not know about them anymore).

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

My recommendation: Delete the detailTable.Controls.Clear();line, remove the detailTable itself from the parent's Controlscollection and dispose it, then create a brand new TableLayout for the next round. Also lose the DisposeAndClearControls method entirely since you won't need it. In my experience, it worked nicely.

我的建议:删除detailTable.Controls.Clear(); 行,从父级的Controls集合中删除 detailTable 本身并处理它,然后为下一轮创建一个全新的 TableLayout。也完全失去 DisposeAndClearControls 方法,因为您不需要它。根据我的经验,它工作得很好。

This way, you won't have to recycle your entire user control anymore but only the TableLayout within.

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

回答by Jay

TableLayoutPanel.Controls.Clear() works fine for me, maybe its because i clear it from a different tab than its displayed in.

TableLayoutPanel.Controls.Clear() 对我来说很好用,也许是因为我从与显示不同的选项卡中清除它。

回答by David

I faced the same problem and found a good way without changing too much:

我遇到了同样的问题,找到了一个没有太大变化的好方法:

in VB.net

在 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)

or in C#:

或在 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);

回答by Diego Pego

List<Control> controls = new List<Control>();
foreach (Control control in tableLayoutPanelEnderecoDetalhes.Controls)
{
    controls.Add(control);
}

foreach (Control control in controls)
{
    control.Dispose();
}