[C#] 컨트롤 및 해당 자식에 대한 페인팅을 일시 중단하려면 어떻게합니까?

큰 수정을해야하는 컨트롤이 있습니다. SuspendLayout과 ResumeLayout으로 충분하지 않은 동안 다시 그리기를 완전히 방지하고 싶습니다. 컨트롤 및 해당 자식에 대한 페인팅을 일시 중단하려면 어떻게합니까?



답변

이전 작업에서 풍부한 UI 앱을 즉각적이고 매끄럽게 페인트하는 데 어려움을 겪었습니다. 표준 .Net 컨트롤, 사용자 지정 컨트롤 및 devexpress 컨트롤을 사용하고있었습니다.

많은 인터넷 검색 및 리플렉터 사용 후 WM_SETREDRAW win32 메시지가 나타났습니다. 이것은 컨트롤을 업데이트하는 동안 컨트롤 그리기를 실제로 중지하고 IIRC를 부모 / 포함 패널에 적용 할 수 있습니다.

이 메시지를 사용하는 방법을 보여주는 매우 간단한 클래스입니다.

class DrawingControl
{
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

    private const int WM_SETREDRAW = 11;

    public static void SuspendDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
    }

    public static void ResumeDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
        parent.Refresh();
    }
}

C # 및 WM_SETREDRAW에 대한 Google에 대한 자세한 토론이 있습니다.

C # 지터

현수 레이아웃

그리고 누가 관심을 가질 지에, 이것은 VB에서 비슷한 예입니다.

Public Module Extensions
    <DllImport("user32.dll")>
    Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Boolean, ByVal lParam As IntPtr) As Integer
    End Function

    Private Const WM_SETREDRAW As Integer = 11

    ' Extension methods for Control
    <Extension()>
    Public Sub ResumeDrawing(ByVal Target As Control, ByVal Redraw As Boolean)
        SendMessage(Target.Handle, WM_SETREDRAW, True, 0)
        If Redraw Then
            Target.Refresh()
        End If
    End Sub

    <Extension()>
    Public Sub SuspendDrawing(ByVal Target As Control)
        SendMessage(Target.Handle, WM_SETREDRAW, False, 0)
    End Sub

    <Extension()>
    Public Sub ResumeDrawing(ByVal Target As Control)
        ResumeDrawing(Target, True)
    End Sub
End Module


답변

다음은 ng5000과 동일한 솔루션이지만 P / Invoke를 사용하지 않습니다.

public static class SuspendUpdate
{
    private const int WM_SETREDRAW = 0x000B;

    public static void Suspend(Control control)
    {
        Message msgSuspendUpdate = Message.Create(control.Handle, WM_SETREDRAW, IntPtr.Zero,
            IntPtr.Zero);

        NativeWindow window = NativeWindow.FromHandle(control.Handle);
        window.DefWndProc(ref msgSuspendUpdate);
    }

    public static void Resume(Control control)
    {
        // Create a C "true" boolean as an IntPtr
        IntPtr wparam = new IntPtr(1);
        Message msgResumeUpdate = Message.Create(control.Handle, WM_SETREDRAW, wparam,
            IntPtr.Zero);

        NativeWindow window = NativeWindow.FromHandle(control.Handle);
        window.DefWndProc(ref msgResumeUpdate);

        control.Invalidate();
    }
}


답변

나는 보통 약간 수정 된 ngLink의 답변 버전을 사용합니다 .

public class MyControl : Control
{
    private int suspendCounter = 0;

    private void SuspendDrawing()
    {
        if(suspendCounter == 0)
            SendMessage(this.Handle, WM_SETREDRAW, false, 0);
        suspendCounter++;
    }

    private void ResumeDrawing()
    {
        suspendCounter--;
        if(suspendCounter == 0)
        {
            SendMessage(this.Handle, WM_SETREDRAW, true, 0);
            this.Refresh();
        }
    }
}

