developer tip

두 화면 중 하나에서 DataGridView의 끔찍한 다시 그리기 성능

optionbox 2020. 10. 5. 07:52
반응형

두 화면 중 하나에서 DataGridView의 끔찍한 다시 그리기 성능


나는 실제로 이것을 해결했지만 후손을 위해 게시하고 있습니다.

듀얼 모니터 시스템에서 DataGridView에 매우 이상한 문제가 발생했습니다. 이 문제는 컨트롤의 매우 느린 다시 그리기로 나타납니다 ( 전체 다시 그리기의 경우 30 초 정도 ).하지만 내 화면 중 하나에있을 때만 나타납니다. 다른 경우에는 다시 그리기 속도가 좋습니다.

최신 비 베타 드라이버 (175. 무언가)가있는 Nvidia 8800 GT가 있습니다. 드라이버 버그입니까? 이 특정 구성으로 살아야하므로 공중에 남겨 두겠습니다. (하지만 ATI 카드에서는 발생하지 않습니다 ...)

페인트 속도는 셀 내용과 관련이 없으며 사용자 정의 드로잉은 단색 사각형을 그리는 경우에도 성능을 전혀 향상시키지 않습니다.

나중에 System.Windows.Forms.Integration 네임 스페이스의 ElementHost를 폼에 배치하면 문제가 해결된다는 사실을 알게되었습니다. 엉망이 될 필요는 없습니다. DataGridView도 켜져있는 형식의 자식이어야합니다. Visible 속성이 true이면 크기를 (0, 0)으로 조정할 수 있습니다 .

내 응용 프로그램에 .NET 3 / 3.5 종속성을 명시 적으로 추가하고 싶지 않습니다. 리플렉션을 사용하여 런타임에 (가능한 경우)이 컨트롤을 만드는 방법을 만듭니다. 작동하고 최소한 필요한 라이브러리가없는 머신에서는 정상적으로 실패합니다. 다시 느려집니다.

이 방법을 사용하면 앱이 실행되는 동안 수정을 적용 할 수 있으므로 내 양식에서 WPF 라이브러리가 변경되는 내용을 쉽게 확인할 수 있습니다 (Spy ++ 사용).

많은 시행 착오 끝에 컨트롤 자체에서 이중 버퍼링을 활성화하면 (단지 양식이 아닌) 문제가 해결된다는 것을 알았습니다!


따라서 DoubleBuffering을 활성화 할 수 있도록 DataGridView를 기반으로 사용자 지정 클래스를 만들기 만하면됩니다. 그게 다야!

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

그리드의 모든 인스턴스가이 사용자 정의 버전을 사용하는 한 모든 것이 정상입니다. 내가 내가 (I 코드가없는 경우) 서브 클래스 솔루션을 사용할 수 아니에요 곳에서 발생하는 상황으로 실행하면, 나는 (양식 : 위에 해당 컨트롤을 삽입을 시도 할 수 있습니다 가정 'I 있지만, 다시 한 번 종속성을 피하기 위해 DoubleBuffered 속성을 외부에서 강제로 사용하도록 리플렉션을 사용하려고 시도 할 가능성이 더 큽니다 .

그렇게 사소한 일이 내 시간을 너무 많이 먹은 것이 슬프다 ...


DoubleBuffering을 활성화 할 수 있도록 DataGridView를 기반으로 사용자 지정 클래스를 만들어야합니다. 그게 다야!


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

그리드의 모든 인스턴스가이 사용자 정의 버전을 사용하는 한 모든 것이 정상입니다. 이로 인해 하위 클래스 솔루션을 사용할 수없는 상황이 발생하면 (코드가없는 경우) 해당 컨트롤을 폼에 삽입 할 수 있다고 가정합니다. 다시 한 번 종속성을 피하기 위해 DoubleBuffered 속성을 외부에서 강제로 사용하도록 리플렉션을 사용하려고 시도 할 가능성이 더 높습니다.)

그렇게 사소한 일이 내 시간을 너무 많이 먹은 것이 슬프다 ...

참고 : 질문이 답변 됨으로 표시 될 수 있도록 답변하기


다음은 Benoit이 제안한대로 서브 클래 싱없이 리플렉션을 사용하여 속성을 설정하는 코드입니다.

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

VB.NET에서 수행하는 방법을 검색하는 사람들을 위해 다음 코드가 있습니다.

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

이전 게시물에 추가하여 Windows Forms 응용 프로그램의 경우 DataGridView 구성 요소에 사용하여 빠르게 만들었습니다. DrawingControl 클래스의 코드는 다음과 같습니다.

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

생성자에서 InitializeComponent () 다음에 DrawingControl.SetDoubleBuffered (control)를 호출합니다.

빅 데이터 업데이트를 수행하기 전에 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();
    }
}

이것에 대한 대답은 나에게도 효과적이었습니다. 솔루션을 구현하는 모든 사람에게 표준 관행이되어야한다고 생각하는 개선 사항을 추가 할 것이라고 생각했습니다.

The solution works well except when the UI is being run as a client session under remote desktop, especially where the available network bandwidth is low. In such a case, performance can be made worse by the use of double-buffering. Therefore, I suggest the following as a more complete answer:

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

For more details, refer to Detecting remote desktop connection


I found a solution to the problem. Go to troubleshoot tab in the advanced display properties and check the hardware acceleration slider. When I got my new company PC from IT, it was set to one tick from full and I didn't have any problems with datagrids. Once I updated the video card driver and set it to full, painting of datagrid controls became very slow. So I reset it back to where it was and the problem went away.

Hope this trick works for you as well.


Just to add what we did to fix this issue: We upgraded to the latest Nvidia drivers solved the problem. No code had to be rewritten.

For completeness, the card was an Nvidia Quadro NVS 290 with drivers dated March 2008 (v. 169). Upgrading to the latest (v. 182 dated Feb 2009) significantly improved the paint events for all my controls, especially for the DataGridView.

This issue was not seen on any ATI cards (where development occurs).


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

We've experienced a similar problem using .NET 3.0 and DataGridView on a dual monitor system.

Our application would display the grid with a gray background, indicating that the cells could not be changed. Upon selecting a "change settings" button, the program would change the background color of the cells white to indicate to the user that the cell text could be changed. A "cancel" button would change the background color of the aforementioned cells back to gray.

As the background color changed there would be a flicker, a brief impression of a default sized grid with the same number of rows and columns. This problem would only occur on the primary monitor (never the secondary) and it would not occur on a single monitor system.

Double buffering the control, using the above example, solved our problem. We greatly appreciated your help.

참고URL : https://stackoverflow.com/questions/118528/horrible-redraw-performance-of-the-datagridview-on-one-of-my-two-screens

반응형