Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从容器中删除控件的最干净的方法是什么?_C#_.net_Winforms - Fatal编程技术网

C# 从容器中删除控件的最干净的方法是什么?

C# 从容器中删除控件的最干净的方法是什么?,c#,.net,winforms,C#,.net,Winforms,我遇到了WinForms性能问题,这可能与我动态添加和删除数百个控件有关 编辑{ 应用程序显示一个由代表历史事件的控件组成的时间线。控件的添加、删除或移动取决于您跳转到的时间。性能问题不仅出现在添加和删除控件期间(我可以接受),甚至在我跳转到没有历史事件的时间之后也会出现(意味着当前没有显示任何控件)。在跳转并到达时间线上没有事件的时间之后,GUI中的某些活动仍然需要很长时间才能完成,例如打开菜单或打开对话框。奇怪的是,其他GUI活动,例如按下按钮,不会停止。} 虽然内存消耗非常稳定,但释放资源

我遇到了WinForms性能问题,这可能与我动态添加和删除数百个控件有关

编辑{ 应用程序显示一个由代表历史事件的控件组成的时间线。控件的添加、删除或移动取决于您跳转到的时间。性能问题不仅出现在添加和删除控件期间(我可以接受),甚至在我跳转到没有历史事件的时间之后也会出现(意味着当前没有显示任何控件)。在跳转并到达时间线上没有事件的时间之后,GUI中的某些活动仍然需要很长时间才能完成,例如打开菜单或打开对话框。奇怪的是,其他GUI活动,例如按下按钮,不会停止。}

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

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

  • 注销所有事件的回调
  • 调用
    containerPanel.Controls.Remove(control)

  • 谢谢!

    由于GC压力,您可能会遇到问题,也就是说,垃圾收集器经常在运行,因为有许多对象被创建然后被释放。当GC运行时,所有线程都停止运行(几乎停止),应用程序看起来像是冻结了一样

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

    -编辑-

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


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

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


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


    如果这是一个问题,它可能会使用户界面在一段时间内完全不可用。解决方案是以这样一种方式进行更新,即在所有挂起的更新完成之前,UI不会重新绘制

    一次删除一个控件并不是WinForms设计的目标

    每次调用
    ControlCollection.Remove
    都会导致调用
    ArrayList.RemoveAt
    。如果要删除集合中的最后一项,这还不算太糟。如果要从集合的中间删除项目
    数组。将调用Copy
    数组列表的内部数组中该元素之后的所有项目向下洗牌,以填充空白点

    您可以尝试以下几种方法:

    删除所有控件,然后重新添加要保留的控件 从最后一个移到第一个
    这两种方法的有效性将取决于删除一批控件后保留的控件数量以及控件在集合中的顺序。

    由于
    控件
    实现了
    IDisposable
    因此,您还应在将控件从其容器中删除后处置该控件

    containerPanel.Controls.Remove(control);
    control.Dispose();
    
    好,, 这看起来很有趣,但对我来说,唯一适合我的解决方案是

      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()方法
    +1用于SuspendLayout。这几乎可以肯定是他所需要的。此外,我想知道频繁添加/删除的控件本身是否可以组合在一个(或其他容器)中,以便可以将它们作为单个控件(
    面板
    )删除,并且可以将替换项添加为新的
    面板
    ?如果还没有完成,这将有助于简化操作(从容器中添加/弹出一个控件,而不是多个;显然,添加代码仍然需要存在,才能将它们添加到
    面板本身)。@pickypg:使用该面板是个好主意,但我们真的不知道OP在这里用数百个控件做什么。此外,您的建议与其说是性能优化,不如说是架构改进/代码简化。假设op在ui线程上完成所有这些工作,渲染是否已经被添加/删除调用阻止了?我认为Suspend/ResumeLayout只是专门禁用了布局事件[例如在面板上]。。尽管如此,这样做将提高添加和删除的性能,因此这仍然是一个好主意:)@aL3891:这个假设是不正确的。控件的每次更新都将触发UI刷新。多个更新将触发多个刷新,即使它们仅在添加所有控件且UI线程再次空闲后执行。此外,您不能从不同的线程调用这些方法,也不能从不同的线程向窗体添加控件:“无效的跨线程调用”
    
    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();
    
    containerPanel.Controls.Remove(control);
    control.Dispose();
    
      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