I wanted to delete all of the TextBoxes that I put on a form that start with "tbx". The below code only deleted some of them.

For Each ctl As Control In Panel1.Controls
    If ctl.Name.StartsWith("tbx") Then
        ctl.Dispose()
    End If
Next

So, I took it a step further in testing...

Now I'm really confused about the .controls collection. When I try to delete controls from Panel1 with the below code, it only deletes every other control instead of all controls.

For Each ctl As Control In Panel1.Controls
    ctl.Dispose()
Next

It looks like it deletes item(0) which moves everything back one step but then advances the pointer to the next step.

For example, if the following controls exist on the form...
tbx1
tbx2
tbx3
tbx4
on the first pass it disposes of tbx1 but the next iteration though the for each loop now points to tbx3, thus bypasssing tbx2.

Any explanation on why this is doing this and how to get around it?

When you do a for each and remove controls from the collection you're iterating over, it creates problems like the one you're having.

Say if you have a list like

item1
item2
item3

You do a for each loop over the collection and remove the first record and then move to the second. With item1 gone, item2 is now the first record, and item3 is the second! The next candidate for removal is therefore not item2, but the new second record of item3.

Instead, consider doing a for loop and going in reverse order, and then remove items at specifix indexes.

Dim controlIndex As Integer = Panel1.Controls.Count - 1

        For index As Integer = controlIndex To 0 Step -1
            Panel1.Controls(index).Dispose() 
        Next

By removing items starting at the end, you're not affecting the order or position of the elements at the beginning and your removal process should work as you intend as you go backwards over the collection.

Edited 6 Years Ago by apegram: n/a

Thanks for the explanation and the workaround. That is what it looked like it was doing but didn't make sense. Someone should tell Microsoft that a "for each" should take a snapshot and process the snapshot.

When you do a for each and remove controls from the collection you're iterating over, it creates problems like the one you're having.

Say if you have a list like

item1
item2
item3

You do a for each loop over the collection and remove the first record and then move to the second. With item1 gone, item2 is now the first record, and item3 is the second! The next candidate for removal is therefore not item2, but the new second record of item3.

Instead, consider doing a for loop and going in reverse order, and then remove items at specifix indexes.

Dim controlIndex As Integer = Panel1.Controls.Count - 1

        For index As Integer = controlIndex To 0 Step -1
            Panel1.Controls(index).Dispose() 
        Next

By removing items starting at the end, you're not affecting the order or position of the elements at the beginning and your removal process should work as you intend as you go backwards over the collection.

Dim i As Integer 

for i=0 to panel1.controls.count-1
              Panel1.Controls(i).Dispose()
next

Edited 6 Years Ago by __avd: Added [code] tags. For easy readability, always wrap programming code within posts in [code] (code blocks).

Remember to step backwards through the controls, otherwise you will get index out of range error.
This code also test if the current control is a TextBox for when you have different controls in that panel1 container.

For i As Integer = Panel1.Controls.Count - 1 To 0 Step -1
     If TypeOf (Panel1.Controls(i)) Is TextBox Then
           Panel1.Controls(i).Dispose()
     End If
Next
This question has already been answered. Start a new discussion instead.