tags:

views:

8166

answers:

7

Hi All,

I'm getting the error below when trying to loop through a listbox and then remove the item.

"List that this enumerator is bound to has been modified. An enumerator can only be used if the list does not change."

foreach (string s in listBox1.Items)
{
 MessageBox.Show(s);
 //do stuff with (s);
 listBox1.Items.Remove(s);
}

How can I remove the item and still loop through the contents?

Thanks

+9  A: 

Do you want to remove all items? If so, do the foreach first, then just use Items.Clear() to remove all of them afterwards.

Otherwise, perhaps loop backwards by indexer:

listBox1.BeginUpdate();
try {
  for(int i = listBox1.Items.Count - 1; i >= 0 ; i--) {
    // do with listBox1.Items[i]

    listBox1.Items.RemoveAt(i);
  }
} finally {
  listBox1.EndUpdate();
}
Marc Gravell
I'd **love** to know what that downvote is about!!
Marc Gravell
Me too Marc. +1 to counter act the injustice of it.
Binary Worrier
I'd love to know the reason for downvotes. Both for me and you! +1
Mehrdad Afshari
@Binary Worrier: In fact upvotes do not really neutralize downvotes, due to 200 points reputation limit.
Mehrdad Afshari
+2  A: 
for (int i = 0; i < listBox1.Items.Count; ++i)
{
   // ...
   listBox1.Items.RemoveAt(i--);
}
Mehrdad Afshari
The downvote is very undeserved; +1 from me...
Marc Gravell
It's not working... Reverse the loop!
Arjan Einbu
Why it's not working? Read carefully.
Mehrdad Afshari
It should work fine - it just keeps removing item 0 (unless we "continue" in the ... - but it should still work fine)
Marc Gravell
As Marc said, it should work pretty fine. It has the advantage of preserving the order, which might be important in some cases. However, for performance reasons it should be put in BeginUpdate and EndUpdate as Marc said.
Mehrdad Afshari
+1  A: 

You have to go through the collection from the last item to the first. this code is in vb

for i as integer= list.items.count-1 to 0 step -1
....
list.items.removeat(i)
next
+1  A: 

Jefferson is right, you have to do it backwards.

Here's the c# equivalent:

for (var i == list.Items.Count - 1; i >= 0; i--)
{
    list.Items.RemoveAt(i);
}
Jon Limjap
+9  A: 

Everyone else has posted "going backwards" answer, so I'll give the alternative: create a list of items you want to remove, then remove them at the end:

List<string> removals = new List<string>();
foreach (string s in listBox1.Items)
{
    MessageBox.Show(s);
    //do stuff with (s);
    removals.Add(s);
}

foreach (string s in removals)
{
    listBox1.Items.Remove(s);
}

Sometimes the "work backwards" method is better, sometimes the above is better - particularly if you're dealing with a type which has a RemoveAll(collection) method. Worth knowing both though.

Jon Skeet
-1. listBox1.Items might contain objects other than string, in that case InvalidCastException will be thrown.
Mehrdad Afshari
If that were the case, the foreach loop in the sample code in the question would already have blown up. I was making the same assumption as the question did, which I think is pretty reasonable.
Jon Skeet
Yeah, I noticed it. It was just fun to downvote you for a reason for a minutes ;)
Mehrdad Afshari
At least you gave a reason :)
Jon Skeet
I know how it feels, Jon. I think SO should require a comment on downvotes.
Mehrdad Afshari
One day my rep will be greater than Jons and Mehdrad's combined. Muhahahaha! Assuming that people are required to give a reason for down-voting, of course.
Hamish Grubijan
+2  A: 

Here my solution without going backward and without a temporary list

while (listBox1.Items.Count > 0)
{
  string s = listBox1.Items[0] as string;
  // do something with s
  listBox1.Items.RemoveAt(0);
}
nruessmann
A: 

How about:

foreach(var s in listBox1.Items.ToArray())
{
    MessageBox.Show(s);
    //do stuff with (s);
    listBox1.Items.Remove(s);
}

The ToArray makes a copy of the list, so you don't need to worry about it changing the list while you are processing it.

Matthew Wright