5

I have the following loop to remove the buttons in my C# Windows Forms application. The only problem is that it skips every other button. How do I go about removing all the button controls from my form?

foreach (Control cntrl in Controls)
{
    if(cntrl.GetType() == typeof(Button))
    {
        Controls.Remove(cntrl);
        cntrl.Dispose();
    }
}
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
Altimus Prime
  • 2,207
  • 2
  • 27
  • 46
  • 4
    Didn't check, but I would expect this code to produce an exception, because you can't modify a collection (`Controls`) while looping through it. – default locale Jul 13 '13 at 04:44
  • I don't get any error in either the execution or in the error list. What do you suggest? – Altimus Prime Jul 13 '13 at 04:45
  • this code will throw Invalid Operation exception may be you would have wrapped this code with try and catch in any of parent methods – Sriram Sakthivel Jul 13 '13 at 06:46
  • I am so glad I was able to track down this question. I had the exact same problem and was trying to search for a solution and it took a while to get the correct search terms. It does not, in fact, give an error, even without a try and catch, which I agree is strange. –  Oct 09 '13 at 23:07
  • I found the issue raised by @defaultlocale and other commenters has already been asked about in [Why does ControlCollection NOT throw InvalidOperationException?](https://stackoverflow.com/q/35084463/150605). I have deleted my comments here and reposted them as [an answer](https://stackoverflow.com/a/52617330/150605) on that question. I linked to this question in that answer, so that question will at least always appear in the Linked section on the right for this question. – Lance U. Matthews Oct 02 '18 at 22:48

3 Answers3

11

I think this way is a bit more readable:

var controlsToRemove = Controls.OfType<Button>().ToArray();
foreach (var control in controlsToRemove)
{
    Controls.Remove(control);
    cntrl.Dispose();
}

Calling ToArray() makes a new concrete collection, so that you can enumerate over one and modify the other.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
2

Surprised that's not erroring on you, since you're modifying the collection as you're iterating over it. Use a for loop and start at the end:

for (int ii = Controls.Count - 1; ii >= 0; ii--)
{
    Control cntrl = Controls[ii];
    Controls.remove(cntrl);
    cntrl.Dispose();
}

(Starting at the end because otherwise you'd be changing the indexes of each control as you iterated.)

zimdanen
  • 5,508
  • 7
  • 44
  • 89
  • Hmm, so you can't remove a ladder while you stand on it? Thanks for explaining the mistake. When trying your method I got an error that there was no definition for Length. I probably tried it wrong. – Altimus Prime Jul 13 '13 at 04:59
  • It's probably `.Count` instead of `.Length` - I didn't verify the code. – zimdanen Jul 15 '13 at 00:37
0

Youre iterating over the same collection from whitch youre removing. Use this code:

    List<Control> cleanControls = new List<Control>();
    foreach(Control ctr in Controls)
    {
       if(cntrl.GetType() != typeof(Button))
       {
          cleanControls.Add(ctr);
       }
       else
       {
         ctr.Dispose();
       }
    } 
    Controls = cleanControls;

That's It! Hope I helped!

Armen Aghajanyan
  • 368
  • 3
  • 15