从容器中删除控件的最简洁方法是什么?

时间:2011-05-29 07:54:46

标签: c# .net winforms

我遇到的WinForms性能问题可能与我动态添加然后删除数百个控件有关。

编辑{ 应用程序显示一个时间轴,其中包含表示历史事件的控件。根据您跳转的时间添加,删除或移动控件。性能问题不仅在控件的添加和删除期间(我可以忍受),但即使在我跳转到没有历史事件的时间(意味着当前没有显示控件)。在跳转并到达时间线上没有事件的时间之后,GUI中的一些活动仍然需要很长时间才能完成,例如打开菜单或打开对话框。奇怪的是,其他GUI活动,如按下按钮,不会停止。的}

虽然内存消耗非常稳定,但仍然存在释放资源的问题吗?

为了删除控件,我做了两件事:

  1. 取消注册所有活动的回调,
  2. 致电containerPanel.Controls.Remove(control)
  3. 谢谢!

6 个答案:

答案 0 :(得分:6)

正如您已经观察到的那样,这不是内存问题。我的猜测是,问题是一个简单的事实,你的程序需要经常刷新屏幕。如果您在一个批次中删除并添加“数百个控件”,则可以尝试禁用屏幕刷新,直到完成为止 您可以使用SuspendLayoutResumeLayout执行此操作:

SuspendLayout();
for(...)
    AddControl(...);
ResumeLayout();

SuspendLayout();
for(...)
    RemoveControl(...);
ResumeLayout();

答案 1 :(得分:3)

由于GC压力,您可能会遇到麻烦,即垃圾收集器经常运行,因为许多对象已经创建然后被释放。当GC运行时,所有线程都停在他们的轨道上(几乎)并且app看起来像冻结

我不认为你的删除代码有任何问题,但也许你可以以某种方式缓存控件?你能告诉我们更多关于你的情况吗?

-edit -

根据你的场景,我建议回避整个问题,删除控件并添加新的控件,如果可能的话,重用视图中已有的控件,但切换出他们的数据上下文(将它们绑定到不同的数据)视图发生了变化。在wpf中,这种方法的通用名称是UI-virtualization,但它可以应用于任何ui框架,至少在原则上

解决问题的另一种方法可能是为时间轴中的所有位置设置空的占位符控件,您可以立即滚动到该位置,然后将内容添加到从磁盘或其中加载的内容。这样您就不必影响整个时间线的布局,只需填写用户正在查看的特定插槽即可。如果所有时间线事件控件的大小都相同,那么这将更加有效,那么整个时间线的布局将完全不受影响。

答案 2 :(得分:2)

一次删除大量控件实际上并不是WinForms设计好的方法。

每次拨打ControlCollection.Remove都会调用ArrayList.RemoveAt。如果要删除集合中的最后一项,这不是太糟糕。如果要从集合中间删除项目Array.Copy,则会调用ArrayList内部数组中该元素后面的所有项目,以填充空白点。

您可以尝试几种方法:

删除所有控件,然后添加要保留的控件

ArrayList l = new ArrayList();
foreach (Control c in Controls){
    if (ShouldKeepControl(c))
        l.Add(c);
    else
        UnhookEvents(c);
}
SuspendLayout();
Controls.Clear();
Controls.AddRange((Control[])l.ToArray(typeof(Control)));
ResumeLayout();

删除最后一个

/* Example assumes your controls are in the best possible
   order for this technique. If they were mostly at the end
   with a few in the middle a modified version of this
   could still work. */
int i = Controls.Count - 1;
bool stillRemoving = true;
SuspendLayout();
while (stillRemoving && i >= 0){
    Control c = Controls[i];
    if (ShouldRemoveControl(c)){
        UnhookEvents(c);
        Controls.RemoveAt(i);
        i--;
    }else{
        stillRemoving = false;
    }
}
ResumeLayout();

任何一种方法的有效性取决于在删除一批控件后的数量以及集合中控件的顺序。

答案 3 :(得分:2)

由于Control实现IDisposable,您还应该在将控件从容器中移除后对其进行处理。

containerPanel.Controls.Remove(control);
control.Dispose();

答案 4 :(得分:1)

当对WinForm应用程序的UI进行数百次小更新时,当UI线程一遍又一遍地重绘界面时可能会出现性能问题。如果从后台线程推送更新,则会发生这种情况。

如果这是问题,它可能会使UI完全无法使用一段时间。解决方案是以一种UI不重绘的方式进行更新,直到完成所有挂起的更新。

答案 5 :(得分:0)

好, 这看起来很有趣,但对我来说,唯一适合我的解决方案是

  For i = 0 To 3 ' just to repeat it !!
            For Each con In splitContainer.Panel2.Controls
                splitContainer.Panel2.Controls.Remove(con)
                con.Dispose()
                'con.Visible = False
            Next
        Next
  • 使用suspendLayout()和resumeLayout()方法!!!