Click here to Skip to main content
15,880,725 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi, I've encountered this problem before and my solution was a while loop of calling Controls.Remove(control) until Controls.Contains(control) returns false. That's too odd when the thing should be done in only one call to Controls.Remove(control).

I have a form with some controls on it, I want to loop through all its controls and check for some controls meeting some criteria to remove from the form using myForm.Controls.Remove(control), it should work, but only some controls are removed and other controls meeting the criteria that I expected to be also removed are still on the form. The myForm.Controls.Clear() works perfectly but that's not I want.

I can't understand why that happens. If you don't believe, you can test this easily by adding randomly some controls to your form and then try removing some of them from the form with Controls.Remove(). I've tried including the block of code removing the controls between SuspendLayout and ResumeLayout but it still doesn't work. Do you have any clue on this issue? Could you please help me out?
I want to say that you should test with a for loop to remove the controls, removing each control seems to be done successfully. The problem seems to be the for loop works so fast that the form couldn't remove its control by that speed. This is surely a bug, I think so.

Here is my testing code:
C#
public Form1()
        {
            InitializeComponent();
            Random rand = new Random();
            for (int i = 0; i < 30; i++)
            {
                Label lbl = new Label();
                lbl.Text = i.ToString();                
                lbl.Location = new Point(rand.Next(10, 1000), rand.Next(10, 1000));
                Controls.Add(lbl);
            }
            Click += (sender, e) =>
            {
                foreach (Control C in Controls)
                {
                    Controls.Remove(C);//Remove without meeting any criteria
                }
            };
        }

Your help should be highly appreciated!
Thanks!
Posted
Updated 19-Oct-12 2:44am
v3
Comments
Rand Ruggles 19-Oct-12 7:35am    
Hello,
Based on your code snippet, what I am seeing is when the Remove loop is executing, every OTHER Control in the COntrols Collection is being removed. Perhaps you should try rewriting your loop so it steps from the tail towards the fron and use RemoveAt() to clear the full collection in sequence.
supernorb 19-Oct-12 9:04am    
Great!!! Why didn't you post it as your answer? Yeah, your idea is also OK. But again, I can't understand why my code doesn't work. As OriginalGriff said, it is odd with a foreach, but I ran the code and it did remove some of the controls, not all but there wasn't any exception. Thanks!

I suppose the problem arises from messing up with the collection while trying to enumerate it.

I tested with the following and it seems to work:
C#
while (Controls.Count > 0)
{
    Controls.RemoveAt(0);
}


Here's the full code:
C#
public class Form1 : Form
    {
        public Form1()
        {
            
            Random rand = new Random();
            for (int i = 0; i < 30; i++)
            {
                Label lbl = new Label();
                lbl.Text = i.ToString();
                lbl.Location = new Point(rand.Next(10, 1000), rand.Next(10, 200));
                Controls.Add(lbl);
            }

            this.MouseDoubleClick += 
                new MouseEventHandler(Form1_MouseDoubleClick);

        }

        void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
        {

            while (Controls.Count > 0)
            {
                Controls.RemoveAt(0);
            }

            //foreach (Control C in Controls)
            //{
            //    Controls.Remove(C);//Remove without meeting any criteria
            //}
        }
    }

I suggest you do the filtering in one go (copying the references to an array) and then run a foreach loop in the filtered controls calling Controls.Remove(C):
C#
foreach (Control C in filteredControls)
{
    Controls.Remove(C);
}
 
Share this answer
 
v4
Comments
supernorb 19-Oct-12 8:54am    
Thanks, yours works great. I tried with while(Controls.Contains(control)) in each loop of the foreach loop but it didn't work. You've solved my problem but the reason why the code I posted couldn't work as it should is still not clear. Do you think it is a bug? Thanks again!
Frederico Barbosa 19-Oct-12 10:40am    
Glad to help.

Is it a bug? Well, I think not. Whenever you enumerate a collection, you shouldn't delete or add members to it, otherwise the enumerator becomes invalid. This is the standard behavior... If you check Visual Studio help, you'll see that enumerators are supposed to throw an exception when the collection changes. In your case, the exception were being silently swallowed.
Ouch!
Not a good idea - you should get an exception if there is more than one control available.

The problem is that you cannot alter a collection while you are iterating through it, as you are trying to do in your foreach loop.

If you do not get an exception (and you don't) it can only be that the Controls property does not return the same collection each time - so there is some delay or other tasking oddity going on which is confusing the issue.
 
Share this answer
 
Comments
supernorb 19-Oct-12 8:48am    
Thanks, I also tried with for(...) instead of foreach, but it is the same. I think the solution of Frederico Barbosa is OK, but the oddity is still there? Why can't it remove the control right after calling to Controls.Remove()? Again, the problem seems to occur only in for loop not with a single call.
supernorb 19-Oct-12 9:17am    
I tested my code to see which controls are removed in a loop and the result is something a little interesting:
1st loop: 0,2,4,6,...,26,28 are removed (step up by 2)
2nd loop: 1,5,9,...,25,29 are removed (step up by 4)
3rd loop: 3,11,19,27 are removed (step up by 8)
4th loop: 7,23 are removed (step up by 16)
5th loop: the last one of 15 is removed.

Do you have any idea on this? Thanks!
OriginalGriff 19-Oct-12 9:32am    
Not sure, but it may be that the Remove starts a chain of Events to actually remove the control from the form, and process is not complete until the Events have been handled. Without Reflecting the MS Control code, it's difficult to be sure, but the very existence of a Control.ControlRemoved Event does point in that direction - as well as towards a possible solution to your problem: remove the next control in the ControlRemoved event of the previous one.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900