Removing dynamically created controls in C#

33,733

Solution 1

Everybody is forgetting a very important detail: you have to Dispose() the control or it will leak forever:

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

I'll put some more emphasis on the forever clause, lots of clamor about it in the comments, the Control class does not behave like any other .NET class. A Control is kept alive by its Handle property. Which stores the native Windows handle. As long as the native window exists, the Control object cannot be destroyed.

This requires the object to be kept alive artificially when you use Clear() or Remove() and remove the control from its parent. Winforms uses the so-called "parking window" as the host of such controls. It is a normal native window like any other, it is just not visible. Its job is to be the parent of such orphaned controls.

The parking window permits lots of neat tricks that are normally very hard to do in Windows. You can for example turn the ShowInTaskbar property on and off at runtime. A property of a window that can normally only be specified when you create the window (WS_EX_APPWINDOW style, specified in the CreateWindowEx() call). Winforms can do it even after you created the window by moving the controls of the form to the parking window, destroying the window, creating it again and moving the controls back. Neat.

But with the not-so-neat hangup that's the topic of this answer, if you remove the control and don't call its Dispose() method then it will continue to survive on the parking window. Forever. A true leak. Nothing that the garbage collector can do about it, it sees a valid reference to the object. A pretty gross violation of the IDisposable contract, calling Dispose() is optional but it is not for the Control class.

Luckily such a bug is pretty easy to diagnose, it doesn't require any special tooling, you can see the leak in Task Manager's Processes tab. Add the "USER Objects" column.

Solution 2

this.Controls.Clear();

Solution 3

You might want to add the blip to a List and then when the user clicks the "Clear" button, just iterate over the list, remove the blip from the Controls collection, then clear the list.

In terms of changing the background color, why don't you just use an if statement?

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

Solution 4

This will remove all of the PictureBox controls from the particular container (i assume a graph in your case).

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

                control.Dispose();
            }
Share:
33,733
Brodie
Author by

Brodie

Updated on July 20, 2022

Comments

  • Brodie
    Brodie almost 2 years

    I have a program that adds a series of "blips" to a graph:

    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);
    
    1. How do I have a button clear all of the "blips" that have been created with this code?

    2. Is there a way to change a blip's background color when its name is equal to a certain callsign? Each blip is associated with a selection in a ListBox, and I would like to change the blip's color when the user selects it.

  • hackerhasid
    hackerhasid over 14 years
    Jonathan Keith mentioned this.Controls.Clear() which may work, but depending on the context (of "this"), might clear other controls too. If the context permits, however, Jonathan's answer may take a couple less clock cycles.
  • NotMe
    NotMe over 14 years
    Optionally, because the controls are dynamically created, the "Clear" button could simply just rebuild the control on a full postback with nothing in it.
  • Stan R.
    Stan R. over 14 years
    this will remove all of the controls, regardless of whether they are PictureBox or not.
  • Brodie
    Brodie over 14 years
    this clears all controls. I just want to clear the "blip" controls that were created.
  • JonathanK
    JonathanK over 14 years
    Yes the context of 'this' in the example is unclear. The solution Stan has posted seems fitting for the context that is now implied. You may consider wrapping this in a UserControl, in which case removing all controls from the this.Controls collection with the Clear() method becomes viable.
  • Stan R.
    Stan R. over 14 years
    +1..thanks for that, completely forgot. in fact controls disposing method calls parent.Controls.Remove(this)
  • Nick
    Nick over 14 years
    It won't leak forever... it will just leak until the Garbage collector comes around... and then hopefully the destructor for PictureBox includes a call to Dispose.
  • user1703401
    user1703401 over 14 years
    The garbage collector won't clean it up because the control is referenced. Controls are kept alive by their Handle property. The native window will stay around, it just isn't visible.
  • Dirk Vollmar
    Dirk Vollmar over 14 years
    +1, and Dispose will also automatically remove the control from the container's control collection (that's why the list iterates down). Btw, you answered this even better here: stackoverflow.com/questions/1969705/… ;-)
  • user1703401
    user1703401 over 14 years
    @divo: couldn't use it, only picture boxes were to be removed.
  • Peter Mortensen
    Peter Mortensen over 6 years
    What was the very important detail he forgot?