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

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值不在新列表中,仍会触发验证错误。

正确实现思路及代码

  1. 精准触发逻辑:仅在编辑Size列时加载对应数据源,获取当前行Material列的有效值。
  2. 参数化查询:避免SQL注入风险。
  3. 预处理旧值:加载新数据源前清空当前单元格旧值,避免验证冲突。
  4. 捕获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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 00:23:11