이를 통해 일시 중단 / 재개 통화를 중첩 할 수 있습니다. 각각 SuspendDrawing과 일치해야합니다 ResumeDrawing. 따라서 공개하는 것은 좋지 않을 것입니다.


답변

도면을 다시 활성화하는 것을 잊지 않으려면 다음을 수행하십시오.

public static void SuspendDrawing(Control control, Action action)
{
    SendMessage(control.Handle, WM_SETREDRAW, false, 0);
    action();
    SendMessage(control.Handle, WM_SETREDRAW, true, 0);
    control.Refresh();
}

용법:

SuspendDrawing(myControl, () =>
{
    somemethod();
});


답변

interop을 사용하지 않는 멋진 솔루션 :

항상 그렇듯이 CustomControl에서 DoubleBuffered = true를 활성화하십시오. 그런 다음 FlowLayoutPanel 또는 TableLayoutPanel과 같은 컨테이너가있는 경우 이러한 각 유형에서 클래스를 파생하고 생성자에서 이중 버퍼링을 사용하십시오. 이제 Windows.Forms 컨테이너 대신 파생 컨테이너를 사용하십시오.

class TableLayoutPanel : System.Windows.Forms.TableLayoutPanel
{
    public TableLayoutPanel()
    {
        DoubleBuffered = true;
    }
}

class FlowLayoutPanel : System.Windows.Forms.FlowLayoutPanel
{
    public FlowLayoutPanel()
    {
        DoubleBuffered = true;
    }
}


답변

ng5000의 답변에 따라이 확장을 사용하는 것이 좋습니다.

        #region Suspend
        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
        private const int WM_SETREDRAW = 11;
        public static IDisposable BeginSuspendlock(this Control ctrl)
        {
            return new suspender(ctrl);
        }
        private class suspender : IDisposable
        {
            private Control _ctrl;
            public suspender(Control ctrl)
            {
                this._ctrl = ctrl;
                SendMessage(this._ctrl.Handle, WM_SETREDRAW, false, 0);
            }
            public void Dispose()
            {
                SendMessage(this._ctrl.Handle, WM_SETREDRAW, true, 0);
                this._ctrl.Refresh();
            }
        }
        #endregion

사용하다:

using (this.BeginSuspendlock())
{
    //update GUI
}


답변

다음은 pinvoke를 사용하지 않는 VB 확장 버전을 가져 오기 위해 ceztko와 ng5000을 결합한 것입니다.

Imports System.Runtime.CompilerServices

Module ControlExtensions

Dim WM_SETREDRAW As Integer = 11

''' <summary>
''' A stronger "SuspendLayout" completely holds the controls painting until ResumePaint is called
''' </summary>
''' <param name="ctrl"></param>
''' <remarks></remarks>
<Extension()>
Public Sub SuspendPaint(ByVal ctrl As Windows.Forms.Control)

    Dim msgSuspendUpdate As Windows.Forms.Message = Windows.Forms.Message.Create(ctrl.Handle, WM_SETREDRAW, System.IntPtr.Zero, System.IntPtr.Zero)

    Dim window As Windows.Forms.NativeWindow = Windows.Forms.NativeWindow.FromHandle(ctrl.Handle)

    window.DefWndProc(msgSuspendUpdate)

End Sub

''' <summary>
''' Resume from SuspendPaint method
''' </summary>
''' <param name="ctrl"></param>
''' <remarks></remarks>
<Extension()>
Public Sub ResumePaint(ByVal ctrl As Windows.Forms.Control)

    Dim wparam As New System.IntPtr(1)
    Dim msgResumeUpdate As Windows.Forms.Message = Windows.Forms.Message.Create(ctrl.Handle, WM_SETREDRAW, wparam, System.IntPtr.Zero)

    Dim window As Windows.Forms.NativeWindow = Windows.Forms.NativeWindow.FromHandle(ctrl.Handle)

    window.DefWndProc(msgResumeUpdate)

    ctrl.Invalidate()

End Sub

End Module