190

大きな変更を加える必要があるコントロールがあります。それをしている間、再描画を完全に防ぎたいと思います-SuspendLayoutとResumeLayoutは十分ではありません。コントロールとその子の描画を一時停止するにはどうすればよいですか?

4

10 に答える 10

323

以前の仕事では、リッチ 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, IntPtr.Zero)
        If Redraw Then
            Target.Refresh()
        End If
    End Sub

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

    <Extension()>
    Public Sub ResumeDrawing(ByVal Target As Control)
        ResumeDrawing(Target, True)
    End Sub
End Module
于 2009-01-28T14:15:31.890 に答える
53

以下は 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();
    }
}
于 2011-06-10T04:36:33.073 に答える
19

私は通常、ngLink のanswerを少し変更したバージョンを使用します。

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ます。したがって、それらを公開することはおそらく良い考えではありません。

于 2009-11-24T13:04:07.487 に答える
14

描画を再度有効にすることを忘れないようにするには:

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();
});
于 2015-03-10T11:40:42.813 に答える
8

相互運用を使用しない優れたソリューション:

いつものように、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;
    }
}
于 2010-03-10T22:53:44.513 に答える
4

これは ceztko と ng5000 の組み合わせで、pinvoke を使用しない VB 拡張バージョンをもたらします。

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
于 2012-03-12T15:47:57.123 に答える
3

これは古い質問であり、すでに回答されていることは知っていますが、これが私の見解です。更新の一時停止を IDisposable にリファクタリングしました。これにより、実行したいステートメントをステートメントで囲むことができusingます。

class SuspendDrawingUpdate : IDisposable
{
    private const int WM_SETREDRAW = 0x000B;
    private readonly Control _control;
    private readonly NativeWindow _window;

    public SuspendDrawingUpdate(Control control)
    {
        _control = control;

        var msgSuspendUpdate = Message.Create(_control.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);

        _window = NativeWindow.FromHandle(_control.Handle);
        _window.DefWndProc(ref msgSuspendUpdate);
    }

    public void Dispose()
    {
        var wparam = new IntPtr(1);  // Create a C "true" boolean as an IntPtr
        var msgResumeUpdate = Message.Create(_control.Handle, WM_SETREDRAW, wparam, IntPtr.Zero);

        _window.DefWndProc(ref msgResumeUpdate);

        _control.Invalidate();
    }
}
于 2015-01-05T23:56:50.853 に答える
-4

または、 と を使用Control.SuspendLayout()Control.ResumeLayout()ます。

于 2012-01-14T15:22:38.380 に答える