0

I've got part of my code that creates a tab in a tabcontrol, and then fills it with a datagridview which contains a couple columns that are DataGridViewComboBoxColumn.

It looks like this:

Private Sub NewTabPage()
    Dim TabPageCount As Integer = RacerOrderTAB.TabPages.Count
    RacerOrderTAB.TabPages.Add(TeamNames(TabPageCount)) 'teamnames() is an array of team names

    Dim CurrentTabPage = RacerOrderTAB.TabPages(TabPageCount)
    Dim GridToAdd As New DataGridView

    GridToAdd.Size = CurrentTabPage.Size
    GridToAdd.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
    GridToAdd.Location = New Point(CurrentTabPage.Location.X, CurrentTabPage.Location.Y)
    GridToAdd.Columns.Add("ShiftCOL", "Shift Name")
    GridToAdd.Name = "grid_" & CurrentTabPage.Text

    For y As Integer = 1 To ShiftSetup.racerspershift 'add extra column for each racer in shift

        Dim cmb As New DataGridViewComboBoxColumn

        cmb.HeaderText = "Racer" & y
        cmb.Name = "Racer_" & y
        cmb.MaxDropDownItems = AmountOfRacers
        cmb.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton

        GridToAdd.Columns.Add(cmb)
    Next

    RacerOrderTAB.TabPages(TabPageCount).Controls.Add(GridToAdd)
End Sub

But I've been having difficulty in adding an eventhandler for the comboboxes. What I want to happen is that when a combobox is clicked and opened, I populate it with the items I want. I managed to vaguely get it working by adding:

AddHandler GridToAdd.EditingControlShowing, AddressOf <sub name> 

but then have been unable to figure out which combobox was clicked, and how to populate it. It's also been requiring like four clicks before the drop list will appear. I'm only slightly very confused.

