This is similar to Steve's answer, but uses a DataSource
. Also, there is no real need to Enable
/Disable
each CBO. As they make changes, previous selections are filtered from the source. Any duplicate "previous" changes get undone.
CBO1 always has the full list, while the others omit previous selections. You cant end up with duplicate picks because re-picking #2 to be the same as #3, changes the contents of #3 replacing the selection to whatever is first in the list.
' the master list
Private OptList As New List(Of String)
Private ignore As Boolean
' initialize somewhere:
OptList.AddRange(New String() {"Red", "Green", "Blue", "Violet", "Orange"})
' set up cbos
' ignore changes while setting up:
ignore = True
cb1.DataSource = OptList.ToArray
cb1.SelectedIndex = -1
cb2.DataSource = OptList.ToArray
cb2.SelectedIndex = -1
cb3.DataSource = OptList.ToArray
cb3.SelectedIndex = -1
ignore = False
With them all enabled, they can pick them in any order.
Private Sub cbo_SelectedIndexChanged(sender As Object, e As EventArgs) _
Handles cb1.SelectedIndexChanged, cb2.SelectedIndexChanged
If ignore Then Exit Sub
Dim cbo = TryCast(sender, ComboBox)
If cbo IsNot Nothing AndAlso cbo Is cb1 Then
cb2.DataSource = GetFilteredList(New String() {cb1.Items(cb1.SelectedIndex)})
Else
cb3.DataSource = GetFilteredList(New String() {cb1.SelectedItem.ToString,
cb2.SelectedItem.ToString})
End If
End Sub
Private Function GetFilteredList(items As String()) As String()
Return OptList.Except(items).ToArray()
End Function
Since cbo3 is limited to those items not picked in #1 or #2 (a slave) you dont have to do anything when that selection changes.
Could this be expanded to say 9 cbo by continuing multiple if/else statements
and
How would I keep the first option. Say if I wanted to include a "None" option always available.
What that many, I would do it more abstractly. The code may be harder to read/follow, but there is less of it (i was using 4 cbos). This may need some tweaking as it is off the top of my head for the follow up revised form:
' additional array to hold the CBOs involved
Private tgtCBOs As ComboBox()
...
' initialization:
OptList.AddRange(New String() {"(None)", "Red", "Green", "Blue", "Violet",
"Orange", "Mauve", "White"})
tgtCBOs = New ComboBox() {cb1, cb2, cb3, cb4}
' set initial index to 0 with a default item
Private Sub cb2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles _
cb2.SelectedIndexChanged, cb1.SelectedIndexChanged, cb3.SelectedIndexChanged
If ignore Then Exit Sub
Dim cbo = TryCast(sender, ComboBox)
' identify which one this is
Dim ndx = Array.IndexOf(tgtCBOs, cbo)
' get all the selections from 0 to here
Dim exclude = GetExclusionList(ndx)
' remove excludes from the NEXT cbo
Dim newList As New List(Of String)
If ndx + 1 < tgtCBOs.Length Then
newList = OptList.Except(exclude).ToList()
If newList(0) <> OptList(0) Then newList.Insert(0, "(None)")
tgtCBOs(ndx + 1).DataSource = newList.ToArray()
End If
End Sub
Private Function GetExclusionList(ndx As Int32) As List(Of String)
Dim exclude As New List(Of String)
For n As Int32 = 0 To ndx
If tgtCBOs(n).SelectedIndex <> -1 Then
exclude.Add(tgtCBOs(n).Items(tgtCBOs(n).SelectedIndex).ToString())
End If
Next
Return exclude
End Function
Note that the last cbo is not connected to that handler because there it has no "slave" . Connect it to its own handler if you need to respond to the event.
Also, the act of resetting the DataSource
for the "next" (or clearing Items for that matter) will cause that event to fire for the next/child/slave CBO. So a change to CBO2 will fire the event for 3, 4, 5..n. It also resets the previous selection in the child/slave/next ones.