How to Suspend Painting For a Control and Its Children

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

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

How to stop/suspend a form from painting in C#?

I figured out the answer to my question. It was as simple as sending a message to stop the painting from happening and adding it to both the the InitializeComponent() and OnPaint().

Adding it to just InitializeComponent() will paint the form, but immediately suspend it. Adding it to just onPaint seems to do nothing, so the winner was both.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication11
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SuspendDrawing(this);
}

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, Int32 wMsg,
bool wParam, Int32 lParam);

private const int WM_SETREDRAW = 11;
private const int WM_PAINT = 0xf;
private const int WM_CREATE = 0x1;

public static void SuspendDrawing(Form parent)
{
SendMessage(parent.Handle, WM_PAINT, false, 0);
}

public static void ResumeDrawing(Form parent)
{
SendMessage(parent.Handle, WM_PAINT, true, 0);
// parent.Refresh();
}

protected override void OnPaint(PaintEventArgs e)
{
SuspendDrawing((this));
}

}
}

Can I suspend redrawing of a form until I have performed all updates?

NEW answer: Override the WndProc and block the WM_PAINT message while you apply the new Window properties.

OLD answer: Override the WndProc, and block the WM_ERASEBKGND message.

Explanation of what the code below does:

When a window's region is invalidated, Windows sends a series of messages to the control that result in a freshly-painted widget. An early message in this series is WM_ERASEBKGND. Normally, in response to this message, the control paints itself a solid color. Later, in response to the WM_PAINT message (which is usually consumed by us in the OnPaint event) the actual drawing is done. If this drawing is non-trivial there will be a delay before the widget is updated and you'll get an annoying flicker.

Looking at your code again I was clearly solving a different problem. Try this new example. It will block the painting of the form/control if the bAllowPaint flag is unset.

The NEW example:

    private const int WM_PAINT = 0x000F;

protected override void WndProc(ref Message m)
{
if ((m.Msg != WM_PAINT) ||
(bAllowPaint && m.Msg == WM_PAINT))
{
base.WndProc(ref m);
}
}

The OLD example:

    private const int WM_ERASEBKGND = 0x0014;

protected override void WndProc(ref Message m)
{
if (m.Msg != WM_ERASEBKGND) // ignore WM_ERASEBKGND
{
base.WndProc(ref m);
}
}

Do I need to call SuspendLayout for every child control?

Take a look at this MSDN article - Practical Tips For Boosting The Performance Of Windows Forms Apps.

Text from article : Remember that SuspendLayout only prevents Layout
events from being performed for that particular control. If controls
are added to a panel, for example, SuspendLayout and ResumeLayout must
be called for the panel and not for the parent form.

Winforms Wait to draw Until Controls Added

The answer is the same as the answer to this question:

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

(Answer copied for convenience: originally from: https://stackoverflow.com/users/36860/ng5000)

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

Why a Panel blinks while removing it and its childs?

Turn off updates with WM_SETREDRAW, update your UI, then turn them back on and refresh the Form:

    // ... at Form level ...
private const int WM_SETREDRAW = 11;

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

// ... some method ...

SendMessage(this.Handle, WM_SETREDRAW, false, 0); // turn updates off

this.Controls.Remove(ctlAvion); // Removes the actual UserControl (red rectangle)
ctlAvion = new ControlAvion(m_avion); // Creates a new one
ctlAvion.Location = new Point(2, 13);
ctlAvion.Size = new Size(597, 475);
this.Controls.Add(ctlAvion); // Adds the new UserControl to the main controls (a Form).

SendMessage(this.Handle, WM_SETREDRAW, true, 0); // turn updates back on
this.Invalidate();
this.Refresh();


Related Topics



Leave a reply



Submit