Frage

Ich habe eine Kontrolle, die ich zu großen Änderungen vornehmen müssen. Ich möchte ganz verhindern, dass neu zu zeichnen, während ich das tun - SuspendLayout und ResumeLayout sind nicht genug. Wie kann ich auszusetzen Malerei für eine Kontrolle und seine Kinder?

War es hilfreich?

Lösung

Bei meinem vorherigen Job kämpfen wir mit unserer reichen UI-App bekommen sofort und reibungslos zu malen. Wir wurden mit Standard-.NET-Steuerelemente, benutzerdefinierte Steuerelemente und DevExpress Kontrollen.

Nach vielen googeln und Reflektor Verwendung stieß ich auf der WM_SETREDRAW win32 Nachricht. Das ist wirklich stoppt Kontrollen zeichnen, während man sie aktualisieren und angewendet werden können, IIRC an die Eltern / enthält Panel.

Dies ist eine sehr sehr einfache Klasse demonstriert, wie diese Nachricht verwenden:

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();
    }
}

Es gibt vollere Diskussionen über das. - Google für C # und WM_SETREDRAW, z

C # Bammel

Suspending Layouts

Und wem es angeht, ist dies ähnlich Beispiel in 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

Andere Tipps

Hier finden Sie die gleiche Lösung von ng5000 aber nicht P / Invoke verwenden.

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();
    }
}

ich in der Regel ein wenig modifizierte Version von ngLink der verwenden Antwort .

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();
        }
    }
}

Auf diese Weise können Suspend / Resume ruft verschachtelt werden. Sie müssen sicherstellen, dass jedes SuspendDrawing mit einem ResumeDrawing übereinstimmen. Daher wäre es nicht wahrscheinlich eine gute Idee, um sie öffentlich zu machen.

Um mit nicht Zeichnung zu ermöglichen, zu vergessen:

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();
}

Nutzung:

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

Eine schöne Lösung ohne Verwendung von Interop:

Wie immer einfach aktiviert DoubleBuffered = auf dem Custom wahr. Dann, wenn Sie keine Behälter wie Flowlayoutpanel oder Table haben, leiten Sie eine Klasse von jedem dieser Typen und in der Konstrukteurs, doppelte Pufferung aktivieren. Nun, verwenden Sie einfach Ihre abgeleitete Container anstelle der Windows.Forms Container.

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

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

Basierend auf ng5000 Antwort, ich mag mit dieser Erweiterung:

        #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

Verwendung:

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

Hier ist eine Kombination aus ceztko ist und ng5000 ist eine VB-Erweiterungen Version zu bringen, die nicht pinvoke

nicht verwendet
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

Ich weiß, das ist eine alte Frage, schon beantwortet, aber hier ist mein nehmen auf diese; Ich Refactoring die Aussetzung des Updates in eine IDisposable -. So kann ich die Aussagen einschließen kann ich in einer using Anweisung ausgeführt werden soll

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();
    }
}

Das ist noch einfacher, und vielleicht Hacky -. Wie ich viele GDI Muskel auf diesem Thread sehen , und offensichtlich nur eine gute Passform für bestimmte Szenarien YMMV

In meinem Szenario verwende ich, was ich als „Parent“ Usercontrol beziehen werden - und während des Load Ereignisses, ich entfernen Sie einfach die von dem .Controls Sammlung Parent Kontrolle zu-manipuliert werden, und der OnPaint Parent nimmt Pflege vollständig das Kind Kontrolle in irgendeiner besonderen Weise zu malen .. die Farbe völlig Fähigkeiten offline Kind nehmen.

Jetzt Hand, ich mein Kind male Routine zu einer Verlängerung Methode aus diesem Konzept von Michael Gold für das Drucken Fenster bildet .

Hier bin ich, um eine Untergruppe von Etiketten machen senkrecht , um das Layout:

Dann befreie ich das Kind Kontrolle von im ParentUserControl.Load Ereignishandler mit diesem Code gemalt:

Private Sub ParentUserControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    SetStyle(ControlStyles.UserPaint, True)
    SetStyle(ControlStyles.AllPaintingInWmPaint, True)

    'exempt this control from standard painting: 
    Me.Controls.Remove(Me.HostedControlToBeRotated) 
End Sub

Dann in der gleichen ParentUserControl, wir malen die Steuerung zu manipulierten werden von Grund auf:

Protected Overrides Sub OnPaint(e As PaintEventArgs)
    'here, we will custom paint the HostedControlToBeRotated instance...

    'twist rendering mode 90 counter clockwise, and shift rendering over to right-most end 
    e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    e.Graphics.TranslateTransform(Me.Width - Me.HostedControlToBeRotated.Height, Me.Height)
    e.Graphics.RotateTransform(-90)
    MyCompany.Forms.CustomGDI.DrawControlAndChildren(Me.HostedControlToBeRotated, e.Graphics)

    e.Graphics.ResetTransform()
    e.Graphics.Dispose()

    GC.Collect()
End Sub

Wenn Sie die ParentUserControl irgendwo hosten, z.B. ein Windows Form - ich finde, dass mein Visual Studio 2015 macht die Form richtig zur Entwurfszeit sowie Laufzeit: ParentUserControl gehostet in einem Windows Form oder vielleicht auch andere Benutzersteuerung

Nun, da meine besondere Manipulation des Kind Kontrolle um 90 Grad dreht, ich bin sicher, dass alle Hot Spots und Interaktivität haben in dieser Region zerstört worden - aber das Problem, das ich war, die Lösung alle für einen Paketaufkleber, der Vorschau erforderlich und Druck, die gut für mich gearbeitet.

Wenn es gibt Möglichkeiten, die Hot Spots und Steuer-ness meine absichtlich verwaiste Kontrolle wieder einführen - würde ich gerne über diese einen Tag zu lernen (nicht für dieses Szenario, natürlich, aber .. nur zu lernen). Natürlich unterstützt WPF solche Verrücktheit OOTB .. aber .. hey .. WinForms immer noch so viel Spaß macht, amiright?

Oder benutzen Sie einfach Control.SuspendLayout() und Control.ResumeLayout().

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top