修改DataGridView自定义ComboBox列实现全行统一下拉列表并支持手动输入
修改方案:让DataGridView可输入ComboBox列所有行共用同一下拉列表
原代码的问题在于每次编辑单元格时,都会把当前单元格的文本添加到下拉项中,再从数据源逐个添加去重的选项,导致每行的下拉列表包含当前行的自定义值+数据源选项,因此每行内容不同。以下是修改后的代码,核心是直接绑定列的数据源,而非手动构建下拉项:
修改后的完整代码
Public Class DataGridViewDropDownComboBoxColumn Inherits DataGridViewColumn Public Sub New() MyBase.New(New DataGridViewDropDownComboBoxCell) End Sub ' 修正属性类型为正确的String类型 Public Property DropDownStyle As ComboBoxStyle Public Property DataSource As Object Public Property ValueMember As String Public Property DisplayMember As String Public Overrides Property CellTemplate As DataGridViewCell Get Return MyBase.CellTemplate End Get Set If Not (Value Is Nothing) AndAlso Not Value.GetType.IsAssignableFrom(GetType(DataGridViewDropDownComboBoxCell)) Then Throw New InvalidCastException("必须是DropDownCell类型") End If MyBase.CellTemplate = Value End Set End Property End Class Public Class DataGridViewDropDownComboBoxCell Inherits DataGridViewTextBoxCell Public Sub New() MyBase.New End Sub Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As DataGridViewCellStyle) MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle) Dim ctl As DataGridViewDropDownComboBoxEditingControl = CType(DataGridView.EditingControl, DataGridViewDropDownComboBoxEditingControl) ' 设置编辑控件的初始文本为当前单元格值 If Me.Value Is Nothing Or IsDBNull(Me.Value) Then ctl.Text = CType(Me.DefaultNewRowValue, String) Else ctl.Text = CType(Me.Value, String) End If End Sub Public Overrides ReadOnly Property EditType As Type Get Return GetType(DataGridViewDropDownComboBoxEditingControl) End Get End Property Public Overrides ReadOnly Property ValueType As Type Get Return GetType(String) End Get End Property Public Overrides ReadOnly Property DefaultNewRowValue As Object Get Return "" End Get End Property End Class Class DataGridViewDropDownComboBoxEditingControl Inherits ComboBox Implements IDataGridViewEditingControl Private dataGridView As DataGridView Private valueChanged As Boolean = False Private rowIndex As Integer Public Sub New() MyBase.New End Sub Public Shadows Property DropDownStyle() As ComboBoxStyle Get Return MyBase.DropDownStyle End Get Set(ByVal value As ComboBoxStyle) If value = ComboBoxStyle.Simple Then value = ComboBoxStyle.DropDown End If MyBase.DropDownStyle = value End Set End Property Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue Get Return Me.Text End Get Set If TypeOf Value Is String Then Me.Text = CStr(Value) End If End Set End Property Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue Return Me.EditingControlFormattedValue End Function Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl Me.Font = dataGridViewCellStyle.Font Me.ForeColor = dataGridViewCellStyle.ForeColor Me.BackColor = dataGridViewCellStyle.BackColor End Sub Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex Get Return Me.rowIndex End Get Set Me.rowIndex = Value End Set End Property Public Function EditingControlWantsInputKey(ByVal key As Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey Select Case (key And Keys.KeyCode) Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp, Keys.F4 Return True Case Else Return Not dataGridViewWantsInputKey End Select End Function Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit Dim col As DataGridViewDropDownComboBoxColumn = CType(dataGridView.Columns(dataGridView.CurrentCell.ColumnIndex), DataGridViewDropDownComboBoxColumn) If col Is Nothing Then Throw New InvalidCastException("必须在DropDownComboBoxColumn列中") End If ' 直接绑定列的数据源,确保所有行共用同一下拉列表 Me.DropDownStyle = col.DropDownStyle Me.DataSource = col.DataSource Me.DisplayMember = col.DisplayMember Me.ValueMember = col.ValueMember ' 设置当前文本为单元格值,保留手动输入的能力 If IsDBNull(dataGridView.CurrentCell.Value) Then Me.Text = "" Else Me.Text = CType(dataGridView.CurrentCell.Value, String) End If End Sub Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange Get Return False End Get End Property Public Property EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView Get Return Me.dataGridView End Get Set Me.dataGridView = Value End Set End Property Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged Get Return Me.valueChanged End Get Set Me.valueChanged = Value End Set End Property Public ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor Get Return MyBase.Cursor End Get End Property Protected Overrides Sub OnTextChanged(ByVal eventargs As EventArgs) Me.valueChanged = True Me.EditingControlDataGridView.NotifyCurrentCellDirty(True) MyBase.OnTextChanged(eventargs) End Sub Protected Overrides Sub OnSelectedIndexChanged(ByVal e As EventArgs) Me.valueChanged = True Me.EditingControlDataGridView.NotifyCurrentCellDirty(True) MyBase.OnSelectedIndexChanged(e) End Sub End Class
关键修改点说明
- 修正列属性类型:将
ValueMember和DisplayMember的类型从Object改为String,符合ComboBox的属性要求。 - 重构编辑控件初始化逻辑:删除原代码中手动添加当前单元格文本到下拉项、遍历数据源构建下拉列表的代码,直接使用列的
DataSource、DisplayMember、ValueMember进行绑定,确保所有行共用同一数据源。 - 保留手动输入功能:由于
DropDownStyle设置为DropDown,依然允许用户手动输入不在下拉列表中的自定义文本。
内容的提问来源于stack exchange,提问作者Dakota Stammen
相关产品推荐
相关产品推荐

