سؤال

لدي جهاز التحكم الذي يجب أن تجعل كبيرة التعديلات.أود أن يمنع تماما من رسم حين أفعل ذلك - SuspendLayout و ResumeLayout ليست كافية.كيف يمكنني تعليق لوحة التحكم و الأطفال ؟

هل كانت مفيدة؟

المحلول

وفي عملي السابق ناضلنا مع الحصول على لدينا التطبيق UI غنية لرسم على الفور وعلى نحو سلس. كنا باستخدام عناصر التحكم. NET القياسية، عناصر التحكم المخصصة والضوابط devexpress.

وبعد الكثير من غوغلينغ واستخدام عاكس جئت عبر رسالة Win32 وWM_SETREDRAW. وهذا يتوقف حقا الضوابط رسم بينما كنت تحديثها ويمكن تطبيقها، 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، منها مثلا

<وأ href = "https://web.archive.org/web/20160324214157/http://blog.bee-eee.com/2008/04/18/c-getting-rid-of-the- غضب / "يختلط =" noreferrer "> 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 / استدعاء.

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

وعادة ما تستخدم نسخة معدلة قليلا من <أ href على ngLink ل= "https://stackoverflow.com/questions/487661/how-do-i-suspend-painting-for-a-control-and-its-children / 487757 # 487757 "> الجواب .

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

وهناك حل لطيفة دون استخدام إمكانية التشغيل المتداخل:

وكما هو الحال دائما، ببساطة تمكين DoubleBuffered = صحيح على CustomControl الخاص بك. ثم، إذا كان لديك أي حاويات مثل 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
}

وهنا هو مزيج من لceztko وفي ng5000 لجلب نسخة ملحقات VB التي لا تستخدم pinvoke

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

وأعرف أن هذا هو السؤال القديم، أجبت، ولكن بلدي يأخذ على هذا هنا. I ريفاكتوريد تعليق التحديثات إلى 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();
    }
}

هذا هو أبسط ، وربما hacky - كما أستطيع أن أرى الكثير من GDI العضلات على هذا الموضوع, و هو من الواضح فقط مناسبا لبعض السيناريوهات. YMMV

في السيناريو الخاص بي يمكنني استخدام ما سوف أشير إلى "الأم" UserControl - وخلال Load الحدث, ببساطة إزالة التحكم-إلى-أن-التلاعب بها من الوالدين .Controls جمع و الوالدين OnPaint يعتني تماما اللوحة الطفل التحكم في أي طريقة خاصة..تماما أخذ الطفل الطلاء قدرات حاليا.

الآن أنا يد طفلي الطلاء روتين امتداد الأسلوب القائم قبالة هذا المفهوم من مايك الذهب طباعة نماذج windows.

هنا أحتاج إلى مجموعة فرعية من التسميات لتقديم عمودي إلى تخطيط:

simple diagram of your Visual Studio IDE

ثم أنا إعفاء الطفل التحكم من كونها رسمت مع هذا الرمز في ParentUserControl.Load معالج الحدث:

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

ثم في نفس ParentUserControl ، ونحن طلاء التحكم-إلى-أن-التلاعب بها من الألف إلى الياء:

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

بمجرد استضافة ParentUserControl في مكان ما, على سبيل المثالنموذج Windows - أنا وجدت أن بلدي Visual Studio 2015 يجعل شكل صحيح في وقت التصميم وكذلك وقت التشغيل:ParentUserControl hosted in a Windows Form or perhaps other user control

الآن منذ خاص التلاعب تدور الطفل السيطرة على 90 درجة ، أنا متأكد أن جميع البقع الساخنة والتفاعل تم تدميرها في تلك المنطقة - لكن المشكلة لقد تم حل كل مجموعة التسمية التي تحتاج إلى معاينة الطباعة التي عملت بشكل جيد بالنسبة لي.

إذا كان هناك طرق لإعادة البقع الساخنة و التحكم نيس إلى عمدا اليتامى التحكم - أحب أن تعلم عن ذلك يوما ما (ليس لهذا السيناريو ، بالطبع ، ولكن..فقط لتعلم).بالطبع, WPF يدعم مثل هذا الجنون OOTB..ولكن..مرحبا..WinForms هو الكثير من المرح لا يزال ، amiright?

وأو مجرد استخدام Control.SuspendLayout() وControl.ResumeLayout().

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top