I ran into a very odd issue with the DataGridView on my dual-monitor system. The issue manifests its
Adding to previous posts, for Windows Forms applications this is what I use for DataGridView components to make them fast. The code for the class DrawingControl is below.
DrawingControl.SetDoubleBuffered(control)
DrawingControl.SuspendDrawing(control)
DrawingControl.ResumeDrawing(control)
Call DrawingControl.SetDoubleBuffered(control) after InitializeComponent() in the constructor.
Call DrawingControl.SuspendDrawing(control) before doing big data updates.
Call DrawingControl.ResumeDrawing(control) after doing big data updates.
These last 2 are best done with a try/finally block. (or even better rewrite the class as IDisposable
and call SuspendDrawing()
in the constructor and ResumeDrawing()
in Dispose()
.)
using System.Runtime.InteropServices;
public static class DrawingControl
{
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
private const int WM_SETREDRAW = 11;
/// <summary>
/// Some controls, such as the DataGridView, do not allow setting the DoubleBuffered property.
/// It is set as a protected property. This method is a work-around to allow setting it.
/// Call this in the constructor just after InitializeComponent().
/// </summary>
/// <param name="control">The Control on which to set DoubleBuffered to true.</param>
public static void SetDoubleBuffered(Control control)
{
// if not remote desktop session then enable double-buffering optimization
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
// set instance non-public property with name "DoubleBuffered" to true
typeof(Control).InvokeMember("DoubleBuffered",
System.Reflection.BindingFlags.SetProperty |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic,
null,
control,
new object[] { true });
}
}
/// <summary>
/// Suspend drawing updates for the specified control. After the control has been updated
/// call DrawingControl.ResumeDrawing(Control control).
/// </summary>
/// <param name="control">The control to suspend draw updates on.</param>
public static void SuspendDrawing(Control control)
{
SendMessage(control.Handle, WM_SETREDRAW, false, 0);
}
/// <summary>
/// Resume drawing updates for the specified control.
/// </summary>
/// <param name="control">The control to resume draw updates on.</param>
public static void ResumeDrawing(Control control)
{
SendMessage(control.Handle, WM_SETREDRAW, true, 0);
control.Refresh();
}
}
The answer to this worked for me too. I thought I would add a refinement that I think should be standard practise for anyone implementing the solution.
The solution works well except when the UI is being run as a client session under remote desktop, especially where the available network bandwidth is low. In such a case, performance can be made worse by the use of double-buffering. Therefore, I suggest the following as a more complete answer:
class CustomDataGridView: DataGridView
{
public CustomDataGridView()
{
// if not remote desktop session then enable double-buffering optimization
if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
DoubleBuffered = true;
}
}
For more details, refer to Detecting remote desktop connection
We've experienced a similar problem using .NET 3.0 and DataGridView on a dual monitor system.
Our application would display the grid with a gray background, indicating that the cells could not be changed. Upon selecting a "change settings" button, the program would change the background color of the cells white to indicate to the user that the cell text could be changed. A "cancel" button would change the background color of the aforementioned cells back to gray.
As the background color changed there would be a flicker, a brief impression of a default sized grid with the same number of rows and columns. This problem would only occur on the primary monitor (never the secondary) and it would not occur on a single monitor system.
Double buffering the control, using the above example, solved our problem. We greatly appreciated your help.
For people searching how to do it in VB.NET, here is the code:
DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True})
Here is some code that sets the property using reflection, without subclassing as Benoit suggests.
typeof(DataGridView).InvokeMember(
"DoubleBuffered",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
null,
myDataGridViewObject,
new object[] { true });
Best!:
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" _
(ByVal hWnd As Integer, ByVal wMsg As Integer, _
ByVal wParam As Integer, ByRef lParam As Object) _
As Integer
Const WM_SETREDRAW As Integer = &HB
Public Sub SuspendControl(this As Control)
SendMessage(this.Handle, WM_SETREDRAW, 0, 0)
End Sub
Public Sub ResumeControl(this As Control)
RedrawControl(this, True)
End Sub
Public Sub RedrawControl(this As Control, refresh As Boolean)
SendMessage(this.Handle, WM_SETREDRAW, 1, 0)
If refresh Then
this.Refresh()
End If
End Sub