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

.NET 7下如何异步填充并格式化DataGridView避免UI卡顿?

解决WinForm DataGridView异步填充和格式化时的UI卡顿问题

你的现有方案无效,核心原因是Task.Run内部直接调用Me.Invoke,所有DataGridView(以下简称DGV)操作被强制转回UI线程执行,和原同步代码没有本质区别,因此UI仍然卡顿。WinForm控件仅能在UI线程操作,优化的关键是减少UI线程上的重绘次数,而非将操作移至后台线程。

方案1:用BeginUpdate/EndUpdate包裹批量操作

DGV每次修改列属性都会触发重绘,批量操作时用BeginUpdate()阻止中间重绘,仅在最后统一刷新,能大幅降低卡顿:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' 1. 异步获取MySQL数据(保留你的原有异步查询逻辑)
    Dim TabStats As New DataTable
    Await FetchDataFromMySQLAsync(TabStats)

    ' 2. 在UI线程执行DGV更新,用BeginUpdate阻止中间重绘
    DataGridView1.BeginUpdate()
    Try
        DataGridView1.DataSource = TabStats

        ' 批量设置列宽与样式
        DataGridView1.Columns(0).Width = 80
        DataGridView1.Columns(1).Width = 80
        DataGridView1.Columns(2).Width = 80

        DataGridView1.Columns(0).DefaultCellStyle.BackColor = Color.Blue
        DataGridView1.Columns(1).DefaultCellStyle.BackColor = Color.Blue
        DataGridView1.Columns(2).DefaultCellStyle.BackColor = Color.Blue

        ' 若需处理150列,可通过循环批量设置
        ' For Each col As DataGridViewColumn In DataGridView1.Columns
        '     col.Width = GetCustomColumnWidth(col.Name) ' 自定义宽度逻辑
        ' Next
    Finally
        DataGridView1.EndUpdate() ' 结束更新,统一触发重绘
    End Try
End Sub

' 示例异步数据获取方法(替换为你的MySQL查询代码)
Private Async Function FetchDataFromMySQLAsync(table As DataTable) As Task
    Using conn As New MySqlConnection("你的数据库连接字符串")
        Await conn.OpenAsync()
        Using cmd As New MySqlCommand("SELECT * FROM 目标表", conn)
            Using reader As MySqlDataReader = Await cmd.ExecuteReaderAsync()
                table.Load(reader)
            End Using
        End Using
    End Using
End Function

方案2:启用双缓冲优化重绘性能

通过反射为DGV开启双缓冲,减少重绘时的闪烁与卡顿:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' 反射设置DGV的DoubleBuffered属性为True
    Dim dgvType As Type = DataGridView1.GetType()
    Dim propInfo As Reflection.PropertyInfo = dgvType.GetProperty("DoubleBuffered", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
    propInfo.SetValue(DataGridView1, True, Nothing)
End Sub

方案3:分批异步更新(针对超大量列场景)

若需处理150列这类超大量列,即使使用BeginUpdate仍可能导致UI短暂阻塞,可分批处理并让出UI线程:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim TabStats As New DataTable
    Await FetchDataFromMySQLAsync(TabStats)

    DataGridView1.DataSource = TabStats
    DataGridView1.BeginUpdate()
    Try
        ' 先处理前3列
        UpdateColumnRange(0, 2)
        Await Task.Delay(0) ' 让出UI线程,处理其他系统消息

        ' 分批处理剩余列(每次处理10列)
        For i = 3 To DataGridView1.Columns.Count - 1 Step 10
            Dim endIdx As Integer = Math.Min(i + 9, DataGridView1.Columns.Count - 1)
            UpdateColumnRange(i, endIdx)
            Await Task.Delay(0)
        Next
    Finally
        DataGridView1.EndUpdate()
    End Try
End Sub

Private Sub UpdateColumnRange(startIdx As Integer, endIdx As Integer)
    For i = startIdx To endIdx
        If i <= 2 Then
            DataGridView1.Columns(i).Width = 80
            DataGridView1.Columns(i).DefaultCellStyle.BackColor = Color.Blue
        Else
            ' 其他列的宽度设置逻辑
            DataGridView1.Columns(i).Width = 120 ' 示例宽度
        End If
    Next
End Sub

关键注意事项

  • WinForm控件必须在UI线程操作,禁止在后台线程直接修改DGV属性
  • BeginUpdate/EndUpdate是优化DGV卡顿的核心,避免多次重绘带来的性能损耗
  • 异步仅用于数据获取阶段,格式化操作需在UI线程执行,可通过Await Task.Delay(0)分批让出线程保持UI响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:55:56