c# - How do I suspend painting for a control and its children?

ID : 20394

viewed : 16

Tags : c#.netwinformspaintc#

Top 5 Answer for c# - How do I suspend painting for a control and its children?

vote vote

93

At my previous job, we struggled with getting our rich UI app to paint instantly and smoothly. We were using standard .Net controls, custom controls and devexpress controls.

After a lot of googling and reflector usage, I came across the WM_SETREDRAW win32 message. This really stops controls drawing whilst you update them and can be applied, IIRC to the parent/containing panel.

This is a very very simple class demonstrating how to use this message:

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

There are fuller discussions on this - google for C# and WM_SETREDRAW, e.g.

C# Jitter

Suspending Layouts

And to whom it may concern, this is a similar example 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, 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 
vote vote

81

The following is the same solution of ng5000 but doesn't use 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();     } } 
vote vote

80

I usually use a little modified version of ngLink's 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();         }     } } 

This allows suspend/resume calls to be nested. You must make sure to match each SuspendDrawing with a ResumeDrawing. Hence, it wouldn't probably be a good idea to make them public.

vote vote

62

To help with not forgetting to reenable drawing:

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

usage:

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

50

Based on ng5000's answer, I like using this extension:

        #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 

Use:

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

Top 3 video Explaining c# - How do I suspend painting for a control and its children?

Related QUESTION?