Joeri E
Joeri E

Reputation: 111

Remove dynamic control

I have a form with dynamic added controls. When the user clicks next all dynamic controls have to be removed. My code only removes some of them.

for example dtxt2 does not get removed the first time but if I run the remove function a second time it does get removed.

This is the function to add the dynamics:


void TextboxesStep21()
{
    removeDynamics();
     TextBox dtxt1 = new TextBox();
     dtxt1.Name = "DynamicTextBox1";
     dtxt1.Location = new System.Drawing.Point(336, 125);
     dtxt1.Text = ((diameterOfDrivingWeel + spindleSlope)*10).ToString();
     this.Controls.Add(dtxt1);
     
    TextBox dtxt2 = new TextBox();
    dtxt2.Name = "DynamicTextBox2";
    dtxt2.Location = new System.Drawing.Point(336, 148);
    dtxt2.Text = gearingRatioDen.ToString();
    this.Controls.Add(dtxt2);

    TextBox dtxt3 = new TextBox();
    dtxt3.Name = "DynamicTextBox3";
    dtxt3.Text = gearingRatioNum.ToString(); ;
    dtxt3.Location = new System.Drawing.Point(336, 171);
    this.Controls.Add(dtxt3);


    pictureBox1.SendToBack();
    pictureBox2.SendToBack();
}

This is my remove function

void removeDynamics()
{
    foreach (Control x in this.Controls)
    {
        if (x.Name.Contains("Dynamic"))
        {
            this.Controls.Remove(x);
            x.Dispose(); 
        }
    }
}

Upvotes: 2

Views: 151

Answers (2)

GazTheDestroyer
GazTheDestroyer

Reputation: 21261

You're amending the Controls collection while you're iterating it. You can use LINQ's ToList() to generate a separate collection that you can iterate instead.

void removeDynamics()
{
    foreach (Control x in this.Controls.OfType<TextBox>().ToList())
    {
         if (x.Name.Contains("Dynamic"))
         {
             this.Controls.Remove(x);
             x.Dispose(); 
         }
    }
}

In fact, since you're now using LINQ. You might as well only pull the controls you want:

void removeDynamics()
{
    foreach (Control x in this.Controls.Where(x => x.Name.StartsWith("Dynamic")).ToList())
    {
         this.Controls.Remove(x);
         x.Dispose(); 
    }
}

Upvotes: 1

Mong Zhu
Mong Zhu

Reputation: 23732

for example dtxt2 does not get removed the first time but if I run the remove function a second time it does get removed.
... if you have time could you further explain what my fault was?

When you iterate through a collection you have an iterator in the background that holds the current position and is checked against the length of the collection. So if the collection has 3 items and the iterator is at the position 2

[X1] [X2] [X3]  // Length 3
      ^
      |
   iterator

Now you remove the second element

[X1] [X3]  // Length 2
      ^
      |
   iterator

the iterator has still its value, but now it is pointing to the next element, and unfortunately this is the last in the collection. Now the loop will increment the iterator and check whether it is already larger than the amount of items in the collection. If so it will cancel the loop, because there are no more items to iterate. X3 is thus never processed

[X1] [X3]      // Length 2
           ^
           |
        iterator

This is why your second textbox got skipped in the first place.

when removing items from a collection during iteration through the collection one would use a reversed loop. starting from the end and going down to 0:

void removeDynamics()
{
    for (int i = this.Controls.Count - 1; i >= 0; i--)
    {
        Control x = this.Controls[i];
        if (x.Name.Contains("Dynamic"))
        {
            this.Controls.Remove(x);
            x.Dispose();
        }
    }
}

You could of course also filter the controls collection and get only the matching items first and then remove them:

List<TextBox> toBeRemoved = this.Controls.OfType<TextBox>()
                                         .Where(x => x.Name.Contains("Dynamic")).ToList();
toBeRemoved.ForEach(x => { this.Controls.Remove(x); x.Dispose(); });

Upvotes: 5

Related Questions