Жахлива перемальовка продуктивності DataGridView на одному з моїх двох екранів


81

Я насправді це вирішив, але публікую для нащадків.

Я зіткнувся з дуже дивною проблемою з DataGridView в моїй системі подвійного монітора. Проблема виявляється НАДІЙНО повільним перефарбовуванням елемента керування ( наприклад, 30 секунд для повного перефарбовування ), але лише тоді, коли воно знаходиться на одному з моїх екранів. З іншого боку, швидкість перефарбовування чудова.

У мене є Nvidia 8800 GT з найновішими драйверами, що не є бета-версіями (175. щось). Це помилка драйвера? Я залишу це в повітрі, оскільки мені доведеться жити саме з цією конфігурацією. (Однак цього не відбувається на картах ATI ...)

Швидкість фарби не має нічого спільного із вмістом комірки, а спеціальне малювання взагалі не покращує продуктивність - навіть коли просто малюєте суцільний прямокутник.

Пізніше я дізнався, що розміщення ElementHost (із простору імен System.Windows.Forms.Integration) у формі виправляє проблему. З цим не треба плутатися; він просто повинен бути нащадком у формі, у якій також включений DataGridView. Його можна змінити до (0, 0), доки властивість Visible має значення true.

Я не хочу явно додавати залежність .NET 3 / 3.5 до мого додатку; Я створюю метод для створення цього елемента керування під час виконання (якщо це можливо) за допомогою відображення. Це працює, і принаймні виходить з ладу на машинах, які не мають необхідної бібліотеки - це просто повертається до повільної роботи.

Цей метод також дозволяє мені застосовувати виправлення під час запуску програми, полегшуючи побачення того, що змінюють бібліотеки WPF у моїй формі (за допомогою Spy ++).

Після багатьох спроб і помилок, я помічаю, що включення подвійної буферизації на самому елементі управління (на відміну від просто форми) виправляє проблему!


Отже, вам просто потрібно створити власний клас на основі DataGridView, щоб ви могли ввімкнути його DoubleBuffering. Це воно!

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

Поки всі мої екземпляри сітки використовують цю спеціальну версію, все добре. Якщо я коли-небудь потрапляю в ситуацію, спричинену цим, коли я не можу використовувати рішення підкласу (якщо у мене немає коду), я думаю, я міг би спробувати ввести цей контроль у форму :) ( хоча я ' Швидше буде спробувати використовувати відображення, щоб змусити властивість DoubleBuffered ззовні, щоб знову уникнути залежності ).

Сумно, що така тривіально проста штука з’їла стільки мого часу ...


1
У нас була подібна проблема з клієнтами, на яких встановлено Multimon . З будь-якої причини, коли вони вимикають Multimon, проблема зникає.
BlueRaja - Danny Pflughoeft

Хто-небудь обізнаний і може пояснити, чому це трапляється, і чому DoubleBuffered не можна ввімкнути за замовчуванням?
Vojtěch Dohnal

Відповіді:


64

Вам просто потрібно створити власний клас на основі DataGridView, щоб ви могли ввімкнути його DoubleBuffering. Це воно!


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

Поки всі мої екземпляри сітки використовують цю спеціальну версію, все добре. Якщо я коли-небудь потрапляю в ситуацію, спричинену цим, коли я не можу використовувати рішення підкласу (якщо у мене немає коду), я думаю, я міг би спробувати ввести цей контроль у форму :) (хоча я ' Швидше буде спробувати використовувати відображення, щоб змусити властивість DoubleBuffered ззовні, щоб знову уникнути залежності).

Сумно, що така тривіально проста штука з’їла стільки мого часу ...

Примітка: Відповідь дасть відповідь, щоб запитання можна було позначити як відповідь


1
Як ви можете це зробити за допомогою інтеграції Windows Forms для WPF ??
Частково

Дякую за відповідь. Як би Ви іноді не могли використовувати рішення підкласу? (Я не зрозумів біт "якщо у мене немає коду").
Dan W

Фантастично! Це працює як шарм у моєму проекті, який страждав від дивного уповільнення як при заповненні, так і при прокручуванні таблиці (:
knut

61

Ось деякий код, який встановлює властивість за допомогою відображення, без підкласу, як пропонує Бенуа.

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

3
Радий допомогти! Я майже не публікував його, тому що цьому питанню було вже рік.
Brian Ensink

1
Ні, це завжди допоможе комусь у майбутньому, наприклад, можливо, навіть мені, який щойно знайшов цю тему від Google. Дякую! До речі, чи бажано це розміщувати у розділі Form1_Load?
Dan W

2
Просто, щоб дати комусь іншому, хто знаходить це, ідею: Це корисний метод розширення Controlкласу. public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered).
Ентоні

Чи можна його розмістити у події завантаження FOrm там, де розміщено подання сітки даних?
Arie

18

Для людей, які шукають, як це зробити у VB.NET, ось код:

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

10

Додаючи до попередніх публікацій, для програм Windows Forms це те, що я використовую для компонентів DataGridView, щоб зробити їх швидкими. Код для класу DrawingControl знаходиться нижче.

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

Виклик DrawingControl.SetDoubleBuffered (control) після InitializeComponent () у конструкторі.

Зателефонуйте DrawingControl.SuspendDrawing (контроль) перед оновленням великих даних.

Викличте DrawingControl.ResumeDrawing (контроль) після оновлення великих даних.

Ці останні 2 найкраще робити за допомогою блоку try / konačno. (або навіть краще переписати клас as IDisposableта викликати SuspendDrawing()конструктор та 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();
    }
}

