De hecho, resolví esto, pero lo voy a publicar para la posteridad.
Me encontré con un problema muy extraño con DataGridView en mi sistema de doble monitor. El problema se manifiesta como un repintado EXTREMADAMENTE lento del control ( como 30 segundos para un repintado completo ), pero solo cuando está en una de mis pantallas. Cuando por el otro lado, la velocidad de repintado es buena.
Tengo una Nvidia 8800 GT con los últimos controladores no beta (175. algo). ¿Es un error de controlador? Lo dejaré en el aire, ya que tengo que vivir con esta configuración en particular. (Sin embargo, no sucede con las tarjetas ATI ...)
La velocidad de pintura no tiene nada que ver con el contenido de la celda, y el dibujo personalizado no mejora el rendimiento en absoluto, incluso cuando solo se pinta un rectángulo sólido.
Más tarde descubrí que colocar un ElementHost (del espacio de nombres System.Windows.Forms.Integration) en el formulario corrige el problema. No tiene por qué meterse con él; solo necesita ser un elemento secundario del formulario en el que también se encuentra DataGridView. Se puede cambiar el tamaño a (0, 0) siempre que la propiedad Visible sea verdadera.
No quiero agregar explícitamente la dependencia .NET 3 / 3.5 a mi aplicación; Hago un método para crear este control en tiempo de ejecución (si puede) usando la reflexión. Funciona, y al menos falla correctamente en máquinas que no tienen la biblioteca requerida, simplemente vuelve a ser lento.
Este método también me permite aplicar para corregir mientras la aplicación se está ejecutando, lo que facilita ver qué están cambiando las bibliotecas de WPF en mi formulario (usando Spy ++).
Después de muchas pruebas y errores, noté que habilitar el almacenamiento en búfer doble en el control en sí (en lugar de solo en el formulario) corrige el problema.
Por lo tanto, solo necesita crear una clase personalizada basada en DataGridView para poder habilitar su DoubleBuffering. ¡Eso es!
class CustomDataGridView: DataGridView
{
public CustomDataGridView()
{
DoubleBuffered = true;
}
}
Siempre que todas mis instancias de la cuadrícula estén usando esta versión personalizada, todo está bien. Si alguna vez me encuentro con una situación causada por esto en la que no puedo usar la solución de subclase (si no tengo el código), supongo que podría intentar inyectar ese control en el formulario :) ( aunque yo ' Será más probable que intente usar la reflexión para forzar la propiedad DoubleBuffered desde el exterior para evitar una vez más la dependencia ).
Es triste que algo tan trivialmente simple haya consumido tanto de mi tiempo ...
fuente
Respuestas:
Solo necesita crear una clase personalizada basada en DataGridView para poder habilitar su DoubleBuffering. ¡Eso es!
class CustomDataGridView: DataGridView { public CustomDataGridView() { DoubleBuffered = true; } }
Siempre que todas mis instancias de la cuadrícula estén usando esta versión personalizada, todo está bien. Si alguna vez me encuentro con una situación causada por esto en la que no puedo usar la solución de subclase (si no tengo el código), supongo que podría intentar inyectar ese control en el formulario :) (aunque yo ' Será más probable que intente usar la reflexión para forzar la propiedad DoubleBuffered desde el exterior para evitar una vez más la dependencia).
Es triste que algo tan trivialmente simple haya consumido tanto de mi tiempo ...
Nota: Hacer que la respuesta sea una respuesta para que la pregunta se pueda marcar como respondida
fuente
Aquí hay un código que establece la propiedad mediante la reflexión, sin subclases como sugiere Benoit.
typeof(DataGridView).InvokeMember( "DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, myDataGridViewObject, new object[] { true });
fuente
Control
clase.public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered)
.Para las personas que buscan cómo hacerlo en VB.NET, aquí está el código:
DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True})
fuente
Agregando a publicaciones anteriores, para las aplicaciones de Windows Forms, esto es lo que uso para que los componentes DataGridView los hagan más rápidos. El código para la clase DrawingControl se encuentra a continuación.
DrawingControl.SetDoubleBuffered(control) DrawingControl.SuspendDrawing(control) DrawingControl.ResumeDrawing(control)
Llame a DrawingControl.SetDoubleBuffered (control) después de InitializeComponent () en el constructor.
Llame a DrawingControl.SuspendDrawing (control) antes de realizar actualizaciones de big data.
Llame a DrawingControl.ResumeDrawing (control) después de realizar actualizaciones de big data.
Estos 2 últimos se hacen mejor con un bloqueo try / finalmente. (o incluso mejor reescriba la clase como
IDisposable
y llameSuspendDrawing()
al constructor yResumeDrawing()
enDispose()
.)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(); } }
fuente
La respuesta a esto también funcionó para mí. Pensé en agregar un refinamiento que creo que debería ser una práctica estándar para cualquiera que implemente la solución.
La solución funciona bien, excepto cuando la interfaz de usuario se ejecuta como una sesión de cliente en un escritorio remoto, especialmente cuando el ancho de banda de red disponible es bajo. En tal caso, el rendimiento puede empeorar mediante el uso de doble búfer. Por lo tanto, sugiero lo siguiente como una respuesta más completa:
class CustomDataGridView: DataGridView { public CustomDataGridView() { // if not remote desktop session then enable double-buffering optimization if (!System.Windows.Forms.SystemInformation.TerminalServerSession) DoubleBuffered = true; } }
Para obtener más detalles, consulte Detectar la conexión de escritorio remoto
fuente
Encontré una solución al problema. Vaya a la pestaña de resolución de problemas en las propiedades de pantalla avanzadas y verifique el control deslizante de aceleración de hardware. Cuando recibí mi nueva PC de la empresa de TI, estaba configurada a un tick de completa y no tuve ningún problema con las redes de datos. Una vez que actualicé el controlador de la tarjeta de video y lo configuré al máximo, la pintura de los controles de la cuadrícula de datos se volvió muy lenta. Así que lo restablecí a donde estaba y el problema desapareció.
Espero que este truco funcione para ti también.
fuente
Solo para agregar lo que hicimos para solucionar este problema: Actualizamos a los últimos controladores de Nvidia que resolvieron el problema. No fue necesario reescribir ningún código.
Para completar, la tarjeta era una Nvidia Quadro NVS 290 con controladores con fecha de marzo de 2008 (v. 169). La actualización a la última (v. 182 de febrero de 2009) mejoró significativamente los eventos de pintura para todos mis controles, especialmente para DataGridView.
Este problema no se vio en ninguna tarjeta ATI (donde ocurre el desarrollo).
fuente
¡Mejor!:
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
fuente
Hemos experimentado un problema similar al usar .NET 3.0 y DataGridView en un sistema de monitor dual.
Nuestra aplicación mostraría la cuadrícula con un fondo gris, lo que indica que las celdas no se pueden cambiar. Al seleccionar un botón "cambiar configuración", el programa cambiaría el color de fondo de las celdas a blanco para indicarle al usuario que el texto de la celda podría cambiarse. Un botón "cancelar" cambiaría el color de fondo de las celdas antes mencionadas a gris.
A medida que cambiaba el color de fondo, se producía un parpadeo, una breve impresión de una cuadrícula de tamaño predeterminado con el mismo número de filas y columnas. Este problema solo ocurriría en el monitor principal (nunca en el secundario) y no ocurriría en un sistema de un solo monitor.
El doble almacenamiento en búfer del control, usando el ejemplo anterior, resolvió nuestro problema. Agradecemos mucho su ayuda.
fuente