it-swarm.com.de

Schreckliche Leistung beim Neuzeichnen des DataGridView auf einem meiner beiden Bildschirme

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

Ich bin auf meinem Dual-Monitor-System auf ein sehr seltsames Problem mit DataGridView gestoßen. Das Problem äußert sich in einem EXTREM langsamen Repaint des Steuerelements (wie 30 Sekunden für ein vollständiges Repaint), jedoch nur, wenn es sich auf einem meiner Bildschirme befindet. Auf der anderen Seite ist die Repaint-Geschwindigkeit in Ordnung.

Ich habe ein Nvidia 8800 GT mit den neuesten Nicht-Beta-Treibern (175. etwas). Ist es ein Treiberfehler? Ich lasse das in der Luft, da ich mit dieser speziellen Konfiguration leben muss. (Das passiert aber nicht bei ATI-Karten ...)

Die Malgeschwindigkeit hat nichts mit dem Zelleninhalt zu tun, und benutzerdefiniertes Zeichnen verbessert die Leistung überhaupt nicht - selbst wenn nur ein festes Rechteck gezeichnet wird.

Später erfahre ich, dass das Problem behoben wird, indem ein ElementHost (aus dem System.Windows.Forms.Integration-Namespace) in das Formular eingefügt wird. Man muss sich nicht damit anlegen; Es muss nur ein untergeordnetes Element des Formulars sein, in dem sich auch DataGridView befindet. Die Größe kann auf (0, 0) geändert werden, solange die Visible -Eigenschaft wahr ist.

Ich möchte meiner Anwendung die .NET 3/3.5-Abhängigkeit nicht explizit hinzufügen. Ich erstelle eine Methode, um dieses Steuerelement zur Laufzeit (wenn möglich) mithilfe von Reflektion zu erstellen. Es funktioniert, und auf Computern, die nicht über die erforderliche Bibliothek verfügen, schlägt es zumindest problemlos fehl - es wird nur langsam.

Mit dieser Methode kann ich das Problem auch beheben, während die App ausgeführt wird. Auf diese Weise kann ich leichter feststellen, welche Änderungen an den WPF-Bibliotheken in meinem Formular (mit Spy ++) vorgenommen wurden.

Nach vielem Ausprobieren stelle ich fest, dass das Problem durch Aktivieren der doppelten Pufferung auf dem Steuerelement selbst (im Gegensatz zu nur dem Formular) behoben wird!


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

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

Solange alle meine Instanzen des Grids diese benutzerdefinierte Version verwenden, ist alles in Ordnung. Wenn ich jemals auf eine Situation stoße, in der ich die Unterklassenlösung nicht verwenden kann (wenn ich nicht über den Code verfüge), könnte ich versuchen, dieses Steuerelement in das Formular einzufügen :) (obwohl ich mit größerer Wahrscheinlichkeit versuchen werde, die DoubleBuffered-Eigenschaft von außen mit Reflection zu erzwingen, um die Abhängigkeit erneut zu vermeiden).

Es ist traurig, dass so eine einfache Sache so viel Zeit in Anspruch genommen hat ...

81
Corey Ross

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


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

Solange alle meine Instanzen des Grids diese benutzerdefinierte Version verwenden, ist alles in Ordnung. Wenn ich jemals auf eine Situation stoße, in der ich die Unterklassenlösung nicht verwenden kann (wenn ich nicht über den Code verfüge), könnte ich versuchen, dieses Steuerelement in das Formular einzufügen :) (obwohl ich Es ist wahrscheinlicher, dass Sie versuchen, die DoubleBuffered-Eigenschaft mithilfe von Reflection von außen zu erzwingen, um die Abhängigkeit erneut zu vermeiden.

Es ist traurig, dass so eine einfache Sache so viel Zeit in Anspruch genommen hat ...

Hinweis: Machen Sie die Antwort zu einer Antwort, damit die Frage als beantwortet markiert werden kann

64
Benoit

Hier ist ein Code, der die Eigenschaft mithilfe von Reflection festlegt, ohne eine Unterklasse zu bilden, wie Benoit dies vorschlägt.

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

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})
15
GELR

Wenn ich zu früheren Beiträgen hinzufüge, verwende ich dies für Windows Forms-Anwendungen für DataGridView-Komponenten, 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 nach InitializeComponent () im Konstruktor DrawingControl.SetDoubleBuffered (control) auf.

Rufen Sie DrawingControl.SuspendDrawing (Steuerung) auf, bevor Sie große Datenmengen aktualisieren.

Rufen Sie DrawingControl.ResumeDrawing (Steuerung) auf, nachdem Sie große Datenmengen aktualisiert haben.

Diese letzten 2 werden am besten mit einem try/finally-Block ausgeführt. (Oder schreiben Sie die Klasse noch besser als IDisposable und rufen Sie SuspendDrawing() im Konstruktor und ResumeDrawing() in Dispose() auf.)

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();
    }
}
8
brtmckn

Die Antwort darauf hat auch bei mir 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 einwandfrei, es sei denn, die Benutzeroberfläche wird als Clientsitzung auf einem Remotedesktop ausgeführt, 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 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 Erkennen einer Remotedesktopverbindung

6
Kev

Ich habe eine Lösung für das Problem gefunden. Wechseln 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 er 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 habe ich es zurückgesetzt, wo es war und das Problem ging weg.

Hoffe, dieser Trick funktioniert auch für Sie.

1
user185931

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
user3727004

Um nur das 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 beheben. Es musste kein Code neu geschrieben werden.

Der Vollständigkeit halber handelte es sich bei der Karte um einen Nvidia Quadro NVS 290 mit Treibern vom März 2008 (Version 169). Durch die Aktualisierung auf die neueste Version (Version 182 vom Februar 2009) wurden die Paint-Ereignisse für alle meine Steuerelemente, insbesondere für DataGridView, erheblich verbessert.

Dieses Problem wurde auf keinen ATI-Karten (wo Entwicklung auftritt) gesehen.

1
Richard Morgan

Bei der Verwendung von .NET 3.0 und DataGridView auf einem Dual-Monitor-System ist ein ähnliches Problem aufgetreten.

Unsere Anwendung würde das Raster mit einem grauen Hintergrund anzeigen, was darauf hinweist, dass die Zellen nicht geändert werden konnten. Bei Auswahl einer Schaltfläche "Einstellungen ändern" ändert das Programm die Hintergrundfarbe der weißen Zellen, um dem Benutzer anzuzeigen, dass der Zellentext geändert werden kann. Ein "Abbrechen" -Button würde die Hintergrundfarbe der oben genannten Zellen wieder in Grau ändern.

Wenn sich die Hintergrundfarbe ändert, entsteht ein Flackern, ein kurzer Eindruck eines Rasters mit der Standardgröße und der gleichen Anzahl von Zeilen und Spalten. Dieses Problem würde nur auf dem primären Monitor (niemals auf dem sekundären) und nicht auf einem einzelnen Monitorsystem auftreten.

Das doppelte Puffern des Steuerelements unter Verwendung des obigen Beispiels löste unser Problem. Wir bedanken uns sehr für Ihre Hilfe.

0
James