7

Відповідь на це спрацював і на мене. Я думав, що додам уточнення, яке, на мою думку, має бути стандартною практикою для кожного, хто впроваджує рішення.

Рішення працює добре, за винятком випадків, коли користувальницький інтерфейс виконується як сесія клієнта під віддаленим робочим столом, особливо там, де доступна пропускна здатність мережі низька. У такому випадку продуктивність може погіршитися завдяки використанню подвійної буферизації. Тому я пропоную наступне як більш повну відповідь:

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

Докладніше див. У розділі Виявлення підключення до віддаленого робочого столу


1

Я знайшов рішення проблеми. Перейдіть на вкладку усунення несправностей у розширених властивостях відображення та перевірте повзунок апаратного прискорення. Коли я отримав свій новий фірмовий ПК від ІТ, для нього було встановлено один флажок, і я не мав жодних проблем із сітками даних. Після того, як я оновив драйвер відеокарти та встановив його на повний, фарбування елементів керування сіткою даних стало дуже повільним. Тому я повернув його назад, де він був, і проблема пішла.

Сподіваюся, цей трюк спрацює і на вас.


1

Просто щоб додати, що ми зробили, щоб вирішити цю проблему: Ми оновили до останньої версії драйверів Nvidia вирішили проблему. Жодного коду не потрібно було переписувати.

Для повноти карткою стала Nvidia Quadro NVS 290 з драйверами від березня 2008 року (v. 169). Оновлення до останньої версії (v. 182 від лютого 2009 р.) Значно покращило події розфарбовування для всіх моїх елементів керування, особливо для DataGridView.

Цю проблему не було помічено ні на одній картці ATI (там, де відбувається розвиток).


1

Найкраще !:

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

0

Ми мали подібну проблему, використовуючи .NET 3.0 та DataGridView у системі подвійного монітора.

Наша програма відображатиме сітку із сірим фоном, що вказує на те, що клітинки змінити не можна. Після вибору кнопки "змінити налаштування" програма змінить колір тла комірок білим, щоб вказати користувачеві, що текст комірки можна змінити. Кнопка "скасувати" змінить колір тла згаданих клітинок на сірий.

Зі зміною кольору тла виникало мерехтіння, коротке враження розміру сітки за замовчуванням із однаковою кількістю рядків і стовпців. Ця проблема виникає лише на первинному моніторі (ніколи не на вторинному) і не виникає на одній системі монітора.

Подвійна буферизація управління, використовуючи наведений вище приклад, вирішила нашу проблему. Ми дуже вдячні за вашу допомогу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.