devxlogo

Mutually exclusive list boxes

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 LongDim thisItemData As Long    If Not IsMissing(itemIndex) Then    ' use itemIndex if not omitted    GoSub MoveListItem_SubElseIf sourceList.MultiSelect = 0 Then    ' else use current item    itemIndex = sourceList.ListIndex    GoSub MoveListItem_SubElse    ' 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    LoopEnd IfExit Sub    MoveListItem_Sub:If itemIndex < 0 Then Exit SubthisItemData = 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 IfEnd If' remove from source listboxsourceList.RemoveItem itemIndexReturnEnd 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 SubPrivate Sub cmdRemove_Click()    MoveListItem List1(1), List1(0)End SubPrivate 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 WithEnd 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    NextEnd Sub

See also  Professionalism Starts in Your Inbox: Keys to Presenting Your Best Self in Email
devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist