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

DataGridView刷新闪烁及自定义列内容消失问题排查与解决

DataGridView刷新闪烁及自定义列内容异常问题排查与解决

问题描述

有一个接收外部数据的DataGridView,额外添加了3个自定义列,控件每3秒刷新一次。刷新时出现单元格先缩小再恢复的闪烁现象,且自定义列内容会消失后重现。移除以下代码后问题消失:

var id = row.Cells[0].Value.ToString();
var nickNameTask = Task.Run<string>(async() => await this.GetTraderName(id));
nickNameTask.Wait();
string nickName = nickNameTask.Result;
row.Cells[1].Value =nickName;

问题根源

  1. UI线程阻塞:这段代码用Task.Run启动异步任务后立刻调用Wait(),强制阻塞UI线程等待任务完成。DataGridView的刷新依赖UI线程,阻塞会导致控件渲染不连贯,出现闪烁、尺寸异常,同时自定义列内容因为线程卡顿无法及时更新,表现为消失后重现。
  2. 低效嵌套循环:外层遍历lstUID、内层遍历所有行,相当于对每一行重复处理多次(次数等于lstUID元素数量),进一步加剧UI线程负担,放大阻塞带来的渲染问题。
  3. 无缓存重复请求:每次刷新都对同一ID重复调用GetTraderName,既浪费资源,又增加UI线程等待时长。

解决方法

1. 改用异步非阻塞方式更新UI

将事件处理改为异步版本,避免阻塞UI线程:

private async void dtgvPositions_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
    List<string> lstUID = this.ReplaceDownTheLine(this.txtUIDs.Text.Trim());
    Dictionary<string, bool> lstUIDColor = new Dictionary<string, bool>();
    for (int index = 0; index < lstUID.Count; ++index)
        lstUIDColor.Add(lstUID[index], index % 2 == 0);

    // 提前缓存所有需要的昵称,避免重复请求
    Dictionary<string, string> nickNameCache = new Dictionary<string, string>();
    foreach (DataGridViewRow row in dtgvPositions.Rows)
    {
        if (row.Cells[0].Value == null) continue;
        string id = row.Cells[0].Value.ToString();
        if (!nickNameCache.ContainsKey(id))
        {
            string nickName = await this.GetTraderName(id);
            nickNameCache[id] = nickName;
        }
    }

    // 统一更新行样式和自定义列内容
    foreach (DataGridViewRow row in dtgvPositions.Rows)
    {
        if (row.Cells[0].Value == null) continue;
        string key = row.Cells[0].Value.ToString();
        Decimal num = 0M;
        Decimal num1 = 0M;
        Decimal num2 = 0M;
        Decimal num3 = 0M;
        try
        {
            Decimal.TryParse(row.Cells[9].Value?.ToString(), out num);
            Decimal.TryParse(row.Cells[5].Value?.ToString(), out num1);
            Decimal.TryParse(row.Cells[7].Value?.ToString(), out num2);
            Decimal.TryParse(row.Cells[3].Value?.ToString(), out num3);

            // 从缓存取昵称
            if (nickNameCache.TryGetValue(key, out string nickName))
            {
                row.Cells[1].Value = nickName;
            }

            bool flag = lstUIDColor.TryGetValue(key, out flag) ? flag : false;
            row.DefaultCellStyle.BackColor = Color.FromArgb(24, 26, 32);
            row.Cells[1].Selected = false;
            row.Cells[1].Style.ForeColor = flag ? Color.LightSkyBlue : Color.LightPink;
            row.Cells[2].Value = num1 >= 0M ? "Long" : "Short";
            row.Cells[2].Style.ForeColor = num1 >= 0M ? Color.FromArgb(22, 181, 93) : Color.FromArgb(194, 52, 54);
            row.Cells[6].Value = num3 != 0 ? (num1 * num2) / num3 : 0M;
            row.Cells[9].Style.ForeColor = num >= 0M ? Color.FromArgb(22, 181, 93) : Color.FromArgb(194, 52, 54);
            row.Cells[10].Style.ForeColor = num >= 0M ? Color.FromArgb(22, 181, 93) : Color.FromArgb(194, 52, 54);
        }
        catch
        {
            // 建议添加日志记录,便于排查异常
        }
    }
}

2. 优化循环逻辑

  • 移除外层foreach (string str in lstUID)的嵌套循环,直接遍历所有行,避免重复处理同一行。
  • 提前缓存昵称,减少GetTraderName的调用次数,提升运行效率。

3. 启用双缓冲减少闪烁

在窗体构造函数或控件初始化时开启双缓冲:

public YourForm()
{
    InitializeComponent();
    // 开启双缓冲
    dtgvPositions.DoubleBuffered = true;
    // 若DoubleBuffered属性不可见,可通过反射设置
    typeof(DataGridView).InvokeMember("DoubleBuffered", 
        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.SetProperty,
        null, dtgvPositions, new object[] { true });
}

4. 移除不必要的Invoke

DataBindingComplete事件本身在UI线程触发,无需额外调用Invoke,直接操作控件即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:22:21