WinForms DataGridView CellPainting事件触发无限重绘循环求助
问题解决:WinForms DataGridView 无限重绘/闪烁问题
问题重现
自定义WinForms DataGridView特定单元格时,遇到CellPainting事件持续触发、无限循环重绘的问题——即使不滚动行也会发生。启动应用时功能正常,但界面持续闪烁,完整绘制GUI需近20秒。添加双缓冲能消除闪烁,但只是掩盖问题,后台仍在不断重绘。仅设置DataGridViewAdvancedCellBorderStyle.Single时无此问题,但使用Inset/Outset边框样式或修改字体时,就会触发重绘/闪烁。
需求是每隔若干行模拟“表头”行(单列场景,让DataGridView看起来像无行间隔的ListView),每隔20-30行添加假表头,修改假表头的BackColor。
问题根源
你的代码在CellPainting事件中直接修改了DataGridViewRow.Height、DataGridViewCell.Style这些属性——这些属性的变更会直接触发DataGridView的重绘操作,而重绘又会再次触发CellPainting事件,形成无限循环,导致持续重绘和性能问题。
解决方案
- 提前初始化样式与行高:在数据绑定完成后,一次性遍历行设置表头行的高度和基础样式,不要在绘制事件中动态修改。
- 手动绘制单元格内容:在
CellPainting事件中手动绘制字体、背景色、边框,完成后设置e.Handled = true,阻止默认绘制逻辑,避免额外重绘触发。 - 复用字体对象:避免每次绘制都创建新的Font实例,防止内存泄漏和性能损耗。
修改后的代码
第一步:数据加载完成后初始化行样式与高度
// 假设在数据绑定完成后调用此方法,比如DataBindingComplete事件中 private void InitRowStyles() { // 复用字体,避免频繁创建 var headerFont = new Font(dataList.Font.FontFamily, dataList.Font.Size + 2.25f, FontStyle.Bold, GraphicsUnit.Point); foreach (DataGridViewRow row in dataList.Rows) { if (row.IsNewRow) continue; var cellValue = row.Cells[0].Value?.ToString(); if (!string.IsNullOrEmpty(cellValue) && cellValue[0] == '=') { row.Height = 26; // 设置基础样式,后续绘制时可直接使用 row.Cells[0].Style.Font = headerFont; row.Cells[0].Style.BackColor = Color.LightGray; // 设置假表头背景色 } else { row.Height = dataList.RowTemplate.Height; row.Cells[0].Style.Font = dataList.Font; row.Cells[0].Style.BackColor = dataList.DefaultCellStyle.BackColor; } } }
第二步:优化CellPainting事件处理
private void dataList_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { // 忽略列头和新行 if (e.RowIndex < 0 || e.ColumnIndex < 0 || dataList.Rows[e.RowIndex].IsNewRow) return; var cell = dataList.Rows[e.RowIndex].Cells[e.ColumnIndex]; var cellValue = cell.Value?.ToString(); // 判断是否是假表头行 bool isHeaderRow = !string.IsNullOrEmpty(cellValue) && cellValue[0] == '='; // 绘制单元格背景 using (var brush = new SolidBrush(cell.Style.BackColor)) { e.Graphics.FillRectangle(brush, e.CellBounds); } // 绘制单元格内容 if (cellValue != null) { // 去掉开头的'='符号(如果需要) var displayText = isHeaderRow ? cellValue.Substring(1) : cellValue; using (var brush = new SolidBrush(cell.Style.ForeColor)) { e.Graphics.DrawString(displayText, cell.Style.Font, brush, e.CellBounds.X + 5, e.CellBounds.Y + 3); } } // 绘制边框 if (isHeaderRow) { e.Graphics.DrawRectangle(Pens.Black, e.CellBounds); // 模拟Single边框 } else { // 无边框,或者只绘制底部边框模拟ListView效果 e.Graphics.DrawLine(Pens.LightGray, e.CellBounds.Left, e.CellBounds.Bottom - 1, e.CellBounds.Right, e.CellBounds.Bottom - 1); } // 标记事件已处理,阻止默认绘制 e.Handled = true; }
额外优化:启用双缓冲(可选,但配合上述方案可彻底解决闪烁)
// 在DataGridView的构造函数或者Form_Load中设置双缓冲 dataList.DoubleBuffered = true; // 也可以通过反射设置,避免WinForms默认不公开的属性 typeof(DataGridView).InvokeMember("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, dataList, new object[] { true });
内容的提问来源于stack exchange,提问作者Bighead
相关产品推荐
相关产品推荐

