Question For Each Iterating Through CheckedListBox.Items Collection Throws Exception

elroyskimms

Member
Joined
Jul 10, 2015
Messages
19
Programming Experience
10+
I have a CheckedListBox with Items in it. If I iterate through the Items collection with a For Each loop and check if the SelectedItems collection contains the current Item, the For Each loop breaks on Next, acting as if I modified the collection. But I didn't modify the collection at all. I've seen this happen in multi-threaded applications where one thread is looping and another thread is editing, and I understand why it fails. But in this case, I have no other threads running at all. I have a simple WinForm app you can paste into a project. I am walking through the Items collection and checking against the SelectedItems collection. I know I can change For Each to For I as integer.... etc and iterate using the Index values without a problem. But I want to understand WHY this happens in a single-thread environment. Am I modifying the collection somewhere and not realizing it?

-E

VB.NET:
Public Class Form1
    Public Property clbBroken As New CheckedListBox With {.Width = 300, .Height = 300, .CheckOnClick = True}
    Public Property txtResults As New TextBox With {.Multiline = True, .Width = 300, .Height = 300}
    Public Property btnDoStuff As New Button With {.Text = "Do It", .Width = 150}
    Public Property MyPanel As New FlowLayoutPanel With {.Width = 615, .Height = 395, .FlowDirection = FlowDirection.LeftToRight}
    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        Me.Width = 650
        Me.Height = 400
        Me.Controls.Add(MyPanel)
        MyPanel.Controls.Add(clbBroken)
        MyPanel.Controls.Add(txtResults)
        AddHandler btnDoStuff.Click, AddressOf btnDoStuff_Click
        MyPanel.Controls.Add(btnDoStuff)
        For i As Integer = 1 To 5
            'Load the CheckedListBox
            clbBroken.Items.Add(New SimpleClass)
        Next
    End Sub
    Private Sub btnDoStuff_Click(sender As Object, e As EventArgs)
        Try
            For Each MySimpleClass As SimpleClass In clbBroken.Items
                If clbBroken.SelectedItems.Contains(MySimpleClass) Then
                    txtResults.Text &= MySimpleClass.ToString & vbCrLf
                End If
                'This will throw an exception even though nothing is actually changing the List. We're just looking inside the SelectedItems collection.
            Next
        Catch Er As Exception
            txtResults.Text &= "EXCEPTION" & vbCrLf & Er.Message & vbCrLf & Er.StackTrace & vbCrLf
        End Try
    End Sub
End Class
Public Class SimpleClass
    Public Property ID As Guid = Guid.NewGuid
    Public Overrides Function ToString() As String
        Return ID.ToString
    End Function
End Class

List that this enumerator is bound to has been modified. An enumerator can only be used if the list does not change.
at System.Windows.Forms.ListBox.ItemArray.EntryEnumerator.System.Collections.IEnumerator.MoveNext()
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,481
Location
Sydney, Australia
Programming Experience
10+
Why would you enumerate the Items collection and test whether the item is in the SelectedItems collection rather than simply enumerate the SelectedItems collection in the first place? Also, are you aware that Selecteditems is not what contains the items that are checked? That would be CheckedItems. Selection in a CheckedListBox means exactly the same as selection in a regular ListBox. Checking is something different.

As for the question, my guess would be that accessing the SelectedItems property somehow affects the collection underneath the Items property and that's causing the issue you're seeing.
 

elroyskimms

Member
Joined
Jul 10, 2015
Messages
19
Programming Experience
10+
1) There are times where I need to execute methods on every object in the List and also perform additional tasks with the Checked items, hence the need to iterate the entire List as well as test if the item is Checked.
2) I (incorrectly) thought that the SelectedItems and CheckedItems were the same thing, but as you pointed out, they are not. When I change the code to loop through the CheckedItems collection, no exception is thrown. This is what I needed and this is what I should have been doing all along.
3) For educational purposes, I would love to know how and why iterating through the SelectedItems collection modifies the Items collection, especially when iterating through the CheckedItems collection works as expected. I've only dabbled in Reflection but I'm wondering if there is a way to step through the underlying code and see if this is a bug or an intended feature.

Thanks for the help and for finding my mistake!

-E
 
Top Bottom