DataGridViewComboBox切换值时提示无效值的问题排查
动态加载DataGridView下拉框选项时的“DataGridView value is not Valid”问题解析
问题背景
我有一个包含两个单元格的DataGridView,其中Size下拉框的选项根据Material下拉框的选择动态加载。首次选择Material并选择Size时无报错,但切换其他Material时出现“DataGridView value is not Valid”错误。
第一种实现(CellValueChanged事件)的问题
原代码:
Private Sub dgMaterial_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgMaterial.CellValueChanged Dim dTableSize As New DataTable Dim lngID as Long If e.ColumnIndex = 0 AndAlso e.RowIndex >= 0 Then lngID = dgMaterial.Rows(e.RowIndex).Cells(e.ColumnIndex).Value Dim AdSize As New SqlDataAdapter("SELECT ID, sizeY from ItemSC WHERE ParentID = " & lngID & "ORDER BY sizeY", con) Dim str As String AdSize.Fill(dTableSize) dgmSize.DataSource = dTableSize dgmSize.DisplayMember = "sizeY" dgmSize.ValueMember = "ID" End If End Sub
这种实现不正确,核心问题:
- CellValueChanged在单元格值确认后触发,切换Material后,之前选中的Size值大概率不在新加载的选项列表中,DataGridView验证单元格值时直接抛出错误。
- 未处理旧值与新数据源的匹配问题,直接替换下拉框数据源会触发验证失败。
- 存在SQL注入风险,直接将
lngID拼接进SQL语句,未使用参数化查询。
第二种实现(EditingControlShowing事件)的问题
原代码:
Private Sub dgMaterial_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgMaterial.EditingControlShowing Dim dTableSize As New DataTable If TypeOf e.Control Is ComboBox Then Dim AdSize As New SqlDataAdapter("SELECT ID, sizeY from ItemSC WHERE ParentID = " & lngIDMat & "ORDER BY sizeY", con) AdSize.Fill(dTableSize) dgmUkuran.DisplayMember = "sizeY" dgmUkuran.ValueMember = "ID" dgmUkuran.DataSource = dTableSize End If End Sub
这种实现也不正确,核心问题:
- 未判断当前编辑的是哪一列,所有ComboBox类型的单元格都会被加载Size数据源,逻辑混乱。
lngIDMat的取值逻辑缺失,无法确保获取到当前行Material列的正确值。- 同样存在SQL注入风险,未使用参数化查询。
- 加载新数据源前未清理当前单元格的旧值,切换Material后旧Size值不在新列表中,仍会触发验证错误。
正确实现思路及代码
- 精准触发逻辑:仅在编辑Size列时加载对应数据源,获取当前行Material列的有效值。
- 参数化查询:避免SQL注入风险。
- 预处理旧值:加载新数据源前清空当前单元格旧值,避免验证冲突。
- 捕获DataError:拦截验证错误提示,优雅处理无效值。
示例代码:
' 存储当前编辑的行索引 Private currentEditRowIndex As Integer = -1 Private Sub dgMaterial_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgMaterial.EditingControlShowing ' 仅处理Size列(假设Size列索引为1) If dgMaterial.CurrentCell.ColumnIndex = 1 AndAlso TypeOf e.Control Is ComboBox Then Dim cboSize As ComboBox = DirectCast(e.Control, ComboBox) currentEditRowIndex = dgMaterial.CurrentCell.RowIndex ' 获取当前行Material列的有效值 If dgMaterial.Rows(currentEditRowIndex).Cells(0).Value IsNot Nothing Then Dim lngIDMat As Long = CLng(dgMaterial.Rows(currentEditRowIndex).Cells(0).Value) Dim dTableSize As New DataTable ' 参数化查询构建数据源 Using cmd As New SqlCommand("SELECT ID, sizeY from ItemSC WHERE ParentID = @ParentID ORDER BY sizeY", con) cmd.Parameters.AddWithValue("@ParentID", lngIDMat) Using AdSize As New SqlDataAdapter(cmd) AdSize.Fill(dTableSize) End Using End Using ' 清空当前单元格旧值,避免验证错误 dgMaterial.Rows(currentEditRowIndex).Cells(1).Value = Nothing ' 绑定下拉框数据源 cboSize.DisplayMember = "sizeY" cboSize.ValueMember = "ID" cboSize.DataSource = dTableSize End If End If End Sub ' 拦截DataError,避免弹窗并处理无效值 Private Sub dgMaterial_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) Handles dgMaterial.DataError If e.ColumnIndex = 1 Then e.Cancel = True dgMaterial.Rows(e.RowIndex).Cells(e.ColumnIndex).Value = Nothing End If End Sub
内容的提问来源于stack exchange,提问作者Mr Bim
相关产品推荐
相关产品推荐

