在 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# 中删除动态创建的控件的主要内容,如果未能解决你的问题,请参考以下文章