VB.NET中DataGridView CellEndEdit触发StackOverflowException错误的解决
解决VB.NET中DataGridView的CellEndEdit触发StackOverflowException问题
问题描述
在VB.NET的DataGridView中,当回到编辑操作前的单元格时,CellEndEdit事件会触发StackOverflowException错误,需排查并解决此问题。
错误原因
出现栈溢出的核心原因是事件循环调用链:
- 在
CellEndEdit事件中直接修改DataGridView单元格值(q.ValueCell.Value = cmd.ExecuteScalar()) - 单元格值变更触发
CellValueChanged事件 CellValueChanged事件调用UpdateColorIndices方法,该方法内执行DataGridView1.EndEdit()再次触发CellEndEdit事件- 循环往复导致栈内存耗尽,触发溢出错误
此外还有两处次要问题:
CellEndEdit开头的行索引判断重复冗余:If e.RowIndex < 0 OrElse e.RowIndex < 0 Then Return- 代码中引用的
SIZEPRODUCT和CATEGORY列未在CreateDataTable方法中定义,会引发运行时错误
解决方案
要打破循环调用链,需在修改单元格值时暂时禁用相关事件绑定,操作完成后再恢复:
- 在数据更新前,移除
CellValueChanged和CellEndEdit的事件处理 - 完成数据库查询和单元格赋值后,重新绑定事件
- 手动触发一次颜色索引更新,保证界面同步
- 修复冗余判断和未定义列的问题
修改后的完整代码
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
错误截图

内容的提问来源于stack exchange,提问作者user22579796
相关产品推荐
相关产品推荐

