【问题标题】:DataGridView slow to refresh on screenDataGridView 在屏幕上刷新缓慢
【发布时间】:2019-01-21 19:04:45
【问题描述】:

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

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

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

公开课表1

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

结束类

【问题讨论】:

  • 从循环中删除 DataGridView1.Update()Me.Text = i & "/100" 调用是一个好的开始。没有理由强制 DGV 手动重绘(更新)自身,尤其不是在每次迭代时。而设置表单的文本只需要更多不必要的更新。
  • my program relies on the values being updated regularly 你应该发布一些展示真实应用而不是人工测试的内容。
  • Thaks Visual Vincent 和 Plutonix 我试图通过仅显示导致问题的代码来简化它。完整的应用程序大约有 100,000 行代码。我用随机数生成器替换了值的生成方式。使用秒表,我发现更新数据网格中的值会花费时间。测定值小于1%

标签: vb.net performance datagridview datatable refresh


【解决方案1】:

编辑:

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

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

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

这样做会产生外观上的变化,因为客户区是黑色的,直到在上面绘制了一些东西。为了缓解这种情况,该类重写了 OnPaint 方法来绘制背景。

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

结束编辑


除了在 cmets 中 @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的所有更改。

【讨论】:

  • 感谢 TnTinMn 的建议,我刚刚对其进行了测试,不幸的是它并没有更快地更新(实际上慢了 5%)。数据通过 TCP 进入,然后被打乱、组织并显示在数据网格中。数据不断传输,我的应用程序要求它不断更新最新数据。所以我需要尽可能快地更新以避免任何滞后。我开始认为这是这种大小的数据网格视图的限制。也许我需要使用一组标签来代替。
  • @jasonwcgray,我把原来的答案搞砸了,并发布了一个可以帮助你解决问题的替代方案。
  • 嗨 TnTinMn 我终于有机会尝试您修改后的双缓冲建议。但是,我想我错过了一些东西。我需要向 Form1 类添加任何内容吗?我刚刚将您的代码复制到 Form1 类下面,但它仍然需要相同的时间,并且当我添加断点时似乎没有运行 BufferedDataGridView 类代码。
  • @jasonwcgray,只是将代码添加到您的项目中是没有用的。你需要使用它。构建项目后,您应该会在工具箱中看到一个新控件。它将列在工具箱顶部的 项目名称 组件下。将其用作标准 DataGridView 控件的替代品。或者,您可以编辑 Form.designer.vb 文件并将 DataGridView1 的类型定义从 System.Windows.Forms.DataGridView 更改为 BufferedDataGridView 。将有地点进行更改。第一个是实例化的地方,第二个是字段定义。
  • 我刚刚测试了一下。这很好用。感谢您的帮助 TnTinMn
猜你喜欢
  • 2015-12-04
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多