Reputation: 111
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
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
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