DataGridView 在屏幕上刷新缓慢

DataGridView slow to refresh on screen

我有一个程序可以使用通过 TCP 接收的新数据定期更新一些数据网格视图。我遇到的问题是屏幕刷新速度很慢。 Bellow 是我的代码的精简版。本例每次迭代 StartButton_Click 中的循环需要 1.1s 来更新屏幕。如何在不减少显示的数据量的情况下加快速度?

我添加了一个秒表来尝试找出哪些代码行导致了最大的问题。从测试来看,主要问题似乎是用新数字更新 datagridview 单元格。

我不确定如何让它更快,因为我的程序依赖于定期更新的值。 datagridview 不是这个应用程序的正确对象吗?我应该使用其他东西吗?有没有办法让 datagridview 更新得更快?

Public Class Form1

Public DataTable1 As New DataTable

Private Sub Load_From(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

    DataGridView1.DataSource = DataTable1

    Me.Height = 700
    Me.Width = 1000
    DataGridView1.Location = New Point(10, 10)
    DataGridView1.Width = Me.Width - 10
    DataGridView1.Height = Me.Height - 10

    For c As Integer = 0 To 20
        DataTable1.Columns.Add("col" & c)
        If DataTable1.Rows.Count = 0 Then
            DataTable1.Rows.Add()
        End If
        DataGridView1.Columns(c).AutoSizeMode = DataGridViewAutoSizeColumnMode.None '0%

        DataTable1.Rows(0).Item(c) = "col" & c
        DataGridView1.Columns(c).Width = 40
        'Header
        DataGridView1.Rows(0).Cells(c).Style.Alignment = DataGridViewContentAlignment.MiddleCenter
        DataGridView1.Rows(0).Cells(c).Style.WrapMode = DataGridViewTriState.True
        DataGridView1.Rows(0).Cells(c).Style.Font = New Font("Verdana", 8, FontStyle.Bold)
        'Data
        DataGridView1.Columns(c).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        DataGridView1.Columns(c).DefaultCellStyle.WrapMode = DataGridViewTriState.False
        DataGridView1.Columns(c).DefaultCellStyle.Font = New Font("Verdana", 8, FontStyle.Regular)
    Next

    For r As Integer = 1 To 25
        DataTable1.Rows.Add()
    Next

End Sub

Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click

    Dim stpw As New Stopwatch
    stpw.Reset()
    stpw.Start()

    For i As Integer = 0 To 10
        Dim rand As New Random
        Dim randnumber As Double = rand.Next(5, 15) / 10

        UpdateDataTable(randnumber)
        DataGridView1.Update()
        Me.Text = i & "/100"
    Next

    stpw.Stop()
    MsgBox(stpw.Elapsed.TotalMilliseconds)

End Sub

Private Sub UpdateDataTable(ByVal offset As Double)

    For r As Integer = 1 To DataTable1.Rows.Count - 1 'loop through rows

        For c As Integer = 0 To DataTable1.Columns.Count - 1 '89%

            DataTable1.Rows(r).Item(c) = (r / c) * offset
        Next

    Next

End Sub

结束Class

编辑:

我不得不承认我完全搞砸了我原来的答案,错误地认为不需要调用 DataGridView.Update 来模拟 OP 条件。我留下了我的原文,因为它可能对其他情况下的人有用。

一个潜在的解决方案是使用 DoubleBuffered DataGridView。这可以通过创建一个继承自 DataGridView 并启用 DoubleBuffering 的 class 来实现。

Public Class BufferedDataGridView : Inherits DataGridView
    Public Sub New()
        MyBase.New()
        Me.DoubleBuffered = True
    End Sub
    Protected Overrides Sub OnPaint(e As PaintEventArgs)
        e.Graphics.Clear(Me.BackgroundColor)
        MyBase.OnPaint(e)
    End Sub
End Class

这样做会导致外观发生变化,因为客户区在绘制内容之前是黑色的。为了缓解这种情况,class 覆盖了 OnPaint 方法来绘制背景。

在我的测试中,这将基准时间从大约 2600 毫秒减少到大约 600 毫秒。

结束编辑


除了@Visual Vincent 在关于消除不必要的更新的评论中提出的非常中肯的建议外,我建议您使用 BindingSource 封装 DataTable 并将其用作 DataGridview.DataSource.

Private bs As New BindingSource

Private Sub Load_From(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    bs.DataSource = DataTable1
    DataGridView1.DataSource = bs

这将允许您暂时挂起通过 DataTable 引发的导致 DataGridView 重新绘制单元格的更改事件。

Private Sub UpdateDataTable(ByVal offset As Double)
    ' prevent each item change from raising an event that causes a redraw
    bs.RaiseListChangedEvents = False

    For r As Integer = 1 To DataTable1.Rows.Count - 1 'loop through rows
        For c As Integer = 0 To DataTable1.Columns.Count - 1 '89%
            DataTable1.Rows(r).Item(c) = (r / c) * offset
        Next
    Next

    bs.RaiseListChangedEvents = True ' re-enable change events
    bs.ResetBindings(False) ' Force bound controls to re-read list
End Sub

这样只会重新绘制一次以反映对底层 DataTable 的所有更改。