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

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事件,形成无限循环,导致持续重绘和性能问题。

解决方案

  1. 提前初始化样式与行高:在数据绑定完成后,一次性遍历行设置表头行的高度和基础样式,不要在绘制事件中动态修改。
  2. 手动绘制单元格内容:在CellPainting事件中手动绘制字体、背景色、边框,完成后设置e.Handled = true,阻止默认绘制逻辑,避免额外重绘触发。
  3. 复用字体对象:避免每次绘制都创建新的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:43:26