Domanda

In realtà l'ho risolto, ma lo sto postando per i posteri.

Ho riscontrato un problema molto strano con DataGridView sul mio sistema a doppio monitor. Il problema si manifesta come una riverniciatura ESTREMAMENTE lenta del controllo ( come 30 secondi per una riverniciatura completa ), ma solo quando è su uno dei miei schermi. Dall'altro, la velocità di ridipingere va bene.

Ho una Nvidia 8800 GT con gli ultimi driver non beta (175. qualcosa). È un bug del driver? Lo lascerò in aria, dal momento che devo convivere con questa particolare configurazione. (Non succede sulle carte ATI, però ...)

La velocità di pittura non ha nulla a che fare con il contenuto della cella e il disegno personalizzato non migliora affatto le prestazioni, anche quando si dipinge solo un rettangolo solido.

In seguito scopro che inserendo un ElementHost (dallo spazio dei nomi System.Windows.Forms.Integration) sul modulo si risolve il problema. Non deve essere incasinato; deve solo essere figlio del modulo su cui si trova anche DataGridView. Può essere ridimensionato su (0, 0) purché la proprietà Visibile sia vera.

Non voglio aggiungere esplicitamente la dipendenza .NET 3 / 3.5 alla mia applicazione; Faccio un metodo per creare questo controllo in fase di esecuzione (se possibile) usando la riflessione. Funziona e almeno non riesce con grazia su macchine che non hanno la libreria richiesta - torna solo ad essere lento.

Questo metodo mi permette anche di applicare la correzione mentre l'app è in esecuzione, rendendo più facile vedere cosa cambiano le librerie WPF sul mio modulo (usando Spy ++).

Dopo molte prove ed errori, noto che abilitare il doppio buffering sul controllo stesso (al contrario del solo modulo) risolve il problema!


Quindi, devi solo creare una classe personalizzata basata su DataGridView in modo da poter abilitare il suo DoubleBuffering. Questo è tutto!

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

Finché tutte le mie istanze della griglia utilizzano questa versione personalizzata, va tutto bene. Se mai dovessi imbattermi in una situazione causata da ciò in cui non sono in grado di utilizzare la soluzione della sottoclasse (se non ho il codice), suppongo che potrei provare a iniettare quel controllo nel modulo :) ( anche se avrò più probabilità di provare a usare la riflessione per forzare la proprietà DoubleBuffered dall'esterno a evitare ancora una volta la dipendenza ).

È triste che una cosa così banalmente semplice abbia mangiato così tanto del mio tempo ...

È stato utile?

Soluzione

Devi solo creare una classe personalizzata basata su DataGridView in modo da poter abilitare il suo DoubleBuffering. Questo è tutto!


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

Finché tutte le mie istanze della griglia utilizzano questa versione personalizzata, va tutto bene. Se mai dovessi imbattermi in una situazione causata da ciò in cui non sono in grado di utilizzare la soluzione della sottoclasse (se non ho il codice), suppongo che potrei provare a iniettare quel controllo nel modulo :) (anche se sarà più probabile provare a usare la riflessione per forzare la proprietà DoubleBuffered dall'esterno ad evitare ancora una volta la dipendenza).

È triste che una cosa così banalmente semplice abbia mangiato così tanto del mio tempo ...

Nota: rendere la risposta una risposta in modo che la domanda possa essere contrassegnata come risposta

Altri suggerimenti

Ecco del codice che imposta la proprietà usando reflection, senza sottoclasse come suggerisce Benoit.

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

Per le persone che cercano come farlo in VB.NET, ecco il codice:

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

Aggiungendo ai post precedenti, per le applicazioni Windows Form questo è quello che uso per i componenti DataGridView per renderli veloci. Il codice per la classe DrawingControl è sotto.

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

Chiama DrawingControl.SetDoubleBuffered (controllo) dopo InitializeComponent () nel costruttore.

Chiama DrawingControl.SuspendDrawing (controllo) prima di eseguire aggiornamenti sui big data.

Chiama DrawingControl.ResumeDrawing (controllo) dopo aver effettuato gli aggiornamenti dei big data.

Questi ultimi 2 sono fatti meglio con un blocco try / finally. (o ancora meglio riscrivere la classe come IDisposable e chiamare SuspendDrawing () nel costruttore e 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();
    }
}

La risposta a questo ha funzionato anche per me. Ho pensato di aggiungere un perfezionamento che penso dovrebbe essere una pratica standard per chiunque stia implementando la soluzione.

La soluzione funziona bene tranne quando l'interfaccia utente viene eseguita come sessione client su desktop remoto, in particolare quando la larghezza di banda di rete disponibile è bassa. In tal caso, le prestazioni possono essere aggravate dall'uso del doppio buffering. Pertanto, suggerisco quanto segue come risposta più completa:

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

Per ulteriori dettagli, consultare Rilevamento della connessione desktop remoto

Ho trovato una soluzione al problema. Vai alla scheda Risoluzione dei problemi nelle proprietà di visualizzazione avanzate e controlla il cursore di accelerazione hardware. Quando ho ottenuto il mio nuovo PC aziendale dall'IT, era impostato su un segno di spunta e non ho avuto problemi con i datagrid. Dopo aver aggiornato il driver della scheda video e averlo impostato al massimo, la verniciatura dei controlli di Datagrid è diventata molto lenta. Quindi l'ho ripristinato al punto in cui si trovava e il problema è scomparso.

Spero che questo trucco funzioni anche per te.

Solo per aggiungere ciò che abbiamo fatto per risolvere questo problema: abbiamo aggiornato gli ultimi driver Nvidia risolto il problema. Nessun codice ha dovuto essere riscritto.

Per completezza, la scheda era una Nvidia Quadro NVS 290 con driver datato marzo 2008 (v. 169). L'aggiornamento all'ultimo (v. 182 del febbraio 2009) ha migliorato significativamente gli eventi di disegno per tutti i miei controlli, in particolare per DataGridView.

Questo problema non è stato riscontrato su alcuna scheda ATI (in cui si verifica lo sviluppo).

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

Abbiamo riscontrato un problema simile utilizzando .NET 3.0 e DataGridView su un sistema a doppio monitor.

La nostra applicazione visualizzerebbe la griglia con uno sfondo grigio, indicando che le celle non possono essere modificate. Selezionando un " modifica impostazioni " pulsante, il programma cambierebbe il colore di sfondo delle celle in bianco per indicare all'utente che il testo della cella poteva essere modificato. A " annulla " il pulsante cambierebbe di nuovo il colore di sfondo delle suddette celle in grigio.

Man mano che il colore di sfondo cambiava, si verificava uno sfarfallio, una breve impressione di una griglia di dimensioni predefinite con lo stesso numero di righe e colonne. Questo problema si verificherebbe solo sul monitor principale (mai sul secondario) e non si verificherebbe su un singolo sistema di monitoraggio.

Il doppio buffering del controllo, usando l'esempio sopra, ha risolto il nostro problema. Abbiamo molto apprezzato il tuo aiuto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top