Thanks for any advice; these DataGridViewComboBoxColumns [deep breath] have been confusing me a lot!

  • There are usually no `ComboBox` controls in the column and never more than one, so there's never any need to determine which one was clicked. In the `EditingControlShowing` event handler, the one and only `ComboBox` can be accessed via the `e.Control` property. – jmcilhinney May 26 '17 at 03:18
  • It is unclear what you want the combo boxes to contain. The posted code appears to make numerous combo box columns, however there is nothing in the posted code that adds items to those combo boxes. Can you clarify what you want the combo boxes to contain. Either they all have the same values or each has its own values but the code does neither. – JohnG May 28 '17 at 02:05
  • @JohnG The idea was that I would be sent whichever combobox the user clicked, and I could populate it then at runtime. – Harry Allen May 29 '17 at 02:13
  • It is easy to determine _”whichever combobox the user clicked_”. However, to _”populate it then at runtime”_... is confusing. What determines what the combo box should be populated with? You may want clarify your question. You may want to peruse… [How do I ask a good question?](http://stackoverflow.com/help/how-to-ask) … AND [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) – JohnG May 29 '17 at 07:52
  • @JohnG Sorry, I must be getting my terms mixed up. The idea was that when a combobox was clicked, I would fill it with a bunch of items (these items are based upon other items selected in the other comboboxes so they need to be filled at 'runtime' ie when the program is running and the combobox is opened) – Harry Allen May 30 '17 at 11:54
  • I understand filling the combo boxes at runtime. I posted an answer that changes the values in a combo box based on the value of another cell in that row. [C# Datagridview - Combobox With Different Values Per Row](https://stackoverflow.com/questions/43932832/c-sharp-datagridview-combobox-with-different-values-per-row/43952472#43952472) From your last comment, _”these items are based upon other items selected in the other comboboxes”_... implies there is more than one condition to determine what to populate the other combo boxes with. – JohnG May 30 '17 at 19:21
  • There are several ways to do this. If all the combo boxes are unique, then obviously you will have to maintain each row’s combo boxes whenever the user changes one of the combo boxes values. Since your question is vague in the sense that it is unclear as to WHAT the combo boxes would initially be filled with AND how you want each successive combo box to change, any answer would be pure speculation at this point. – JohnG May 30 '17 at 19:21
  • Again, you may want to clarify what each combo box would contain and if each row of combo boxes are unique. It is difficult to picture what this would look like. I am guessing an example would be a better way to explain what you are trying to achieve. Playing twenty questions is a waste of time. It may be easier to give a step-by-step example of what you want each combo box to contain. Initially, what would the combo boxes contain if you simply filled the grid and before the user made any changes? – JohnG May 30 '17 at 19:21
  • Email is not going to happen, however I can create a chat room. I am looking at your code and am stumped as to what you are trying to achieve. Again, further clarification of what you want to happen is needed. – JohnG Jun 08 '17 at 04:41
  • Hi @JohnG, would a chatroom be possible? The above code is what I have used to create a new TabControl Page, then add a datagridview, then populate it with columns of comboboxescolumns. Currently, I've managed to add a handler dynamically to the combobox when clicked: so when it dropdowns it triggers a sub that fills it the dropdown with the items that are currently present in a list. I want each combobox to take into account the selected items of the other comboboxes in the same row and not display those items, essentially. – Harry Allen Jun 09 '17 at 05:43

1 Answers1

1

It may be a little hacky but it should do what you are asking… hopefully. I created two List(Of String) variables. AllRacers contains all the racers… i.e. all the names we want to appear in a combo box such that there is no other combo box on that row that has an item selected. These names are what all combo boxes on all rows would initially contain in the selectable items list.

The other List(Of String) UsedRacers contains a list of all the “comboboxes” on the current row that have selected items. Each time a cell value is changed and it is one of the “combobox” column cells, then UsedRacers is cleared/updated to reflect the added/changed selected item on the current row.

When a “comboBox” cell value is changed, SetUsedRacersForRow is called…

Private Sub SetUsedRacersForRow(rowIndex As Int16)
  UsedRacers.Clear()
  Dim curValue = ""
  For i = 1 To racersPerShift
    If (Not (dgvRacers.Rows(rowIndex).Cells(i).Value Is Nothing)) Then
      curValue = dgvRacers.Rows(rowIndex).Cells(i).Value.ToString()
      If (Not (String.IsNullOrEmpty(curValue))) Then
        UsedRacers.Add(curValue)
      End If
    End If
  Next
End Sub

The code above loops through all the “combobox” cells in the given row and if a “combobox” cell has something selected, the selected value is added to the UsedRacers list.

Now that the selected items for all the “comboboxes” in that row are in the UsedRacers list, we can now loop through each “combobox” cell in that row and set the proper list of names. To help, a method is created that returns a DataGridViewComboBoxCell such that the names in the current UsedRacers list will NOT be in the DataGridViewComboBoxCell’s list of selectable names.

The only issue here would be with cells that currently have an item selected. Each “combobox” cell with an item selected will uniquely need to have its selected item in its list of items. To remedy this, a check is needed to see if the “combobox” cell contains a value. If the “combobox” cell DOES contain a selected value, then this value is also contained in the UsedRacers list. Since THIS cell is the cell that is in the UseRacers list… then we need to ADD this value to this cells items list. Otherwise, we would not be able to display the unique selection.

To keep the UsedRacers list consistent, we need to add this item directly to the individual “combobox” cell and not remove or alter the UsedRacers list as this will be used for the other “combobox” cells. In other words… whatever value is selected in a combo box, we need to make sure it is one of the items in the “combobox’s” list of selectable items. I hope that makes sense.

This can all be done in the DataGridViews CellChanged event.

Private Sub dgvRacers_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgvRacers.CellValueChanged
  If (e.ColumnIndex >= 1 And e.ColumnIndex <= racersPerShift) Then
    SetUsedRacersForRow(e.RowIndex)
    For i = 1 To racersPerShift
      Dim newCell As DataGridViewComboBoxCell = GetCurrentComboBoxCell()
      If (Not (dgvRacers.Rows(e.RowIndex).Cells(i).Value Is Nothing)) Then
        Dim curValue = dgvRacers.Rows(e.RowIndex).Cells(i).Value.ToString()
        newCell.Items.Add(curValue)
        newCell.Value = curValue
      End If
      dgvRacers.Rows(e.RowIndex).Cells(i) = newCell
    Next
  End If
End Sub

In the code above, a method GetCurrentComboBoxCell (below) returns a DataGridViewComboBoxCell such that the items in the combo boxes list of items does not contain any items that are in the UsedRacers list. Because of this, a check is needed (above) to see if the cell already contains a value. NOTE: the DataGridViewComboBoxCell returned will always contain a “blank” empty item. This is necessary to allow the user to “De-Select” any currently selected value and then make the “De-Selected” item available to the other combo box cells.

Public Function GetCurrentComboBoxCell() As DataGridViewComboBoxCell
  Dim newComboCell = New DataGridViewComboBoxCell()
  newComboCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
  newComboCell.FlatStyle = FlatStyle.Flat
  newComboCell.Items.Add("")
  For Each curRacer In AllRacers
    If (Not UsedRacers.Contains(curRacer)) Then
      newComboCell.Items.Add(curRacer)
    End If
  Next
  Return newComboCell
End Function

Finally, putting all this together…

Dim racersInShift = 3
Dim AllRacers As List(Of String) = New List(Of String) From {"John", "Bobby", "Trent", "Josh", "Chapman", "Henry", "George", "Marvin"}
'Dim racersPerShift As Int16 = AllRacers.Count '<-- should be MAX value
Dim racersPerShift As Int16 = 4
Dim UsedRacers = New List(Of String)

Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  BuildGrid()
End Sub

Private Sub BuildGrid()
  dgvRacers.Size = New Size(800, 200)
  dgvRacers.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
  'dgvRacers.Location = New Point(50, 200)
  dgvRacers.Columns.Add("ShiftCOL", "Shift Name")
  dgvRacers.Name = "RacersDGV"
  dgvRacers.EditMode = DataGridViewEditMode.EditOnEnter
  dgvRacers.AllowUserToAddRows = False
  AddRacerColumns()
  AddRacerRows()
End Sub

Private Sub AddRacerColumns()
  Dim newColumn As DataGridViewComboBoxColumn
  For i As Integer = 1 To racersPerShift
    newColumn = GetNewComboBoxColumn("Racer" & i, "Racer " & i)
    dgvRacers.Columns.Add(newColumn)
  Next
End Sub

Private Sub AddRacerRows()
  For i As Integer = 1 To racersInShift
    Dim row As New DataGridViewRow
    dgvRacers.Rows.Add(row)
  Next
End Sub

Private Sub dgvRacers_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) 
   ‘See code above
End Sub

Private Sub SetUsedRacersForRow(rowIndex As Int16)
  ‘See code above
End Sub

Public Function GetCurrentComboBoxCell() As DataGridViewComboBoxCell
  ‘See code above
End Function

‘Lastly a method to set a whole `DataGridviewComboBoxColumn` which is used to initialize all the combo box columns

Public Function GetNewComboBoxColumn(colName As String, colHeader As String) As DataGridViewComboBoxColumn
  Dim newComboCol = New DataGridViewComboBoxColumn()
  newComboCol.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
  newComboCol.FlatStyle = FlatStyle.Flat
  newComboCol.Items.Add("")
  newComboCol.HeaderText = colHeader
  newComboCol.Name = colName
  For Each curRacer In AllRacers
    newComboCol.Items.Add(curRacer)
  Next
  Return newComboCol
End Function

I hope this helps, I am guessing there is an easier way to do this.

JohnG
  • 9,259
  • 2
  • 20
  • 29