Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


Tip of the Day
Language: VB4,VB5,VB6
Expertise: Intermediate
Sep 8, 2001

Mutually exclusive list boxes

Many Windows programs use two adjacent list box controls to let the user select a number of items from a list of available values; such list boxes are mutually exclusive, in the sense that a given item always appear in the list box on the left (available items) or in the list box on the right (items that have been selected so far). While it is not difficult to implement a simple VB program that does the trick, a minor problem is that in the real world you have to preserve the same logical ordering of the items enforced in the original list box, and this has often nothing to do with the alphabetical order. For instance, you might offer a list of months for the user to select from, and wish that selected months appear in the rightmost list box in the same logical (i.e. chronological) order expected by the user. Another point you should deal with are list box controls that support multiple selections. Here is a routine that takes all these details into account:

Sub MoveListItem(sourceList As ListBox, destList As ListBox, _
    Optional itemIndex As Variant)
Dim index As Long
Dim thisItemData As Long
    
If Not IsMissing(itemIndex) Then
    ' use itemIndex if not omitted
    GoSub MoveListItem_Sub
ElseIf sourceList.MultiSelect = 0 Then
    ' else use current item
    itemIndex = sourceList.ListIndex
    GoSub MoveListItem_Sub
Else
    ' this is a multi-selection listbox
    ' loop over all selected items
    ' NOTE that we cannot use a simple For..Next loop
    ' because while we remove items their index changes
    itemIndex = 0
    Do While itemIndex < sourceList.ListCount
        If sourceList.Selected(itemIndex) Then
            GoSub MoveListItem_Sub
        Else
            itemIndex = itemIndex + 1
        End If
    Loop
End If
Exit Sub
    
MoveListItem_Sub:
If itemIndex < 0 Then Exit Sub
thisItemData = sourceList.ItemData(itemIndex)
If thisItemData = 0 Or destList.Sorted = True Then
    ' if there is no associated ItemData append this
    ' element to the end or exploit curret Sort order
    destList.AddItem sourceList.List(itemIndex)
Else
    ' else we must search the proper location
    ' in the destination listbox
    For index = 0 To destList.ListCount - 1
        ' loop until we find an item with higher ItemData
        If destList.ItemData(index) > thisItemData Then
            ' add new item in that position
            destList.AddItem sourceList.List(itemIndex), index
            ' do not forget the associated data
            destList.ItemData(index) = thisItemData
            ' signal that we did the insert
            thisItemData = 0
            Exit For
        End If
    Next
    If thisItemData Then
        ' this happens when the element goes to the end
        destList.AddItem sourceList.List(itemIndex)
        destList.ItemData(destList.ListCount - 1) = thisItemData
    End If
End If
' remove from source listbox
sourceList.RemoveItem itemIndex
Return

End Sub
You can call this routine specifying the index of the item you with to move, or you can omit it. In this latter case the routine behaves differently if the source list box supports multiple selections (in which case all selected items are moved) or not (in which case only the current item is moved). The insertion of a new item in the destination list box is a bit tricky, because the routine also checks if the source item has any ItemData value associated to it, and if so it uses this value to search for the correct position in the list box. While this routine does all the actual work, you still have to build a reasonable user interface. In this code example I build a couple of list box controls - named List1(0) and List1(1) - plus two command buttons. You can move items by double clicking on any item on either list box, or use the Add and Remove buttons:

Private Sub cmdAdd_Click()
    MoveListItem List1(0), List1(1)
End Sub

Private Sub cmdRemove_Click()
    MoveListItem List1(1), List1(0)
End Sub

Private Sub List1_DblClick(index As Integer)
    MoveListItem List1(index), List1(1 - index)
End Sub
At this point you only have to load some data in the leftmost list box control, which you usually do in the Form_Load event. For instance, you might offer the list of months

Private Sub Form_Load()
    With List1(0)
        .AddItem "January"
        .AddItem "February"
        .AddItem "March"
        .AddItem "April"
        .AddItem "May"
        .AddItem "June"
        .AddItem "July"
        .AddItem "August"
        .AddItem "September"
        .AddItem "October"
        .AddItem "November"
        .AddItem "December"
    End With
End Sub
Obviously, it makes no sense to use sorted list boxes in this cases. In order to enforce this same ordering even when items are moved to another list box (and possibly back to the original one), you should assign a progressive ItemData value to each element. I have prepared a short routine that does just that, and that you can easily reuse in all your projects:

Sub InitItemData(sourceList As ListBox)
    Dim i As Long
    For i = 0 To sourceList.ListCount - 1
        sourceList.ItemData(i) = i + 1
    Next
End Sub
Francesco Balena
 
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap