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