在 C# 中删除动态创建的控件

Posted

技术标签:

【中文标题】在 C# 中删除动态创建的控件【英文标题】:Removing dynamically created controls in C# 【发布时间】:2011-01-02 03:01:06 【问题描述】:

我有一个程序可以在图表中添加一系列“光点”:

PictureBox blip = new PictureBox();
blip.Location = new Point(blipHours, blipAltitude);
blip.Size = new Size(6, 6);
blip.BackColor = System.Drawing.Color.Lime;
blip.Text = "";
blip.Name = callsign;
this.Controls.Add(blip);
this.Controls.SetChildIndex(blip, 0);

    如何让按钮清除使用此代码创建的所有“光点”?

    当 blip 的名称等于某个 callsign 时,是否可以更改其背景颜色?每个 blip 都与 ListBox 中的一个选择相关联,我想在用户选择它时更改 blip 的颜色。

【问题讨论】:

【参考方案1】:

每个人都忘记了一个非常重要的细节:您必须 Dispose() 控件,否则它将永远泄漏:

for (int ix = this.Controls.Count - 1; ix >= 0; ix--) 
    if (this.Controls[ix] is PictureBox) this.Controls[ix].Dispose();


我将更加强调 forever 子句,在 cmets 中有很多关于它的喧嚣,Control 类的行为与任何其他 .NET 类不同。控件通过其Handle 属性保持活动状态。其中存储本机 Windows 句柄。只要原生窗口存在,Control对象就不能被销毁。

这需要在您使用 Clear() 或 Remove() 并从其父级移除控件时人为地保持对象处于活动状态。 Winforms 使用所谓的“停车窗口”作为此类控件的宿主。它是一个普通的原生窗口,就像其他任何窗口一样,只是不可见。它的工作是成为此类孤立控件的父级。

停车窗口提供了许多在 Windows 中通常很难做到的巧妙技巧。例如,您可以在运行时打开和关闭 ShowInTaskbar 属性。通常只能在创建窗口时指定的窗口属性(WS_EX_APPWINDOW 样式,在 CreateWindowEx() 调用中指定)。 Winforms 可以通过将窗体的控件移动到停放窗口、销毁窗口、再次创建它并将控件移回来创建窗口之后执行此操作。整洁。

但是对于这个答案的主题不是那么整洁的挂断,如果您删除控件并且不要调用它的 Dispose() 方法,那么它将继续在停车时存活窗户。永远。真正的泄漏。垃圾收集器对此无能为力,它会看到对该对象的有效引用。非常严重地违反了 IDisposable 合同,调用 Dispose() 是可选的,但对于 Control 类来说它是不是

幸运的是,这样的错误很容易诊断,它不需要任何特殊工具,您可以在任务管理器的进程选项卡中看到泄漏。添加“USER Objects”列。

【讨论】:

+1..谢谢,完全忘记了。实际上控件处理方法调用 parent.Controls.Remove(this) 它不会永远泄漏......它只会泄漏,直到垃圾收集器出现......然后希望 PictureBox 的析构函数包括对 Dispose 的调用。 垃圾收集器不会清理它,因为引用了控件。控件通过其 Handle 属性保持活动状态。本机窗口将保留,只是不可见。 +1 和Dispose 也会自动从容器的控件集合中移除控件(这就是列表向下迭代的原因)。顺便说一句,你在这里回答得更好:***.com/questions/1969705/… ;-) @divo: 不能用,只能去掉图片框。【参考方案2】:
this.Controls.Clear();

【讨论】:

可选,因为控件是动态创建的,所以“清除”按钮可以简单地在完全回发时重建控件,其中没有任何内容。 这将删除所有控件,无论它们是否为 PictureBox。 这将清除所有控件。我只想清除已创建的“blip”控件。 是的,示例中“this”的上下文不清楚。 Stan 发布的解决方案似乎适合现在隐含的上下文。您可以考虑将其包装在 UserControl 中,在这种情况下,使用 Clear() 方法从 this.Controls 集合中删除所有控件变得可行。【参考方案3】:

您可能希望将 blip 添加到列表,然后当用户单击“清除”按钮时,只需遍历列表,从 Controls 集合中删除 blip,然后清除列表。

在改变背景颜色方面,为什么不直接使用 if 语句?

blip.BackColor = callsign == "SpecialSign"? System.Drawing.Color.Red : System.Drawing.Color.Lime

【讨论】:

Jonathan Keith 提到 this.Controls.Clear() 可能有效,但根据上下文(“this”),也可能清除其他控件。但是,如果上下文允许,乔纳森的回答可能需要更少的时钟周期。【参考方案4】:

这将从特定容器中删除所有 PictureBox 控件(我假设您的情况是一个图表)。

 for (int i = this.Controls.Count - 1; i >= 0; i--)
            
                PictureBox control = this.Controls[i] as PictureBox;
                if (control == null)
                    continue;

                control.Dispose();
            

【讨论】:

【参考方案5】:

Hans Passant 似乎也忘记了一个非常重要的细节(或者他可能只是添加到现有答案中,而不是提交完整答案)。无论如何,这就是我必须做的事情来隐藏和处置我的动态控件:

Panel p = tp.Controls[panelName] as Panel;
p.Controls.Clear();
for (int i = 0; i < p.Controls.Count; i++)

    p.Controls[i].Dispose();

【讨论】:

他忘记了哪些非常重要的细节?

以上是关于在 C# 中删除动态创建的控件的主要内容,如果未能解决你的问题,请参考以下文章

C# 动态创建控件时注册Name属性

C#把动态创建的多个控件中指定控件显示在最上层

在 C# MonoMac (NSButton, NSLabel) 中动态创建控件/对象

WPF 中动态创建和删除控件

WPF 中动态创建和删除控件

在 Asp.net C# 中创建动态控件,并缓存控件并绑定数据