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


81

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

듀얼 모니터 시스템에서 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 있지만, 다시 한 번 종속성을 피하기 위해 DoubleBuffered 속성을 외부에서 강제로 사용하도록 리플렉션을 사용하려고 시도 할 가능성이 더 높습니다 .

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


1
Multimon이 설치된 클라이언트와 비슷한 문제가있었습니다 . 어떤 이유로 든 Multimon을 끄면 문제가 사라집니다.
BlueRaja-Danny Pflughoeft

누구나 알고 있고 왜 이런 일이 발생하고 DoubleBuffered를 기본적으로 켤 수 없는지 설명 할 수 있습니까?
Vojtěch Dohnal

답변:


64

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


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

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

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

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


1
WPF 용 Windows Forms 통합으로 어떻게이 작업을 수행 할 수 있습니까 ??
Partial

답변 해주셔서 감사합니다. 때때로 서브 클래스 솔루션을 어떻게 사용할 수 없습니까? ( "코드가없는 경우"비트를 이해하지 못했습니다.)
Dan W

환상적입니다! 테이블을
채우고

61

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

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

3
도와 주셔서 감사합니다! 이 질문은 이미 1 년이 되었기 때문에 거의 게시하지 않았습니다.
Brian Ensink

1
Neah, Google에서 방금이 스레드를 찾은 저와 같은 미래의 누군가에게 항상 도움이 될 것입니다. 감사! Btw, 이것을 Form1_Load 섹션에 넣는 것이 낫습니까?
댄 W

2
이것을 발견 한 다른 사람에게 아이디어를 제공하기 위해 : 이것은 Control클래스 에서 유용한 확장 메소드입니다 . public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered).
Anthony

데이터 그리드보기가 배치 된 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)

생성자에서 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();
    }
}

7

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

이 솔루션은 UI가 원격 데스크톱에서 클라이언트 세션으로 실행되는 경우, 특히 사용 가능한 네트워크 대역폭이 낮은 경우를 제외하고는 잘 작동합니다. 이 경우 이중 버퍼링을 사용하면 성능이 저하 될 수 있습니다. 따라서 더 완전한 대답으로 다음을 제안합니다.

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

자세한 내용은 원격 데스크톱 연결 감지를 참조하십시오.


1

문제에 대한 해결책을 찾았습니다. 고급 디스플레이 속성의 문제 해결 탭으로 이동하여 하드웨어 가속 슬라이더를 확인하십시오. IT에서 새 회사 PC를 받았을 때 전체에서 1 틱으로 설정되었고 데이터 그리드에 문제가 없었습니다. 비디오 카드 드라이버를 업데이트하고 전체로 설정하면 데이터 그리드 컨트롤의 페인팅이 매우 느려졌습니다. 그래서 나는 그것을 원래 위치로 재설정하고 문제가 사라졌습니다.

이 트릭이 당신에게도 효과가 있기를 바랍니다.


1

이 문제를 해결하기 위해 수행 한 작업을 추가하기 위해 최신 Nvidia 드라이버로 업그레이드하여 문제를 해결했습니다. 코드를 다시 작성할 필요가 없습니다.

완성도를 위해 카드는 2008 년 3 월 (v. 169) 드라이버가있는 Nvidia Quadro NVS 290이었습니다. 최신 버전 (v. 182, 2009 년 2 월)으로 업그레이드하면 모든 컨트롤, 특히 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를 사용하여 유사한 문제가 발생했습니다.

애플리케이션은 회색 배경으로 그리드를 표시하여 셀을 변경할 수 없음을 나타냅니다. "설정 변경"버튼을 선택하면 프로그램이 셀의 배경색을 흰색으로 변경하여 사용자에게 셀 텍스트를 변경할 수 있음을 나타냅니다. "취소"버튼은 앞서 언급 한 셀의 배경색을 다시 회색으로 변경합니다.

배경색이 변경되면 깜박임이 발생하고 동일한 수의 행과 열이있는 기본 크기의 격자가 잠시 나타납니다. 이 문제는 기본 모니터에서만 발생하며 (보조 모니터는 아님) 단일 모니터 시스템에서는 발생하지 않습니다.

위의 예제를 사용하여 컨트롤을 이중 버퍼링하면 문제가 해결되었습니다. 도움을 주셔서 대단히 감사합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.