Refresh after sending a WM_SETREDRAW message to the panel

ControlHelper.SuspendDrawing(panel);
panel.Controls.Clear();
AddItemIdLabel();
AddLastEditedLabel();
AddDeleteButton();
AddSaveButton();
ControlHelper.ResumeDrawing(panel);

public static class ControlHelper
{
    [DllImport("user32.dll")]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

    private const int WM_SETREDRAW = 0xB;

    public static void SuspendDrawing(Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
    }

    public static void ResumeDrawing(Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 1, 0);
        target.Refresh();
    }
} 

If I test with the code above, parts of the panel aren't being refreshed. You can see the old controls from before the Clear() on places where no new controls have been added.

If I put the panel.Controls.Clear(); before the ControlHelper.SuspendDrawing(panel); everything works as intented but some flickering is visible which I'm trying to avoid.

So what's going on here? How can depending on whether I clear the collection of controls before or after the suspend make a difference?

Answers


Your target.Refresh() call is not fully invalidating the areas where the old controls were removed. Since Control.Refresh() is a 'virtual' method, it may be overridden and may have side side effects like this.

To guarantee complete coverage, you should use the Invalidate(true) & Update() methods within ResumeDrawing(). The Invalidate(true) method will set the entire region of the control and all child controls as invalid and the Update() method will repaint everything just once at the end.

Also, you should consider implementing those methods as .NET Extensions. That would automatically add the SuspendDrawing() and ResumeDrawing() methods to every System.Windows.Forms.Control where you have a USING for the namespace in which the Extensions are declared:

[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(this Control Target)
{
    SendMessage(Target.Handle, WM_SETREDRAW, false, 0);
}

public static void ResumeDrawing(this Control Target)
{
    SendMessage(Target.Handle, WM_SETREDRAW, true, 0);
    Target.Invalidate(true);
    Target.Update();
}

Need Your Help

Getting value from a dropdown list that was populated with AJAX

c# jquery asp.net .net ajax

I had populated an ASP.net dropdown list with AJAX now I need to get the Id to store in into the database in a C# method, (I'm using LINQ)

Display Dialog Box When User Meets MaxLength of TextBox

c# javascript asp.net

I have a asp.net C# application. I have a TextBox that has a MaxLength set of 3000. When the user reaches the maxlength of 3000 I want a JavaScript dialog box to open and alter the user of this. ...