0

I have an application that has two DataGridView controls on a form. The grids are lined up next to each other, both are in Virtual Mode and the rows need to be kept in alignment. One of the grids is in a control, which exposes the FirstDisplayedScrollingRowIndex property and scroll event.

When either of the grids is scrolled, the other grids FirstDisplayedScrollingRowIndex property is set to keep them in alignment.

DoubleBuffer has been set to true using:

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

The app was originally written in VS2005 and has been upgraded to vs2012 in VB but am happy to have answers in C#.

When either control sets the other controls FirstDisplayedScrollingRowIndex property, a flag is set to ensure that the second controls scroll event does not get raised back to the container causing cyclic events.

The first grid has roughly 50 columns in, the second has 8-10. There are typically be around 250-350 rows of data in the grid.

The problem is when scrolling one of the grids using the mouse wheel you get a bit of lag before the data in the second grid moves in line with the first grid even though the scroll-bars stay perfectly in line. If you grab the scroll bar with the mouse to move it up or down, the scroller on the other grid moves perfectly but the grids data does not change until the scroller is released.

If the code to make them stay in alignment is removed, individually each grid scrolls as expected.

There are case statements in the CellValueNeeded procedures which have been broken down in to small select statements for performance reasons.

I have tried code along the lines of:

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

At the moment the following rough code seems to provide a better result than either Refresh or 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

The following code reproduces the error to a certain amount - this tries to show most of the properties that are being set - i'm not allowed to post any of the actual code. To create the app I have added to datagridview controls and anchored them to the forms edges. The second grid has had a single DataGridViewComboBoxColumn column added - no property changes, with six values added to the list 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
Matt
  • 23
  • 5
  • Did this work before the upgrade to VS 2012? – Ann L. Jul 10 '13 at 17:50
  • Also: here's someone with a similar problem: http://stackoverflow.com/questions/8260167/datagridview-virtual-mode-and-lags?rq=1 – Ann L. Jul 10 '13 at 17:52
  • It has only been raised by the user base in the last couple of weeks, so there will have been at least 5 releases built in 2012. We're trying to see if we can associate the error with a specific release. There have been a number of environmental changes at the same time including the users getting new PCs with larger monitors. It does seem to be more of an issue when there are a lot of rows displayed. – Matt Jul 11 '13 at 10:25
  • Are you autosizing the columns, by any chance? – Ann L. Jul 11 '13 at 14:24
  • Thanks for the Link Ann. I've tried making the Autosizing change, this has improved the situation on some machines but not on all. AutoSizeMode was set to NotSet, its now being set to None. – Matt Jul 11 '13 at 14:39

1 Answers1

0

A couple of ideas:

  1. It would be interesting to run a profiler on that part of the app. Do you have a version of VS that includes the profiler? It sounds as if a lot is happening with every scroll movement, but we don't know what - or, more precisely, we don't know everything.

  2. You mention some complex-sounding case statements in the CellValueNeeded procedure. Assuming "procedure" doesn't mean "stored procedure", is there any possibility of caching some of these values without rearchitecting the whole thing?

ETA: I don't know how much your example code represents the real code, but there's a teeny, tiny performance adjustment you could make: pre-determine the cell to change rather than doing lookups on the Rows and Columns collection every time:

DataGridViewCell cell = DataGridView2.Rows(e.RowIndex).Cells(e.ColumnIndex)
cell.ReadOnly = (e.ColumnIndex < 2)
If e.ColumnIndex < 2 Then cell.Style.BackColor = ReadOnlyColour
Ann L.
  • 13,760
  • 5
  • 35
  • 66
  • I have VS2012 Ultimate and will try and produce something from a profiler. I'm also trying to create a basic app to replicate the problem. The case statements are basically ensuring that e.Value gets set to an objects property in CellValueNeeded, but there are nearly 50 of them. The original developer broke this down into smaller statements as 1 large select case block performed badly. – Matt Jul 12 '13 at 09:57
  • I have tried to use the profiler but am not sure what I can upload – Matt Jul 12 '13 at 17:02
  • You don't need to upload anything; I just thought the profile info could give you insight on what was going on behind the scenes when the scrollbar gets moved. For example: is CellValueNeeded called a billion times? Is something else taking up some large percentage of the elapsed time? You might have to turn on the setting that displays stats for .NET framework classes as well as your own code. – Ann L. Jul 12 '13 at 17:50
  • Have added a small perf. improvement suggestion based on your code sample. – Ann L. Jul 12 '13 at 18:00
  • Thanks @Ann the example code is to give an idea of the properties that are being set during scrolling. In the actual app the cell properties are being set based the results of business rules. I'm trying to refactor some of these so the logic occurs during data editing rather than during reading. I'm also reviewing the code based on your slight perf suggestion. I've checked the profiler results and it seems to be calling each function once per scroll event. The issue only seems to be an issue when the form/grids are maximised on fairly high res Monitors - 1920x1080 Regards, Matt – Matt Jul 16 '13 at 12:17