Question

J'ai effectivement résolu ce problème, mais je le publie pour la postérité.

J'ai rencontré un problème très étrange avec DataGridView sur mon système à double moniteur. Le problème se manifeste par un repeint extrêmement lent du contrôle ( comme 30 secondes pour un repeint complet ), mais uniquement quand il se trouve sur l'un de mes écrans. Dans le cas contraire, la vitesse de repeinte est correcte.

J'ai un Nvidia 8800 GT avec les derniers pilotes non-bêta (175. quelque chose). Est-ce un bug de pilote? Je vais laisser ça dans les airs, car je dois vivre avec cette configuration particulière. (Cela ne se produit pas sur les cartes ATI, cependant ...)

La vitesse de peinture n'a rien à voir avec le contenu de la cellule, et le dessin personnalisé n'améliore en rien les performances, même lorsque vous dessinez un rectangle plein.

Je découvre plus tard que le fait de placer un ElementHost (à partir de l'espace de noms System.Windows.Forms.Integration) sur le formulaire corrige le problème. Il ne doit pas être dérangé; il doit simplement être un enfant du formulaire sur lequel DataGridView est également activé. Il peut être redimensionné à (0, 0) tant que la propriété Visible est vraie.

Je ne souhaite pas ajouter explicitement la dépendance .NET 3 / 3.5 à mon application. Je crée une méthode pour créer ce contrôle au moment de l'exécution (si possible) en utilisant la réflexion. Cela fonctionne, et au moins cela échoue gracieusement sur les machines ne disposant pas de la bibliothèque requise - cela redevient tout simplement lent.

Cette méthode me permet également d'appliquer une correction pendant l'exécution de l'application, ce qui permet de voir plus facilement ce que les bibliothèques WPF changent sur mon formulaire (à l'aide de Spy ++).

Après de nombreux essais et erreurs, je remarque que l'activation de la double mise en mémoire tampon sur le contrôle lui-même (par opposition à la forme uniquement) corrige le problème!

Il vous suffit donc de créer une classe personnalisée basée sur DataGridView pour pouvoir activer sa fonction DoubleBuffering. C'est ça!

class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    }
}

Tant que toutes mes instances de la grille utilisent cette version personnalisée, tout va bien. Si jamais je tombe dans une situation provoquée par ceci où je ne peux pas utiliser la solution de sous-classe (si je n'ai pas le code), je suppose que je pourrais essayer d'injecter ce contrôle sur le formulaire :) ( Bien que je sois plus susceptible d’utiliser la réflexion pour forcer la propriété DoubleBuffered de l’extérieur à éviter de nouveau la dépendance ).

C’est triste qu’une chose aussi simple et triviale me prenne tant de temps ...

Était-ce utile?

La solution

Il vous suffit de créer une classe personnalisée basée sur DataGridView pour pouvoir activer sa fonction DoubleBuffering. C'est ça!


class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    } 
}

Tant que toutes mes instances de la grille utilisent cette version personnalisée, tout va bien. Si jamais je me trouve dans une situation causée par ceci où je ne peux pas utiliser la solution de sous-classe (si je n'ai pas le code), je suppose que je pourrais essayer d'injecter ce contrôle sur le formulaire :) (bien que je ' Vous aurez plus de chances d’utiliser la réflexion pour forcer la propriété DoubleBuffered de l’extérieur à éviter de nouveau la dépendance).

C’est triste qu’une chose aussi simple et triviale me prenne tant de temps ...

Remarque: Faire de la réponse une réponse afin que la question puisse être marquée comme ayant obtenu une réponse

Autres conseils

Voici un code qui définit la propriété en utilisant la réflexion, sans sous-classe comme le suggère Benoit.

typeof(DataGridView).InvokeMember(
   "DoubleBuffered", 
   BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
   null, 
   myDataGridViewObject, 
   new object[] { true });

Pour les personnes cherchant comment procéder dans VB.NET, voici le code:

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

En ajoutant aux publications précédentes, pour les applications Windows Forms, c’est ce que j’utilise pour les composants DataGridView afin de les rendre rapides. Le code de la classe DrawingControl est ci-dessous.

DrawingControl.SetDoubleBuffered(control)
DrawingControl.SuspendDrawing(control)
DrawingControl.ResumeDrawing(control)

Appelez DrawingControl.SetDoubleBuffered (contrôle) après InitializeComponent () dans le constructeur.

Appelez DrawingControl.SuspendDrawing (contrôle) avant d'effectuer des mises à jour Big Data.

Appelez DrawingControl.ResumeDrawing (control) après avoir effectué les mises à jour de Big Data.

Ces deux derniers sont mieux réalisés avec un bloc try / finally. (ou mieux encore, réécrivez la classe sous la forme IDisposable et appelez SuspendDrawing () dans le constructeur et ResumeDrawing () dans 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();
    }
}

