views:

1854

answers:

5

Using C# and .Net 2.0, I'm using an irregularly shaped form (TransparencyKey, FormBorderStyle = None, etc...) and want to allow "normal" bordered mode.

I change the back colour to default from Lime I change FormBorderStyle to FixedSingle I change the TransparencyKey to Colour.None

Unfortuanately this looks a complete mess on screen with the image jumping a few pixels down and to the side and Lime green form.

I think this is caused by the form being redrawn after each line of code, is it possible to suspend drawing the form until I have made my changes and then just redraw the form once?

G

+1  A: 

Try the Form.DoubleBuffered property. Set it to 'true'.

Also, if your form has any child controls, also set DoubleBuffered to true for those as well (and children's children, etc, down the line).

Lastly, call SuspendLayout before your changes and ResumeLayout afterwards. Keep in mind this only affects placement of child controls. If you are doing any custom drawing the DoubleBuffered property will give you more bang for the buck.

mjmarsh
+3  A: 

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 = 0xHF;

    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 = 0x14;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg != WM_ERASEBKGND) // ignore WM_ERASEBKGND
        {
            base.WndProc(ref m);
        }
    }
overslacked
Could you explain in more detail what this does please? I understand the concept of overriding, but what does this code prevent/do?
G-
A: 

A way to send all the "image" of the form to the screen in one step is to enable DoubleBuffer.

In the Constructor you can set the ControlStyles

VB.NET:

SetStyle(ControlStyles.DoubleBuffer, True)
Romias
+3  A: 

You are modifying properties that have a rather large impact on a form. TransparencyKey and FormBorderStyle require changing the window style bits. Windows doesn't allow those style bits to be changed. Windows Forms implements them by completely destroying the window and re-creating it from scratch. Neat trick, but that takes time and the form will be repainted each time you change the style. Causing the unpleasant visual effect you see.

Try this: 1. Set Opacity to 0 so the form becomes invisible 2. Change BackColor, no problem 3. Change FormBorderStyle, window gets recreated 4. Change TransparencyKey, window gets recreated 5. Change Opacity to 1, window gets recreated, then visible

For example:

  this.Opacity = 0;
  this.BackColor = Color.FromKnownColor(KnownColor.Control);
  this.FormBorderStyle = FormBorderStyle.Sizable;
  this.TransparencyKey = Color.Empty;
  this.Opacity = 1;
Hans Passant
A: 

If all that fails, you could try some lowlevel hacking by blocking all paint messages to your form.

WARNING: I am not promoting the use of this method, but you can try it if you really want to. It has helped me in the past.

Win32.LockWindowUpdate(this.Handle);
try
{
   //make your changes here
}
finally
{
  //release the lock
  Win32.LockWindowUpdate((IntPtr)0);
}

This code relies on the following supporting code:

public class Win32
{
  private Win32() { }

    /// <summary>
    /// Lock ore relase the wndow for updating.
    /// </summary>
    [DllImport("user32")]
    public static extern int LockWindowUpdate(HWND hwnd);
 }