Looping through your data with for each

There are two built-in interfaces that you can use to implement the usage of For Each...Next to loop through data: Iterator and Iterable.

Creating an iterable ListBox

As an example use of Iterator and Iterable, you can create a version of the DesktopListBox control that lets you iterate through its rows using For Each...Next. Here's how you can implement it.

Create a new class called ListBoxIterator and assign it the Iterator interface which adds empty MoveNext and Value methods. You'll add code to those in a moment, but first add a Constructor method to ListBoxIterator:

Public Sub Constructor(lb As DesktopListBox)
  Data = lb

  InitialRowCount = Data.RowCount
End Sub

This constructor takes as a parameter a DesktopListBox and saves references to the ListBox and its initial count to properties on the class which you should add now:

  • Data As DesktopListBox

  • InitialRowCount As Integer = -1

In addition you'll want one other property to track the current row of the iterator:

  • IteratorRow As Integer

You'll also want to add a computed property that checks to verify that the ListBox has not been modified while you are iterating through it. The computed property:

  • IsValid As Boolean

has this code in its Get block:

If InitialRowCount <> Data.RowCount Then
    Return False
  Else
    Return True
  End If

With these properties in place you can now implement the two interface methods. First, start with MoveNext:

' Ensure ListBox has not been modified while iterating
If Not IsValid Then
  Var exc As New IteratorException
  exc.Message = "ListBox row count cannot be modified within For...Each loop."
  Raise exc
End If

If IteratorRow < (Data.RowCount - 1) Then
  ' Move to the next row in the ListBox.
  ' Note the first row is "-1", so you have to call
  ' MoveNext once to get to the first row 0.
  IteratorRow = IteratorRow + 1
  Return True
Else
  ' No more rows in ListBox
  Return False
End If

And now implement the Value method which actually gets the data from the ListBox. You can actually return the data however you want. In the case of a ListBox, this code gets the String value for each column in the row, separates it by spaces, and returns it as a single String value.

Var lbRow As String
Var space As String
For column As Integer = 0 To Data.LastColumnIndex
  lbRow = lbRow + space + Data.CellTextAt(IteratorRow, column)
  space = " "
Next

Return lbRow

Now that you have created an iterator that can work with a DesktopListBox you need to create a DesktopListBox subclass that uses the iterator. Add a new class (called IterableListBox) and set its super to DesktopListBox and assign it the Iterable interface which adds the GetIterator method. In this method you need to set up and return the iterator you just created. You do that by calling New and passing in the ListBox itself:

Var lbIterator As New ListBoxIterator(Self)
Return lbIterator

That is all you need on this subclass. Now you can use the subclass with For Each...Next. On Window1, drag IterableListBox to the layout and add some initial data using the Initial Value property in the Inspector. Now add a button to the Window and put this code in it to iterate through the values you've added to the ListBox:

For Each row As String In IterableListBox1
  MessageBox(row)
Next

Run your project and click the button where you'll get the contents of the ListBox displayed in a message box, one row at a time.

See also

Iterator, Iterable classes