【问题标题】:How can I make a .net DataGridView control in Virtual mode redraw quickly when Scrolling滚动时如何在虚拟模式下快速重绘.net DataGridView控件
【发布时间】:2013-07-10 17:43:05
【问题描述】:

我的应用程序在一个窗体上有两个 DataGridView 控件。网格彼此相邻排列,两者都处于虚拟模式,并且行需要保持对齐。其中一个网格位于控件中,该控件公开 FirstDisplayedScrollingRowIndex 属性和滚动事件。

当任一网格滚动时,其他网格的 FirstDisplayedScrollingRowIndex 属性将设置为保持对齐。

DoubleBuffer 已通过以下方式设置为 true:

dataGridViewLines.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, dgvStoreLines, New Object() {True})

该应用程序最初是用 VS2005 编写的,并已在 VB 中升级到 vs2012,但很高兴在 C# 中得到答案。

当任一控件设置其他控件的 FirstDisplayedScrollingRowIndex 属性时,将设置一个标志以确保第二个控件的滚动事件不会引发回容器,从而导致循环事件。

第一个网格大约有 50 列,第二个网格有 8-10 列。网格中通常有大约 250-350 行数据。

问题是,当使用鼠标滚轮滚动其中一个网格时,即使滚动条完全对齐,在第二个网格中的数据与第一个网格对齐之前,您也会有一点延迟。如果你用鼠标抓住滚动条向上或向下移动,另一个网格上的滚动条会完美移动,但网格数据在滚动条释放之前不会改变。

如果使它们保持对齐的代码被删除,每个网格都会按预期单独滚动。

CellValueNeeded 过程中有一些 case 语句,出于性能原因,这些语句已分解为小的 select 语句。

我尝试过以下代码:

dgv.SuspendLayout()
dgv.Refresh() ' have also tried dgv.PerformLayout()
dgv.ResumeLayout()

目前,以下粗略代码似乎提供了比 Refresh 或 PerformLayout 更好的结果。

For i As Integer = 0 To dgv.ColumnCount - 1
    If dgv.Columns(i).Visible AndAlso dgv.Columns(i).Displayed Then
         dgv.InvalidateColumn(i)
    End If
Next

以下代码在一定程度上重现了错误 - 这试图显示正在设置的大部分属性 - 我不允许发布任何实际代码。为了创建应用程序,我已添加到 datagridview 控件并将它们锚定到表单边缘。第二个网格添加了一个 DataGridViewComboBoxColumn 列 - 没有属性更改,在列表 A-F 中添加了六个值。

Private IsSettingDataGridView1RowIndex As Boolean = False
Private IsSettingDataGridView2RowIndex As Boolean = False
Private ReadOnlyColour As System.Drawing.Color = Color.FromArgb(255, 255, 255, 192)


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    For grid1columns As Integer = 0 To 50
        DataGridView1.Columns.Add("Column " + grid1columns.ToString, "Column " + grid1columns.ToString)
        DataGridView1.Columns(grid1columns).AutoSizeMode = DataGridViewAutoSizeColumnMode.None
    Next
    For grid1columns As Integer = 0 To 10
        DataGridView2.Columns.Add("Column " + grid1columns.ToString, "Column " + grid1columns.ToString)
        DataGridView1.Columns(grid1columns).AutoSizeMode = DataGridViewAutoSizeColumnMode.None
    Next
    PrepGrid(DataGridView1)
    PrepGrid(DataGridView2)

End Sub

Private Sub PrepGrid(dgv As DataGridView)
    dgv.VirtualMode = True
    dgv.RowCount = 300
    dgv.AllowUserToAddRows = False
    dgv.AllowUserToResizeRows = False
    dgv.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.True
    dgv.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing
    dgv.DefaultCellStyle.WrapMode = DataGridViewTriState.False
    dgv.EditMode = DataGridViewEditMode.EditOnKeystroke
    dgv.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing
    dgv.ShowCellToolTips = False
    dgv.StandardTab = True
    dgv.RowHeadersVisible = False
    dgv.ScrollBars = ScrollBars.Vertical
End Sub

Private Sub DataGridView1_CellValueNeeded(sender As Object, e As DataGridViewCellValueEventArgs) Handles DataGridView1.CellValueNeeded
    e.Value = String.Format("val r{0} c{1}", e.RowIndex, e.ColumnIndex)
End Sub

