You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改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

关键修改点说明

  1. 修正列属性类型:将ValueMember和DisplayMember的类型从Object改为String,符合ComboBox的属性要求。
  2. 重构编辑控件初始化逻辑:删除原代码中手动添加当前单元格文本到下拉项、遍历数据源构建下拉列表的代码,直接使用列的DataSource、DisplayMember、ValueMember进行绑定,确保所有行共用同一数据源。
  3. 保留手动输入功能:由于DropDownStyle设置为DropDown,依然允许用户手动输入不在下拉列表中的自定义文本。

内容的提问来源于stack exchange,提问作者Dakota Stammen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 20:05:23