Ужасная производительность перерисовки DataGridView на одном из моих двух экранов

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

Я столкнулся с очень странной проблемой с DataGridView на моей системе с двумя мониторами. Проблема проявляется как ЧРЕЗВЫЧАЙНАЯ медленная перерисовка управления (например, 30 секунд для полной перерисовки), но только когда она находится на одном из моих экранов. Когда с другой стороны, скорость перерисовки прекрасна.

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

Скорость рисования не имеет ничего общего с содержимым ячейки, и пользовательский чертеж не улучшает производительность вообще - даже когда вы просто рисуете сплошной прямоугольник.

Позже я узнаю, что размещение ElementHost (из пространства имен System.Windows.Forms.Integration) в форме исправляет проблему. Его не нужно путать; он просто должен быть дочерним элементом формы DataGridView. Он может быть изменен до (0, 0) до тех пор, пока свойство Visible истинно.

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

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

После большого количества проб и ошибок я замечаю, что включение двойной буферизации в самом элементе управления (в отличие от только формы) исправляет проблему!


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

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

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

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

Ответ 1

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


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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

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

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

Эти последние 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

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

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

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

Ответ 7

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

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

Ответ 8

Лучший:

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

Ответ 9

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

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

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

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