Pytanie Okropne przerysowanie wydajności DataGridView na jednym z moich dwóch ekranów


Naprawdę to rozwiązałem, ale publikuję to dla potomności.

Wystąpił bardzo dziwny problem z DataGridView w moim systemie z dwoma monitorami. Problem ten objawia się jako NIEZWŁOCZNIE powolne odświeżenie kontroli (jak 30 sekund na pełne odmalowanie), ale tylko wtedy, gdy jest na jednym z moich ekranów. Kiedy z drugiej strony, prędkość odmalowywania jest w porządku.

Mam Nvidię 8800 GT z najnowszymi sterownikami bez wersji beta (175. coś). Czy to błąd sterownika? Zostawię to w powietrzu, ponieważ muszę żyć z tą szczególną konfiguracją. (Nie dzieje się to na kartach ATI, chociaż ...)

Prędkość malowania nie ma nic wspólnego z zawartością komórki, a niestandardowy rysunek nie poprawia w ogóle wydajności - nawet podczas malowania solidnego prostokąta.

Później dowiaduję się, że umieszczenie elementu ElementHost (z przestrzeni nazw System.Windows.Forms.Integration) w formularzu rozwiązuje problem. To nie musi być zmylone; to po prostu musi być dzieckiem formularza, na którym jest również DataGridView. Można go zmienić na (0, 0) tak długo jak Widoczny właściwość jest prawdziwa.

Nie chcę jednoznacznie dodawać zależności .NET 3 / 3.5 do mojej aplikacji; Tworzę metodę tworzenia tej kontroli w środowisku wykonawczym (jeśli może) za pomocą refleksji. Działa, a przynajmniej nie zawodzi z wdziękiem na maszynach, które nie mają wymaganej biblioteki - po prostu powraca do powolności.

Ta metoda pozwala mi również stosować poprawki podczas działania aplikacji, dzięki czemu łatwiej jest zobaczyć, co zmieniają biblioteki WPF w moim formularzu (używając Spy ++).

Po wielu próbach i błędach zauważam, że włączenie podwójnego buforowania w samym sterowaniu (w przeciwieństwie do samego formularza) rozwiązuje problem!


Musisz więc stworzyć niestandardową klasę opartą na DataGridView, abyś mógł włączyć DoubleBuffering. To jest to!

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

Dopóki wszystkie moje wystąpienia siatki używają tej niestandardowej wersji, wszystko jest w porządku. Jeśli kiedykolwiek napotkam na sytuacji spowodowanej przez to, że nie jestem w stanie użyć rozwiązania podklasy (jeśli nie mam kodu), przypuszczam, że mógłbym spróbować wprowadzić tę kontrolę do formularza :) (chociaż będę bardziej skłonny spróbować użyć refleksji, aby wymusić na DoubleBuffered właściwość z zewnątrz, aby ponownie uniknąć zależności).

To smutne, że tak trywialnie prosta rzecz pożerała tyle mojego czasu ...


76
2017-09-23 01:01


pochodzenie


Mieliśmy podobny problem z klientami, którzy to zrobili Multimon zainstalowany. Z jakiegoś powodu, kiedy wyłączają Multimon, problem znika. - BlueRaja - Danny Pflughoeft


Odpowiedzi:


Trzeba tylko utworzyć niestandardową klasę na podstawie DataGridView, aby można było włączyć DoubleBuffering. To jest to!


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

