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

VB.NET中CellValueChanged后DataTable表达式列不即时更新问题

VB.NET中CellValueChanged事件下表达式列无法即时更新的解决方法

问题说明

修改DataGridView单元格值后,关联的DataTable表达式列MATERIALCOLORNAME无法即时更新,操作过程如下:
CellValueChanged操作过程

相关代码

DataTable创建代码

Private Sub CreateDataTable()
    If m_DataTable IsNot Nothing Then Return
    m_DataTable = New DataTable
    'Columns
    m_DataTable.Columns.Add("CODEPRODUCT", GetType(String))
    m_DataTable.Columns.Add("DESCRIPTION", GetType(String))
    m_DataTable.Columns.Add("COLORCODE", GetType(String))
    m_DataTable.Columns.Add("COLORNAME", GetType(String))
    m_DataTable.Columns.Add("MATERIAL", GetType(String))
    m_DataTable.Columns.Add("MATERIALCOLORNAME", GetType(String), "TRIM(IIF(MATERIAL Is Null Or SUBSTRING(MATERIAL, 1, 1) = '-', '', MATERIAL) + IIF(COLORNAME Is Null Or SUBSTRING(COLORNAME, 1, 1) = '-', '', ' ' + COLORNAME))")
End Sub

其他核心代码

Public m_DataTable As DataTable

Public Function GetConnectionString2() As String
    Dim strCon As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\TRIAL.accdb;Persist Security Info=False;"
    Return strCon
End Function

Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
    If e.RowIndex < 0 OrElse e.RowIndex < 0 Then Return
    Dim dgv = DirectCast(sender, DataGridView)
    'If Not dgv.IsCurrentCellDirty Then Return
    Dim row = dgv.Rows(e.RowIndex)
    Dim arrQueries() = {
        New With {
            .KeyCell = row.Cells("CODEPRODUCT"),
            .ValueCell = row.Cells("DESCRIPTION"),
            .Table = "PRODUCT"},
        New With {
            .KeyCell = row.Cells("SIZEPRODUCT"),
            .ValueCell = row.Cells("CATEGORY"),
            .Table = "SIZEPRODUCT"},
        New With {
            .KeyCell = row.Cells("COLORCODE"),
            .ValueCell = row.Cells("COLORNAME"),
            .Table = "COLORCODE"},
        New With {
            .KeyCell = row.Cells("COLORCODE"),
            .ValueCell = row.Cells("MATERIAL"),
            .Table = "COLORCODE"}
    }
    If Not arrQueries.Any(
        Function(q) q.KeyCell.OwningColumn Is dgv.Columns(e.ColumnIndex)) Then
        Return
    End If
    Using con = New OleDbConnection(GetConnectionString2()), cmd = con.CreateCommand()
        con.Open()
        For Each q In arrQueries
            If q.KeyCell.Value IsNot Nothing AndAlso
                q.KeyCell.Value IsNot DBNull.Value AndAlso
                Not String.IsNullOrEmpty(q.KeyCell.Value.ToString()) Then
                cmd.CommandText = String.Format(
                    "SELECT {0} FROM {1} WHERE {2} = ?",
                    q.ValueCell.OwningColumn.Name, q.Table, q.KeyCell.OwningColumn.Name)
                cmd.Parameters.Clear()
                cmd.Parameters.Add("?", OleDbType.VarWChar).Value = q.KeyCell.Value
                q.ValueCell.Value = cmd.ExecuteScalar()
            Else
                q.ValueCell.Value = Nothing
            End If
        Next
    End Using
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    CreateDataTable()
    DataGridView1.DataSource = m_DataTable
End Sub

问题原因

直接修改DataGridView单元格的Value属性,不会触发底层DataTable对应DataRow的变更通知机制,导致表达式列无法自动重新计算值。

解决方法

方法一:操作DataTable的DataRow(推荐)

放弃直接修改DataGridView单元格,转而操作绑定的DataRowView/DataRow,这样能触发DataTable的变更通知,表达式列会自动更新:

修改后的DataGridView1_CellValueChanged事件代码:

Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
    If e.RowIndex < 0 Then Return
    Dim dgv = DirectCast(sender, DataGridView)
    Dim row = dgv.Rows(e.RowIndex)
    ' 获取绑定的DataRowView
    Dim dataRowView = DirectCast(row.DataBoundItem, DataRowView)
    If dataRowView Is Nothing Then Return

    Dim arrQueries() = {
        New With {
            .KeyColName = "CODEPRODUCT",
            .ValueColName = "DESCRIPTION",
            .Table = "PRODUCT"},
        New With {
            .KeyColName = "SIZEPRODUCT",
            .ValueColName = "CATEGORY",
            .Table = "SIZEPRODUCT"},
        New With {
            .KeyColName = "COLORCODE",
            .ValueColName = "COLORNAME",
            .Table = "COLORCODE"},
        New With {
            .KeyColName = "COLORCODE",
            .ValueColName = "MATERIAL",
            .Table = "COLORCODE"}
    }

    Dim changedColName = dgv.Columns(e.ColumnIndex).Name
    If Not arrQueries.Any(Function(q) q.KeyColName = changedColName) Then
        Return
    End If

    Using con = New OleDbConnection(GetConnectionString2()), cmd = con.CreateCommand()
        con.Open()
        For Each q In arrQueries
            Dim keyValue = dataRowView(q.KeyColName)
            If keyValue IsNot DBNull.Value AndAlso Not String.IsNullOrEmpty(keyValue.ToString()) Then
                cmd.CommandText = String.Format(
                    "SELECT {0} FROM {1} WHERE {2} = ?",
                    q.ValueColName, q.Table, q.KeyColName)
                cmd.Parameters.Clear()
                cmd.Parameters.Add("?", OleDbType.VarWChar).Value = keyValue
                ' 修改DataRowView的值,触发表达式列更新
                dataRowView(q.ValueColName) = cmd.ExecuteScalar()
            Else
                dataRowView(q.ValueColName) = DBNull.Value
            End If
        Next
    End Using
End Sub

方法二:强制触发DataRow编辑结束

如果坚持直接修改DataGridView单元格,可在修改完成后调用EndEdit()触发表达式列重新计算,再刷新DataGridView:

在原有事件代码的Using块结束后添加:

' 触发DataRow编辑结束,强制计算表达式列
DirectCast(row.DataBoundItem, DataRowView).EndEdit()
' 刷新DataGridView确保显示最新值
dgv.Refresh()

来源说明

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:17:06