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

VB.NET中DataGridView CellEndEdit触发StackOverflowException错误的解决

解决VB.NET中DataGridView的CellEndEdit触发StackOverflowException问题

问题描述

在VB.NET的DataGridView中,当回到编辑操作前的单元格时,CellEndEdit事件会触发StackOverflowException错误,需排查并解决此问题。

错误原因

出现栈溢出的核心原因是事件循环调用链:

  1. 在CellEndEdit事件中直接修改DataGridView单元格值(q.ValueCell.Value = cmd.ExecuteScalar())
  2. 单元格值变更触发CellValueChanged事件
  3. CellValueChanged事件调用UpdateColorIndices方法,该方法内执行DataGridView1.EndEdit()再次触发CellEndEdit事件
  4. 循环往复导致栈内存耗尽,触发溢出错误

此外还有两处次要问题:

  • CellEndEdit开头的行索引判断重复冗余:If e.RowIndex < 0 OrElse e.RowIndex < 0 Then Return
  • 代码中引用的SIZEPRODUCT和CATEGORY列未在CreateDataTable方法中定义,会引发运行时错误

解决方案

要打破循环调用链,需在修改单元格值时暂时禁用相关事件绑定,操作完成后再恢复:

  1. 在数据更新前,移除CellValueChanged和CellEndEdit的事件处理
  2. 完成数据库查询和单元格赋值后,重新绑定事件
  3. 手动触发一次颜色索引更新,保证界面同步
  4. 修复冗余判断和未定义列的问题

修改后的完整代码

Public Class Form1
  Private bmp As Bitmap
  Public m_DataTable As DataTable
  Private colors As Color() ' 补充声明缺失的colors变量

  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

  Public Sub New()
    InitializeComponent()
    ' 减少控件闪烁
    DataGridView1.GetType().
    GetProperty("DoubleBuffered",
                BindingFlags.Instance Or BindingFlags.NonPublic).
    SetValue(DataGridView1, True)
    ' 收集深色系颜色
    colors = GetType(Color).
    GetProperties(BindingFlags.Public Or BindingFlags.Static).
    Where(Function(pi) pi.PropertyType = GetType(Color)).
    Select(Function(pi) CType(pi.GetValue(GetType(Color), Nothing), Color)).
    Where(Function(c)
              Return ((
          c.R * 0.299F +
          c.G * 0.587F +
          c.B * 0.114F) / 256.0F) <= 0.5F
          End Function).ToArray()
    bmp = My.Resources.money
  End Sub

  Private Sub CreateDataTable()
    If m_DataTable IsNot Nothing Then Return
    m_DataTable = New DataTable
    ' 定义数据表列,补充缺失的SIZEPRODUCT和CATEGORY列
    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))")
    m_DataTable.Columns.Add("INFO", GetType(String))
    m_DataTable.Columns.Add("QTY", GetType(Integer))
    m_DataTable.Columns.Add("SIZEPRODUCT", GetType(String))
    m_DataTable.Columns.Add("CATEGORY", GetType(String))
  End Sub

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

  Private Sub UpdateColorIndices()
    DataGridView1.EndEdit()
    Dim groups = DataGridView1.Rows.Cast(Of DataGridViewRow).
    Where(Function(r) r.Index <> DataGridView1.NewRowIndex).
    GroupBy(Function(r) New With {
    Key .Col0 = r.Cells("CODEPRODUCT").Value,
            Key .Col1 = r.Cells("COLORCODE").Value,
                            Key .Col2 = r.Cells("SIZEPRODUCT").Value
            })
    For i = 0 To groups.Count - 1
        Dim index = i Mod colors.Length
        For Each row In groups(i)
            row.Tag = index
        Next
    Next
    DataGridView1.Invalidate()
  End Sub

  Private Sub Grid_CellValueChanged(sender As Object,
            e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
    UpdateColorIndices()
  End Sub

  Private Sub DataGridView1_CellPainting(sender As Object,
       e As DataGridViewCellPaintingEventArgs) _
       Handles DataGridView1.CellPainting
    If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
    e.RowIndex <> DataGridView1.NewRowIndex Then

        Dim ellipseSize = DataGridView1.RowTemplate.Height - 32
        Dim ellipseRect = New Drawing.Rectangle(
            e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
            e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
            ellipseSize, ellipseSize)
        Dim imgSize = ellipseSize
        Dim imgRect = New Drawing.Rectangle(
        ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
        ellipseRect.Y + (ellipseRect.Height - imgSize) \ 2,
        imgSize, imgSize)
        Dim colorIndex = Convert.ToInt32(DataGridView1.Rows(e.RowIndex).Tag)
        Dim g = e.Graphics
        Dim gs = g.Save()

        e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
            DataGridViewPaintParts.Border Or
            DataGridViewPaintParts.SelectionBackground)

        Using ellipseBrush = New SolidBrush(colors(colorIndex))
            g.SmoothingMode = SmoothingMode.AntiAlias
            g.FillEllipse(ellipseBrush, ellipseRect)
            g.InterpolationMode = InterpolationMode.HighQualityBicubic
            g.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height,
                    GraphicsUnit.Pixel)
        End Using

        g.Restore(gs)
        e.Handled = True
    End If
  End Sub

  Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
    ' 修复冗余的索引判断
    If e.RowIndex < 0 OrElse e.ColumnIndex < 0 Then Return
    Dim dgv = DirectCast(sender, DataGridView)
    Dim row = dgv.Rows(e.RowIndex)
    DirectCast(row.DataBoundItem, DataRowView)?.EndEdit()

    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

    ' 临时移除事件处理,避免循环触发
    RemoveHandler dgv.CellValueChanged, AddressOf Grid_CellValueChanged
    RemoveHandler dgv.CellEndEdit, AddressOf DataGridView1_CellEndEdit

    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.DataPropertyName,
                q.Table,
                q.KeyCell.OwningColumn.DataPropertyName)
                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

    ' 重新绑定事件
    AddHandler dgv.CellValueChanged, AddressOf Grid_CellValueChanged
    AddHandler dgv.CellEndEdit, AddressOf DataGridView1_CellEndEdit

    ' 手动触发颜色索引更新,保证界面同步
    UpdateColorIndices()
  End Sub
End Class

错误截图

System.StackOverflowException错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 23:14:54