Dopóki wszystkie moje wystąpienia siatki używają tej niestandardowej wersji, wszystko jest w porządku. Jeśli kiedykolwiek napotkam na sytuację spowodowaną przez to, że nie jestem w stanie użyć rozwiązania podklasy (jeśli nie mam kodu), przypuszczam, że mógłbym spróbować wprowadzić tę kontrolę do formularza :) (chociaż ja ' Będzie bardziej prawdopodobne, że spróbuje użyć refleksji do wymuszenia na obiekcie DoubleBuffered z zewnątrz, aby ponownie uniknąć zależności).

To smutne, że tak trywialnie prosta rzecz pożerała tyle mojego czasu ...

Uwaga: Udzielenie odpowiedzi jest odpowiedzią, więc pytanie można oznaczyć jako odpowiedź


60
2017-10-01 19:49



Jak to zrobić z Windows Forms Integration for WPF ?? - Partial
Dziękuję za odpowiedź. Jak czasami nie byłbyś w stanie użyć rozwiązania podklasy? (Nie rozumiem bitów "jeśli nie mam kodu"). - Dan W
Fantastyczny! Działa jak urok w moim projekcie, który cierpi z powodu dziwnego spowolnienia zarówno na wypełnianiu, jak i przewijaniu stołu (: - knut


Oto kod, który ustawia właściwość za pomocą odbicia, bez podklasy, jak sugeruje Benoit.

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

58
2018-04-24 18:52



Miło, że mogłem pomóc! Prawie nie opublikowałem tego, ponieważ pytanie to miało już rok. - Brian Ensink
Neah, to zawsze pomoże komuś w przyszłości, takim jak może nawet ja, który właśnie znalazł ten wątek od Google. Dzięki! Btw, czy lepiej jest umieścić to w sekcji Form1_Load? - Dan W
Aby dać komuś, kto znajdzie taki pomysł: Jest to przydatna metoda rozszerzenia na Control klasa. public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered). - Anthony


Dla osób szukających tego w VB.NET, oto kod:

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

13
2018-05-18 15:15





Dodając do poprzednich postów, dla aplikacji Windows Forms to jest to, czego używam dla komponentów DataGridView, aby je szybko. Kod klasy DrawingControl znajduje się poniżej.

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

Wywołaj DrawingControl.SetDoubleBuffered (control) po InitializeComponent () w konstruktorze.

Przed wykonaniem dużych aktualizacji danych wywołaj DrawingControl.SuspendDrawing (kontrola).

Wywołaj DrawingControl.ResumeDrawing (control) po wykonaniu dużych aktualizacji danych.

Te ostatnie 2 najlepiej wykonać przy pomocy bloku try / finally. (lub jeszcze lepiej przepisać klasę jako IDisposable i zadzwoń SuspendDrawing() w konstruktorze i ResumeDrawing() w 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();
    }
}

7
2018-04-13 16:45





Odpowiedź na to pytanie również zadziałała. Pomyślałem, że dodaję wyrafinowanie, które moim zdaniem powinno być standardową praktyką dla każdego, kto wdraża rozwiązanie.

Rozwiązanie działa dobrze, z wyjątkiem sytuacji, gdy interfejs jest uruchamiany jako sesja klienta pod zdalnym pulpitem, zwłaszcza gdy dostępna przepustowość sieci jest niska. W takim przypadku wydajność może być gorsza dzięki zastosowaniu podwójnego buforowania. Dlatego proponuję poniższe jako bardziej kompletną odpowiedź:

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

Aby uzyskać więcej informacji, zobacz Wykrywanie połączenia zdalnego pulpitu


6
2017-10-07 20:28





Znalazłem rozwiązanie tego problemu. Przejdź do karty rozwiązywania problemów w zaawansowanych właściwościach ekranu i sprawdź suwak przyspieszenia sprzętowego. Kiedy dostałem nowy komputer firmowy od IT, ustawiono go na jeden od pełnego tiku i nie miałem żadnych problemów z datagridami. Gdy zaktualizowałem sterownik karty wideo i ustawiłem go na pełny, malowanie kontrolek datagrid stało się bardzo powolne. Więc zresetowałem go z powrotem tam, gdzie był i problem zniknął.

Mam nadzieję, że ta sztuczka działa również dla ciebie.


1
2018-02-26 22:35





Aby dodać, co zrobiliśmy, aby rozwiązać ten problem: zaktualizowaliśmy system do najnowszej wersji sterowników Nvidii, aby rozwiązać problem. Nie trzeba było przepisywać kodu.

Dla kompletności, karta była Nvidia Quadro NVS 290 z sterownikami z marca 2008 r. (Wer. 169). Aktualizacja do najnowszej wersji (wer. 182 z lutego 2009 r.) Znacznie poprawiła warunki lakierowania dla wszystkich kontrolek, szczególnie dla DataGridView.

Ten problem nie był widoczny na żadnych kartach ATI (w których występuje rozwój).


1
2018-06-10 16:54