Frage Schreckliche Neuzeichnen der DataGridView auf einem meiner beiden Bildschirme


Ich habe das tatsächlich gelöst, aber ich poste es für die Nachwelt.

Ich habe ein sehr seltsames Problem mit dem DataGridView auf meinem Dual-Monitor-System. Das Problem manifestiert sich als EXTREM langsames Repaint der Kontrolle (wie 30 Sekunden für ein vollständiges Repaint), aber nur wenn es auf einem meiner Bildschirme ist. Auf der anderen Seite ist die Repaint-Geschwindigkeit in Ordnung.

Ich habe eine Nvidia 8800 GT mit den neuesten Nicht-Beta-Treibern (175. etwas). Ist es ein Treiberfehler? Das lasse ich in der Luft liegen, da ich mit dieser besonderen Konfiguration leben muss. (Es passiert jedoch nicht auf ATI-Karten ...)

Die Farbgeschwindigkeit hat nichts mit dem Zelleninhalt zu tun, und die benutzerdefinierte Zeichnung verbessert die Leistung überhaupt nicht - selbst wenn nur ein durchgehendes Rechteck gezeichnet wird.

Ich finde später heraus, dass das Platzieren eines ElementHost (aus dem Namespace System.Windows.Forms.Integration) auf dem Formular das Problem behebt. Es muss nicht durcheinander gebracht werden; Es muss nur ein Kind der Form sein, in der DataGridView ebenfalls aktiv ist. Es kann auf (0, 0) skaliert werden, solange der Sichtbar Eigenschaft ist wahr.

Ich möchte die .NET 3 / 3.5-Abhängigkeit nicht explizit zu meiner Anwendung hinzufügen. Ich mache eine Methode, um dieses Steuerelement zur Laufzeit (wenn es möglich ist) mithilfe von Reflektion zu erstellen. Es funktioniert, und es scheitert zumindest auf Rechnern, die nicht über die erforderliche Bibliothek verfügen - es geht nur langsam zurück.

Mit dieser Methode kann ich auch eine Korrektur anwenden, während die App ausgeführt wird. Dadurch wird es einfacher zu sehen, was die WPF-Bibliotheken in meinem Formular ändern (mit Spy ++).

Nach viel Versuch und Irrtum merke ich, dass das Aktivieren der doppelten Pufferung auf dem Steuerelement selbst (im Gegensatz zu nur dem Formular) das Problem behebt!


Sie müssen also nur eine benutzerdefinierte Klasse basierend auf DataGridView erstellen, damit Sie DoubleBuffering aktivieren können. Das ist es!

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

Solange alle meine Instanzen des Rasters diese benutzerdefinierte Version verwenden, ist alles in Ordnung. Wenn ich jemals in eine Situation komme, die dadurch verursacht wird, dass ich nicht in der Lage bin, die Unterklassen-Lösung zu verwenden (wenn ich den Code nicht habe), könnte ich versuchen, dieses Steuerelement in das Formular zu injizieren :) (obwohl ich eher versuchen werde, mit Reflection die DoubleBuffered-Eigenschaft von außen zu erzwingen, um die Abhängigkeit noch einmal zu vermeiden).

Es ist traurig, dass so eine trivial einfache Sache so viel Zeit in Anspruch nimmt ...


76
2017-09-23 01:01


Ursprung


Antworten:


Sie müssen nur eine benutzerdefinierte Klasse basierend auf DataGridView erstellen, damit Sie DoubleBuffering aktivieren können. Das ist es!


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

Solange alle meine Instanzen des Rasters diese benutzerdefinierte Version verwenden, ist alles in Ordnung. Wenn ich jemals auf eine Situation stoße, die dadurch verursacht wird, dass ich die Unterklassen-Lösung nicht verwenden kann (wenn ich den Code nicht habe), könnte ich versuchen, dieses Steuerelement in das Formular zu injizieren :) (obwohl ich Es ist wahrscheinlicher, dass Sie mit Reflection versuchen, die DoubleBuffered-Eigenschaft von außen zu aktivieren, um die Abhängigkeit erneut zu vermeiden.

Es ist traurig, dass so eine trivial einfache Sache so viel Zeit in Anspruch nimmt ...

Hinweis: Die Antwort wird so beantwortet, dass die Frage als beantwortet markiert werden kann


60
2017-10-01 19:49



Hier ist ein Code, der die Eigenschaft mithilfe von Reflection setzt, ohne Subclassing, wie es Benoit vorschlägt.

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

57
2018-04-24 18:52



Für Leute, die in VB.NET suchen, ist hier der Code:

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

13
2018-04-13 16:45



Die Antwort darauf hat auch für mich funktioniert. Ich dachte, ich würde eine Verfeinerung hinzufügen, die meiner Meinung nach Standard für jeden sein sollte, der die Lösung implementiert.

Die Lösung funktioniert gut, außer wenn die Benutzeroberfläche als Clientsitzung unter dem Remotedesktop ausgeführt wird, insbesondere wenn die verfügbare Netzwerkbandbreite niedrig ist. In einem solchen Fall kann die Leistung durch die Verwendung von Doppelpufferung verschlechtert werden. Daher schlage ich Folgendes als eine vollständigere Antwort vor:

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

Weitere Informationen finden Sie unter Remote-Desktop-Verbindung erkennen


6
2018-05-18 15:15



Hinzufügen zu früheren Posts für Windows Forms-Anwendungen ist das, was ich für DataGridView-Komponenten verwende, um sie schneller zu machen. Der Code für die Klasse DrawingControl ist unten.

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

Rufen Sie DrawingControl.SetDoubleBuffered (control) nach InitializeComponent () im Konstruktor auf.

Rufen Sie DrawingControl.SuspendDrawing (control) auf, bevor Sie große Daten aktualisieren.

Rufen Sie DrawingControl.ResumeDrawing (control) auf, nachdem Sie große Daten aktualisiert haben.

Diese letzten 2 werden am besten mit einem try / finally Block gemacht. (oder besser noch die Klasse umschreiben als IDisposable und Ruf an SuspendDrawing() im Konstruktor und ResumeDrawing() im 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();
    }
}

6
2017-10-07 20:28



Ich habe eine Lösung für das Problem gefunden. Navigieren Sie in den erweiterten Anzeigeeigenschaften zur Registerkarte Fehlerbehebung und überprüfen Sie den Schieberegler für die Hardwarebeschleunigung. Als ich meinen neuen Firmen-PC von der IT bekam, war es auf einen Tick von voll eingestellt und ich hatte keine Probleme mit Datagrids. Nachdem ich den Grafikkartentreiber aktualisiert und auf voll gesetzt hatte, wurde das Zeichnen von DataGrid-Steuerelementen sehr langsam. Also setze ich es zurück, wo es war und das Problem ging weg.

Hoffe, dieser Trick funktioniert auch für dich.


1
2018-02-26 22:35



Nur um hinzuzufügen, was wir getan haben, um dieses Problem zu beheben: Wir haben ein Upgrade auf die neuesten Nvidia-Treiber durchgeführt, um das Problem zu lösen. Kein Code musste neu geschrieben werden.

Der Vollständigkeit halber war die Karte eine Nvidia Quadro NVS 290 mit Treibern vom März 2008 (v. 169). Die Aktualisierung auf den neuesten Stand (v. 182 vom Feb. 2009) hat die Paint-Ereignisse für alle meine Steuerelemente, insbesondere für das DataGridView, erheblich verbessert.

Dieses Problem wurde auf keiner ATI-Karte (bei der Entwicklung) festgestellt.


1
2018-06-10 16:54



Beste!:

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

1
2017-10-07 19:45