WinForms DataGridView同列行级多控件切换实现方案问询
DataGridView同列行级多控件切换可行方案
初始方案错误分析
直接替换单元格对象的方式存在核心问题:
cllValueField = cboConfigured仅修改局部变量引用,并未实际替换DataGridView行内的单元格,需改为dgv.Rows(cboFeature.RowIndex).Cells(valueColumn.Index) = cboConfiguredCellValueChanged会在编辑过程中多次触发,建议改用CellEndEdit确保用户选择完成后再处理- DataError多因值类型不匹配、ComboBox选中值不在数据源范围内导致,需严格保证值与控件类型的一致性
方案一:利用CellBeginEdit动态切换编辑控件(无需自定义列)
通过监听单元格编辑开始/结束事件,动态替换编辑控件并同步值,实现轻量型的控件切换。
核心代码
' 编辑开始时切换控件 Private Sub dgv_CellBeginEdit(sender As Object, e As DataGridViewCellCancelEventArgs) Handles dgv.CellBeginEdit If e.ColumnIndex <> valueColumn.Index Then Return Dim row = dgv.Rows(e.RowIndex) Dim featureID = Convert.ToInt32(row.Cells(featureIDColumn.Index).Value) Dim matches = From r In dtbFeatureList Where r.Field(Of Integer)("ID") = featureID If Not matches.Any Then Return Dim controlType = matches.First().Field(Of String)("FieldControl") Dim editControl As Control = Nothing Select Case controlType Case "Checkbox" Dim chk As New CheckBox() chk.Checked = Convert.ToBoolean(row.Cells(valueColumn.Index).Value) chk.Dock = DockStyle.Fill editControl = chk Case "Textbox" Dim txt As New TextBox() txt.Text = row.Cells(valueColumn.Index).Value?.ToString() txt.Dock = DockStyle.Fill editControl = txt Case "Combobox" Dim cbo As New ComboBox() Dim dvOptions As New DataView(dtbFeatureValueList) dvOptions.RowFilter = $"[FeatureID] = {featureID}" cbo.DataSource = dvOptions.ToTable() cbo.ValueMember = "Value" cbo.DisplayMember = "ValueText" cbo.SelectedValue = row.Cells(valueColumn.Index).Value cbo.Dock = DockStyle.Fill editControl = cbo End If If editControl IsNot Nothing Then dgv.EditingControl = editControl End If End Sub ' 编辑结束后同步值到单元格 Private Sub dgv_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellEndEdit If e.ColumnIndex <> valueColumn.Index Then Return Dim editControl = dgv.EditingControl Dim cell = dgv.Rows(e.RowIndex).Cells(valueColumn.Index) Select Case editControl.GetType() Case GetType(CheckBox) cell.Value = DirectCast(editControl, CheckBox).Checked Case GetType(TextBox) cell.Value = DirectCast(editControl, TextBox).Text Case GetType(ComboBox) cell.Value = DirectCast(editControl, ComboBox).SelectedValue End Select dgv.InvalidateCell(cell) End Sub ' 非编辑状态下自定义显示样式 Private Sub dgv_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgv.CellFormatting If e.ColumnIndex <> valueColumn.Index OrElse e.RowIndex = dgv.NewRowIndex Then Return Dim row = dgv.Rows(e.RowIndex) Dim featureID = Convert.ToInt32(row.Cells(featureIDColumn.Index).Value) Dim matches = From r In dtbFeatureList Where r.Field(Of Integer)("ID") = featureID If Not matches.Any Then Return Dim controlType = matches.First().Field(Of String)("FieldControl") Select Case controlType Case "Checkbox" e.Value = If(Convert.ToBoolean(e.Value), "√", "×") e.FormattingApplied = True Case "Combobox" Dim dvOptions As New DataView(dtbFeatureValueList) dvOptions.RowFilter = $"[FeatureID] = {featureID} AND [Value] = '{e.Value}'" If dvOptions.Count > 0 Then e.Value = dvOptions(0)("ValueText") e.FormattingApplied = True End If End Select End Sub
方案二:修复自定义列实现
若需更封装的实现,需完善自定义单元格的编辑控件初始化、值同步与绘制逻辑:
自定义单元格类
Public Class DataGridViewCustomConfigurableCell Inherits DataGridViewTextBoxCell Public Property FeatureID As Integer Public Property ControlType As String Public Overrides Sub InitializeEditingControl(rowIndex As Integer, initialFormattedValue As Object, dataGridViewCellStyle As DataGridViewCellStyle) MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle) Dim host = TryCast(DataGridView.EditingControl, DataGridViewTextBoxEditingControl) If host Is Nothing Then Return host.Controls.Clear() Dim editControl As Control = Nothing Select Case ControlType Case "Checkbox" Dim chk As New CheckBox() chk.Checked = Convert.ToBoolean(Value) chk.Dock = DockStyle.Fill AddHandler chk.CheckedChanged, AddressOf OnEditControlValueChanged editControl = chk Case "Textbox" Dim txt As New TextBox() txt.Text = Value?.ToString() txt.Dock = DockStyle.Fill AddHandler txt.TextChanged, AddressOf OnEditControlValueChanged editControl = txt Case "Combobox" Dim cbo As New ComboBox() Dim dvOptions As New DataView(dtbFeatureValueList) dvOptions.RowFilter = $"[FeatureID] = {FeatureID}" cbo.DataSource = dvOptions.ToTable() cbo.ValueMember = "Value" cbo.DisplayMember = "ValueText" cbo.SelectedValue = Value cbo.Dock = DockStyle.Fill AddHandler cbo.SelectedValueChanged, AddressOf OnEditControlValueChanged editControl = cbo End If If editControl IsNot Nothing Then host.Controls.Add(editControl) End If End Sub Private Sub OnEditControlValueChanged(sender As Object, e As EventArgs) Dim editControl = TryCast(sender, Control) If editControl Is Nothing Then Return Select Case editControl.GetType() Case GetType(CheckBox) Value = DirectCast(editControl, CheckBox).Checked Case GetType(TextBox) Value = DirectCast(editControl, TextBox).Text Case GetType(ComboBox) Value = DirectCast(editControl, ComboBox).SelectedValue End Select End Sub Public Overrides Sub Paint(graphics As Graphics, clipBounds As Rectangle, cellBounds As Rectangle, rowIndex As Integer, cellState As DataGridViewElementStates, value As Object, formattedValue As Object, errorText As String, cellStyle As DataGridViewCellStyle, advancedBorderStyle As DataGridViewAdvancedBorderStyle, paintParts As DataGridViewPaintParts) MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts) If ControlType = "Checkbox" Then Dim chkRect = New Rectangle(cellBounds.X + 5, cellBounds.Y + (cellBounds.Height - 16) \ 2, 16, 16) ControlPaint.DrawCheckBox(graphics, chkRect, If(Convert.ToBoolean(value), ButtonState.Checked, ButtonState.Normal)) End If End Sub End Class
自定义列类
Public Class DataGridViewCustomConfigurableColumn Inherits DataGridViewColumn Public Sub New() MyBase.New(New DataGridViewCustomConfigurableCell()) End Sub Public Overrides Property CellTemplate As DataGridViewCell Get Return MyBase.CellTemplate End Get Set(value As DataGridViewCell) If value IsNot Nothing AndAlso Not TypeOf value Is DataGridViewCustomConfigurableCell Then Throw New InvalidCastException("必须使用DataGridViewCustomConfigurableCell作为单元格模板") End If MyBase.CellTemplate = value End Set End Property End Class
绑定数据与更新控件
Private Sub UpdateCustomColumnCells() For Each row As DataGridViewRow In dgv.Rows If row.IsNewRow Then Continue For Dim featureID = Convert.ToInt32(row.Cells(featureIDColumn.Index).Value) Dim matches = From r In dtbFeatureList Where r.Field(Of Integer)("ID") = featureID If Not matches.Any Then Continue For Dim controlType = matches.First().Field(Of String)("FieldControl") Dim cfgCell = DirectCast(row.Cells(cfgValueColumn.Index), DataGridViewCustomConfigurableCell) cfgCell.FeatureID = featureID cfgCell.ControlType = controlType cfgCell.Value = row.Cells(valueColumn.Index).Value Next dgv.Refresh() End Sub ' Feature列值变化时更新自定义列 Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged If e.ColumnIndex = cboFeatureColumn.Index AndAlso e.RowIndex <> dgv.NewRowIndex Then UpdateCustomColumnCells() End If End Sub
内容的提问来源于stack exchange,提问作者Alan O'Brien
相关产品推荐
相关产品推荐

