Finding Items

Rather than examining all of the code, let’s just look at a few interesting properties. The Item property illustrates indexing by number or by string, and its Property Get procedure has a quirky return value that changes depending on the argument.

Remember that the Item property is the default. It can be used for reading items:

Debug.Print srt(3)
Debug.Print srt("Lion")

Numeric indexing of XListBoxPlus works like using the List property of ListBox, except that it is one-based. String indexing does a lookup on the data:

Property Get Item(ByVal vIndex As Variant) As String
    If VarType(vIndex) <> vbString Then
        ' For numeric index, return string value
        Item = lst.List(vIndex - 1)
    Else
        ' For string index, return matching index or 0 for none
        Item = Match(vIndex)
        If Item = 0 Then ErrRaise eseItemNotFound
    End If
End Property

The Match function is used here and in many other parts of the implementation to do string lookups. How do you suppose it works? If you remember Chapter 6, you might want to use LookupItem, which is a wrapper for the SendMessage API function with the LB_FINDSTRING message. But this message is case insensitive, and that’s not what you need for searching some kinds of sorted data. Furthermore, Windows doesn’t know that the internal list box is sorted; the only way it can find an item is with a linear search. But you know that it’s sorted. The most efficient way to find a sorted item is with a binary search—the same one we talked about in Chapter 5. Here’s the code for Match:

Private Function Match(ByVal sItem As String) As Integer
    Dim iPos As Integer
    Select Case esmlMode
    Case esmlUnsorted, esmlShuffle
        Match = LookupItem(lst, sItem) + 1
    Case Else   ' Some kind of sorting
        If BSearch(sItem, iPos) Then Match = iPos + 1 Else Match = 0
    End Select
End Function

Either way, Match will come up with an index to the item, if it exists. Match is also used in the Item Property Let, which enables you to assign items:

.Item(3) = "Deer"
.Item("Lion") = "Big Cat"

It works this way:

Property Let Item(ByVal vIndex As Variant, sItemA As String)
    ' For string index, look up matching index
    If VarType(vIndex) = vbString Then
        vIndex = Match(vIndex)
        ' Fail if old item isn't found or if new item is found
        If vIndex = 0 Then ErrRaise eseItemNotFound
        If Match(sItemA) Then ErrRaise eseDuplicateNotAllowed
    End If
    ' Assign value by removing old and inserting new
    Remove vIndex
    Add sItemA
End Property

This code converts a string index to a numeric index and then uses the Remove and Add methods to assign it in the proper sort order. I could go on to show how Remove finds and deletes items, but the code is the same as the Item Property Get. The Add method is more interesting:

Sub Add(sItem As String, Optional iPos As Integer = 1)
With lst
    ' Adding differs depending on the mode
    Select Case esmlMode
    Case esmlUnsorted
        ' Add where directed (start is default)
        .AddItem sItem, iPos - 1
    Case esmlShuffle
        ' Add at random position
        iPos = GetRandom(0, .ListCount - 1)
        If .ListCount Then
            .AddItem sItem, iPos
        Else
            .AddItem sItem
        End If

    Case Else   ' Some kind of sorting
        ' Binary search for the item
        If BSearch(sItem, iPos) Then
            ErrRaise eseDuplicateNotAllowed
        Else
            ' Insert at sorted position
            If .ListCount Then
                .AddItem sItem, iPos
            Else
                .AddItem sItem
            End If
        End If
    End Select
End With
End Sub

Add recognizes the optional position argument in unsorted modes, but ignores it if the data is sorted. The data is always inserted at the correct position so that the list remains sorted. The only time you actually have to sort the data is if you change the sort mode. Notice that the code raises an error if you try to insert an item that already exists into a sorted list. Duplicate items could have been permitted, but I made a design judgment that most applications that want sorted lists would not want duplicates.

There’s a lot more to XListBoxPlus, but I’ll leave you to explore the rest.