7

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brodie
  • 3,526
  • 8
  • 42
  • 61

5 Answers5

27

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    +1..thanks for that, completely forgot. in fact controls disposing method calls parent.Controls.Remove(this) – Stan R. Jan 06 '10 at 16:38
  • 1
    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. – Nick Jan 06 '10 at 16:38
  • 2
    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. – Hans Passant Jan 06 '10 at 16:46
  • +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: http://stackoverflow.com/questions/1969705/clear-controls-does-not-dispose-them-what-is-the-risk/1970158#1970158 ;-) – Dirk Vollmar Jan 06 '10 at 16:58
  • @divo: couldn't use it, only picture boxes were to be removed. – Hans Passant Jan 06 '10 at 17:05
4
this.Controls.Clear();
JonathanK
  • 3,000
  • 1
  • 20
  • 20
  • 2
    Optionally, because the controls are dynamically created, the "Clear" button could simply just rebuild the control on a full postback with nothing in it. – NotMe Jan 06 '10 at 16:16
  • this will remove all of the controls, regardless of whether they are PictureBox or not. – Stan R. Jan 06 '10 at 16:17
  • this clears all controls. I just want to clear the "blip" controls that were created. – Brodie Jan 06 '10 at 16:18
  • 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. – JonathanK Jan 06 '10 at 16:22
2

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
hackerhasid
  • 11,699
  • 10
  • 42
  • 60
  • 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. – hackerhasid Jan 06 '10 at 16:15
2

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();
            }
Stan R.
  • 15,757
  • 4
  • 50
  • 58
0

It seems Hans Passant forgot a very important detail, too (or perhaps he was just adding to the existing answers, not submitting a full answer). At any rate, here's what I had to do both to invisiblize and dispose my dynamic controls:

Panel p = tp.Controls[panelName] as Panel;
p.Controls.Clear();
for (int i = 0; i < p.Controls.Count; i++)
{
    p.Controls[i].Dispose();
}
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 2
    What was the very important detail he forgot? – Peter Mortensen Oct 29 '17 at 16:29
  • If you clear the controls, `p.Controls.Count` will be zero, nothing to loop. Those controls are not disposed. Also, that's the wrong way to loop something like this, since it moves the index position of each child control. – LarsTech Jan 13 '23 at 22:08