Private Sub DataGridView2_CellValueNeeded(sender As Object, e As DataGridViewCellValueEventArgs) Handles DataGridView2.CellValueNeeded
    If e.ColumnIndex > 0 Then e.Value = String.Format("val r{0} c{1}", e.RowIndex, e.ColumnIndex)
    DataGridView2.Rows(e.RowIndex).Cells(e.ColumnIndex).ReadOnly = (e.ColumnIndex < 2)
    If e.ColumnIndex < 2 Then DataGridView2.Rows(e.RowIndex).Cells(e.ColumnIndex).Style.BackColor = ReadOnlyColour
End Sub

Private Sub DataGridView1_Scroll(sender As Object, e As ScrollEventArgs) Handles DataGridView1.Scroll
    If Not IsSettingDataGridView2RowIndex Then
        IsSettingDataGridView2RowIndex = True
        DataGridView2.FirstDisplayedScrollingRowIndex = DataGridView1.FirstDisplayedScrollingRowIndex
        IsSettingDataGridView2RowIndex = False
    End If
End Sub

Private Sub DataGridView2_Scroll(sender As Object, e As ScrollEventArgs) Handles DataGridView2.Scroll
    If Not IsSettingDataGridView1RowIndex Then
        IsSettingDataGridView1RowIndex = True
        DataGridView1.FirstDisplayedScrollingRowIndex = DataGridView2.FirstDisplayedScrollingRowIndex
        IsSettingDataGridView1RowIndex = False
    End If
End Sub

【问题讨论】:

  • 在升级到 VS 2012 之前这项工作有效吗?
  • 另外:这里有人有类似问题:stackoverflow.com/questions/8260167/…
  • 最近几周用户群才提出这个问题,因此 2012 年将至少发布 5 个版本。我们正在尝试查看是否可以将错误与一个特定的版本。同时发生了许多环境变化,包括用户购买了配备更大显示器的新 PC。当显示很多行时,这似乎是一个更大的问题。
  • 您是否正在自动调整列的大小?
  • 感谢安的链接。我已经尝试进行 Autosizing 更改,这改善了某些机器上的情况,但并非全部。 AutoSizeMode 设置为 NotSet,现在设置为 None。

标签: datagridview


【解决方案1】:

几个想法:

  1. 在应用程序的该部分运行分析器会很有趣。您是否有包含分析器的 VS 版本?听起来好像每次滚动运动都会发生很多事情,但我们不知道是什么 - 或者,更准确地说,我们不知道一切

  2. 您在CellValueNeeded 过程中提到了一些听起来很复杂的case 语句。假设“过程”并不意味着“存储过程”,是否有可能在不重新架构整个事物的情况下缓存其中一些值?

ETA:我不知道您的示例代码在多大程度上代表了真实代码,但您可以进行微小的性能调整:预先确定要更改的单元格,而不是在 Rows 和 @ 上进行查找987654324@每次收藏:

DataGridViewCell cell = DataGridView2.Rows(e.RowIndex).Cells(e.ColumnIndex)
cell.ReadOnly = (e.ColumnIndex < 2)
If e.ColumnIndex < 2 Then cell.Style.BackColor = ReadOnlyColour

【讨论】:

  • 我有 VS2012 Ultimate,将尝试从分析器中生成一些东西。我也在尝试创建一个基本的应用程序来复制问题。 case 语句基本上确保将 e.Value 设置为 CellValueNeeded 中的 objects 属性,但其中有近 50 个。最初的开发人员将其分解为较小的语句,因为 1 个大的选择案例块表现不佳。
  • 我尝试使用分析器,但不确定我可以上传什么
  • 你不需要上传任何东西;我只是认为配置文件信息可以让您了解滚动条移动时幕后发生的事情。例如:CellValueNeeded 是否被调用了十亿次?是否有其他东西占用了大部分经过的时间?您可能必须打开显示 .NET 框架类以及您自己的代码的统计信息的设置。
  • 添加了一个小的性能。基于您的代码示例的改进建议。
  • 感谢@Ann 示例代码是为了让您了解在滚动期间设置的属性。在实际应用程序中,单元格属性是根据业务规则的结果设置的。我正在尝试重构其中的一些,以便逻辑发生在数据编辑期间而不是读取期间。我还在根据您的轻微性能建议审查代码。我检查了探查器结果,似乎每个滚动事件都调用了每个函数。当表单/网格在相当高分辨率的显示器上最大化时,这个问题似乎才是一个问题 - 1920x1080 问候,Matt
猜你喜欢
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 2011-08-10
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多