Reputation: 1598
I have three combo boxes on a WinForm
as the following:
___________________________
ComboBox List 1: |_______________________|___|
___________________________
ComboBox List 2: |_______________________|___|
___________________________
ComboBox List 3: |_______________________|___|
Each of these combo boxes will have, at design-time, a list of "A", "B", "C".
By default, List 1 is the only one active on the form and List 2 and List 3 will become active when its predecessor is given a selection.
What I would like to do, is iF the user choose option C, I would like to have Option C no longer be available for list 2 and 3.
I know this will involve the .SelectedIndexChanged
event of the combobox but do not know where to get started with the coding.
I found the following answer on StackOverflow, how my situation doesn't apply since I'm supplying the items at design time and not via an import of a file: vb.net change combobox options depending on selected item in 2 previous comboboxes
Upvotes: 1
Views: 4443
Reputation: 38905
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.
Upvotes: 2
Reputation: 216343
Well, suppose that your comboboxes are named b1, b2 and b3. All these combos are linked to the same SelectedIndexChanged
eventhandler and (as you say) you have set the initial elements on the b1,b2 and b3 to a fixed list of string.
Now you should also set the Enabled property of b2 and b3 to false and let the selection on b1 drive your logic in the SelectedIndexChanged event
Sub onIndexChanged(sender As Object, e As EventArgs)
Dim b As ComboBox = CType(sender, System.Windows.Forms.ComboBox)
If b.Equals(b1) Then
If b1.SelectedIndex = -1 Then
b2.Enabled = False
b3.Enabled = False
Else
b2.Enabled = True
b3.Enabled = False
b2.Items.Clear()
b3.Items.Clear()
b2.Items.AddRange(b1.Items.Cast(Of String)() _
.Where(Function(x) x <> b1.SelectedItem.ToString()).ToArray())
End If
Else if b.Equals(b2) Then
If b2.SelectedIndex <> -1 Then
b3.Items.Clear()
b3.Items.AddRange(b2.Items.Cast(Of String)(). _
Where(Function(x) x <> b2.SelectedItem.ToString()).ToArray())
b3.Enabled = True
b3.SelectedIndex = 0
End If
End If
End Sub
Upvotes: 1