title: How can I make a .net DataGridView control in Virtual mode redraw quickly when Scrolling
body: <p>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. </p>
  
  <p>When either of the grids is scrolled, the other grids FirstDisplayedScrollingRowIndex property is set to keep them in alignment.</p>
  
  <p>DoubleBuffer has been set to true using:</p>
  
  <pre><code>dataGridViewLines.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, dgvStoreLines, New Object() {True})
  </code></pre>
  
  <p>The app was originally written in VS2005 and has been upgraded to vs2012 in VB but am happy to have answers in C#.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>If the code to make them stay in alignment is removed, individually each grid scrolls as expected.</p>
  
  <p>There are case statements in the CellValueNeeded procedures which have been broken down in to small select statements for performance reasons.</p>
  
  <p>I have tried code along the lines of:</p>
  
  <pre><code>dgv.SuspendLayout()
  dgv.Refresh() ' have also tried dgv.PerformLayout()
  dgv.ResumeLayout()
  </code></pre>
  
  <p>At the moment the following rough code seems to provide a better result than either Refresh or PerformLayout.</p>
  
  <pre><code>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
  </code></pre>
  
  <p>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.</p>
  
  <pre><code>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 &gt; 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 &lt; 2)
      If e.ColumnIndex &lt; 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
  </code></pre>