La réponse à cette question a fonctionné pour moi aussi. J'ai pensé ajouter un raffinement qui, à mon avis, devrait être une pratique courante pour tous ceux qui mettent en œuvre la solution.

La solution fonctionne bien, sauf lorsque l'interface utilisateur est exécutée en tant que session client sous un poste de travail distant, notamment lorsque la bande passante réseau disponible est faible. Dans un tel cas, les performances peuvent être dégradées par l’utilisation de la double mise en mémoire tampon. Par conséquent, je suggère ce qui suit comme réponse plus complète:

class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        // if not remote desktop session then enable double-buffering optimization
        if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
            DoubleBuffered = true;
    } 
}

Pour plus d'informations, reportez-vous à la Détection de la connexion au bureau à distance

.

J'ai trouvé une solution au problème. Accédez à l'onglet Résolution des problèmes dans les propriétés d'affichage avancées et vérifiez le curseur de l'accélération matérielle. Lorsque j'ai obtenu mon nouveau PC d'entreprise auprès du service informatique, il était réglé sur une coche de plein et je n'avais aucun problème avec les datagrids. Une fois que j'ai mis à jour le pilote de la carte vidéo et que je l'ai défini sur complet, la peinture des contrôles de la grille de données est devenue très lente. Je l'ai donc remis à l'état initial et le problème a disparu.

J'espère que cette astuce fonctionne aussi pour vous.

Juste pour ajouter ce que nous avons fait pour résoudre ce problème: Nous avons mis à niveau les derniers pilotes Nvidia pour résoudre le problème. Aucun code n'a dû être réécrit.

Pour être complet, la carte était un NVIDIA Quadro NVS 290 avec des pilotes datés de mars 2008 (v. 169). La mise à niveau vers la dernière version (version 182 datée de février 2009) a considérablement amélioré les événements de peinture de tous mes contrôles, en particulier de DataGridView.

Ce problème n'a été observé sur aucune carte ATI (où le développement a lieu).

Meilleur!:

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

Nous avons rencontré un problème similaire lors de l'utilisation de .NET 3.0 et de DataGridView sur un système à double moniteur.

Notre application affiche la grille avec un fond gris, indiquant que les cellules ne peuvent pas être modifiées. Lorsque vous sélectionnez un " modifier les paramètres " bouton, le programme changerait la couleur de fond des cellules blanches pour indiquer à l'utilisateur que le texte de la cellule pouvait être modifié. A " annuler " bouton changerait la couleur de fond des cellules susmentionnées en gris.

Lorsque la couleur d'arrière-plan change, il se produit un scintillement, une brève impression d'une grille de taille par défaut avec le même nombre de lignes et de colonnes. Ce problème ne se produirait que sur le moniteur principal (jamais le secondaire) et ne se produirait pas sur un seul système de moniteur.

La mise en mémoire tampon du contrôle, en utilisant l'exemple ci-dessus, a résolu notre problème. Nous avons grandement apprécié votre aide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top