You don't mention if you are working on a
WPF or
WinForms app, nor do you mention if you are using
.Net 3.0+ or
.Net Framework 4.5+, so I will assume it is a
.Net Framework 4.5 WinForms app. You're using VB.Net, so I will do the same.
You can let the
DataGridView
control do all of the work for you, you do not need to do this manually. Below is a sample app to demonstrate this.
1. Create a new "Windows Forms app (.Net FrameWork) in Visual Studio
2. Drop a
DataGridView
control on the design surface, click on the arrow button in the top-right corner, and select "Doct to Container"
3. now go to code view and add the following code:
Public Class Form1
Public Property Widgets As List(Of Widget) = New List(Of Widget)()
Public Property Colors As List(Of String) = New List(Of String)() From
{
"Red",
"Green",
"Blue"
}
Public Sub New()
InitializeComponent()
CreateWidgets()
SetupDataGrid()
End Sub
Private Sub SetupDataGrid()
DataGridView1.DataSource = Widgets
DataGridView1.ColumnHeadersVisible = True
DataGridView1.AutoGenerateColumns = False
DataGridView1.EditMode = DataGridViewEditMode.EditOnKeystroke
Dim NameColumn = New DataGridViewTextBoxColumn()
NameColumn.HeaderText = "Name"
NameColumn.ReadOnly = True
NameColumn.DataPropertyName = "Name"
Dim comboBoxColumn = New DataGridViewComboBoxColumn()
comboBoxColumn.HeaderText = "Color"
comboBoxColumn.DataSource = Colors
comboBoxColumn.DataPropertyName = "Color"
DataGridView1.Columns.Clear()
DataGridView1.Columns.Add(NameColumn)
DataGridView1.Columns.Add(comboBoxColumn)
End Sub
Private Sub CreateWidgets()
Dim colorIndex As Integer = 0
For i As Integer = 0 To 9
Widgets.Add(New Widget() With {
.Name = $"Widget {i}",
.Color = Colors(colorIndex)
})
colorIndex += 1
If colorIndex = Colors.Count Then
colorIndex = 0
End If
Next
End Sub
End Class
Public Class Widget
Public Property Name As String
Public Property Color As String
End Class
Now run the app. You can change the values in the ComboBox in each column and the value selected remains.
The
SetupDataGrid()
subroutine manually configures the
DataGridView
control, first the Control, then each column.
UPDATE
That is a little different. For the
DataGridViewTextBoxColumn
, we cannot set the
DataSource
for the column as it is readonly. If you do use the
DataSource
for the column, then you will see the following
unhandled exception in the
OnCellValidating
event when trying to update the column's
Items
collection:
System.ArgumentException: 'Items collection cannot be modified when the DataSource property is set.'
However, from what I have read, you can update it
if we are using a DataTable
.
So here is the above solution altered to support in-cell
ComboBox
editing.
Partial Public Class Form1
Inherits Form
Public Property Widgets As List(Of Widget) = New List(Of Widget)()
Public Property Colors As List(Of String) = New List(Of String)() From {
"Red",
"Green",
"Blue"
}
Public Sub New()
InitializeComponent()
CreateWidgets()
SetupDataGrid()
End Sub
Private Sub SetupDataGrid()
DataGridView1.DataSource = Widgets
DataGridView1.ColumnHeadersVisible = True
DataGridView1.AutoGenerateColumns = False
DataGridView1.EditMode = DataGridViewEditMode.EditOnKeystroke
Dim NameColumn = New DataGridViewTextBoxColumn()
NameColumn.HeaderText = "Name"
NameColumn.ReadOnly = True
NameColumn.DataPropertyName = "Name"
Dim comboBoxColumn = New DataGridViewComboBoxColumn()
comboBoxColumn.HeaderText = "Color"
For Each color As String In Colors
comboBoxColumn.Items.Add(color)
Next
comboBoxColumn.DataPropertyName = NameOf(Widget.Color)
DataGridView1.Columns.Clear()
DataGridView1.Columns.Add(NameColumn)
DataGridView1.Columns.Add(comboBoxColumn)
AddHandler DataGridView1.CellValidating, AddressOf OnCellValidating
AddHandler DataGridView1.EditingControlShowing, AddressOf OnEditingControlShowing
End Sub
Private Sub CreateWidgets()
Dim colorIndex As Integer = 0
For i As Integer = 0 To 9
Widgets.Add(New Widget() With {
.Name = $"Widget {i}",
.Color = Colors(colorIndex)
})
colorIndex += 1
If colorIndex = Colors.Count Then
colorIndex = 0
End If
Next
End Sub
Private Sub OnEditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs)
Dim column = TryCast(DataGridView1.Columns(1), DataGridViewComboBoxColumn)
Dim cellIndex As Integer = DataGridView1.CurrentCellAddress.X
Dim type As Type = e.Control.GetType()
If cellIndex <> column.DisplayIndex OrElse type <> GetType(DataGridViewComboBoxEditingControl) Then
Return
End If
Dim cb As ComboBox = TryCast(e.Control, ComboBox)
If cb IsNot Nothing Then
cb.DropDownStyle = ComboBoxStyle.DropDown
End If
End Sub
Private Sub OnCellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs)
Dim column = TryCast(DataGridView1.Columns(1), DataGridViewComboBoxColumn)
Dim cellIndex As Integer = DataGridView1.CurrentCellAddress.X
If cellIndex <> column.DisplayIndex Then
Return
End If
Dim color As String = If(TryCast(e.FormattedValue, String), String.Empty)
If String.IsNullOrWhiteSpace(color) OrElse Colors.Contains(color) Then
Return
End If
Colors.Add(color)
column.Items.Add(e.FormattedValue)
DataGridView1.CurrentCell.Value = e.FormattedValue
End Sub
End Class
Public Class Widget
Public Property Name As String
Public Property Color As String
End Class
The code will work (tested) in both .Net Framework or Dot Net Core.