Ужасная производительность перерисовки DataGridView на одном из моих двух экранов - PullRequest
81 голосов
/ 23 сентября 2008

Я действительно решил это, но выкладываю это для потомков.

Я столкнулся с очень странной проблемой с 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 извне, чтобы еще раз избежать зависимости ).

Печально, что такая банально простая вещь съела так много моего времени ...

Ответы [ 9 ]

64 голосов
/ 01 октября 2008

Вам просто нужно создать собственный класс на основе DataGridView, чтобы вы могли включить его DoubleBuffering. Вот и все!


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

Пока все мои экземпляры сетки используют эту пользовательскую версию, все хорошо. Если я когда-нибудь столкнусь с ситуацией, вызванной этим, когда я не смогу использовать решение подкласса (если у меня нет кода), я полагаю, я мог бы попытаться внедрить этот элемент управления в форму :) С большей вероятностью можно будет попытаться использовать отражение, чтобы принудительно включить свойство DoubleBuffered извне, чтобы еще раз избежать зависимости).

Печально, что такая банально простая вещь съела так много моего времени ...

Примечание: сделать ответ ответом, чтобы вопрос можно было пометить как ответивший

59 голосов
/ 01 октября 2009

Вот некоторый код, который устанавливает свойство, используя отражение, без подклассов, как предлагает Бенуа.

typeof(DataGridView).InvokeMember(
   "DoubleBuffered", 
   BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
   null, 
   myDataGridViewObject, 
   new object[] { true });
13 голосов
/ 24 апреля 2012

Для людей, которые ищут, как это сделать в VB.NET, вот код:

DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True})
8 голосов
/ 18 мая 2013

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

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

Вызовите DrawingControl.SetDoubleBuffered (control) после InitializeComponent () в конструкторе.

Вызовите DrawingControl.SuspendDrawing (control) перед выполнением обновлений больших данных.

Вызовите DrawingControl.ResumeDrawing (control) после выполнения больших обновлений данных.

Последние 2 лучше всего делать с блоком try / finally. (или даже лучше переписать класс как IDisposable и вызвать SuspendDrawing() в конструкторе и ResumeDrawing() в 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 голосов
/ 13 апреля 2013

Ответ на этот вопрос сработал и у меня. Я подумал, что добавлю уточнение, которое, по моему мнению, должно стать стандартной практикой для всех, кто внедряет решение.

Решение работает хорошо, за исключением случаев, когда пользовательский интерфейс запускается как сеанс клиента на удаленном рабочем столе, особенно там, где пропускная способность сети низкая. В таком случае производительность может быть ухудшена с помощью двойной буферизации. Поэтому в качестве более полного ответа предлагаю следующее:

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

Подробнее см. Обнаружение подключения к удаленному рабочему столу

1 голос
/ 10 июня 2014

Лучший:

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 голос
/ 08 октября 2009

Я нашел решение проблемы. Перейдите на вкладку устранения неполадок в дополнительных свойствах экрана и проверьте ползунок аппаратного ускорения. Когда я получил свой новый ПК компании от IT, он был установлен на один тик из полного, и у меня не было никаких проблем с сетями данных. Как только я обновил драйвер видеокарты и установил его на полную, рисование элементов управления сеткой данных стало очень медленным. Поэтому я вернул его туда, где он был, и проблема ушла.

Надеюсь, этот трюк работает и для вас.

1 голос
/ 27 февраля 2009

Просто добавим, что мы сделали, чтобы решить эту проблему: мы обновили драйверы Nvidia до последней версии, и решили эту проблему. Код не нужно было переписывать.

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

Эта проблема не была замечена ни на одной карте ATI (где происходит разработка).

0 голосов
/ 07 октября 2008

Мы столкнулись с подобной проблемой при использовании .NET 3.0 и DataGridView в системе с двумя мониторами.

Наше приложение будет отображать сетку на сером фоне, указывая, что ячейки не могут быть изменены. После выбора кнопки «изменить настройки» программа изменит цвет фона ячеек на белый, чтобы указать пользователю, что текст ячейки может быть изменен. Кнопка «Отмена» изменяет цвет фона вышеупомянутых ячеек на серый.

Когда цвет фона изменился, появилось мерцание, краткое впечатление от сетки размера по умолчанию с тем же количеством строк и столбцов. Эта проблема возникает только на основном мониторе (но не на дополнительном) и не возникает в системе с одним монитором.

Двойная буферизация элемента управления, используя приведенный выше пример, решила нашу проблему. Мы высоко ценим вашу помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...