كيف يمكنني تعليق لوحة التحكم و الأطفال ؟
سؤال
لدي جهاز التحكم الذي يجب أن تجعل كبيرة التعديلات.أود أن يمنع تماما من رسم حين أفعل ذلك - 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.
هنا أحتاج إلى مجموعة فرعية من التسميات لتقديم عمودي إلى تخطيط:
ثم أنا إعفاء الطفل التحكم من كونها رسمت مع هذا الرمز في 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 يجعل شكل صحيح في وقت التصميم وكذلك وقت التشغيل:
الآن منذ خاص التلاعب تدور الطفل السيطرة على 90 درجة ، أنا متأكد أن جميع البقع الساخنة والتفاعل تم تدميرها في تلك المنطقة - لكن المشكلة لقد تم حل كل مجموعة التسمية التي تحتاج إلى معاينة الطباعة التي عملت بشكل جيد بالنسبة لي.
إذا كان هناك طرق لإعادة البقع الساخنة و التحكم نيس إلى عمدا اليتامى التحكم - أحب أن تعلم عن ذلك يوما ما (ليس لهذا السيناريو ، بالطبع ، ولكن..فقط لتعلم).بالطبع, WPF يدعم مثل هذا الجنون OOTB..ولكن..مرحبا..WinForms هو الكثير من المرح لا يزال ، amiright?
وأو مجرد استخدام Control.SuspendLayout()
وControl.ResumeLayout()
.