2

Scenario

Having a Windows Forms Form-derived form that contains a Panel-derived control:

enter image description here

The form gets a black background color set:

public MyForm()
{
    InitializeComponent();

    base.BackColor = Color.Black;
}

And the panel control is configured to be double buffered and the like, as described here, here and here:

public MyPanel()
{
    base.DoubleBuffered = true;

    SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    SetStyle(ControlStyles.ResizeRedraw, true);
    SetStyle(ControlStyles.UserPaint, true);
    SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

    UpdateStyles();
}

The actual drawing is done inside these overrides in MyPanel:

protected override void OnPaintBackground(PaintEventArgs e)
{
    e.Graphics.Clear(Color.Black);
}

protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.Clear(Color.Black);
}

Wrong behaviour

Every now and then, when the form is initially shown, my owner drawn panel is shortly drawn as white, before my own drawing code is actually called:

enter image description here

After my drawing code is called, everything is drawn correctly and I never can reproduce the white background again:

enter image description here

Even resizing the window and panel does not make it flicker in white.

Enforcing the wrong behavior

I can enforce the initial white-drawn of my panel, if I put a sleep in the Shown event handler of my form:

private void MyForm_Shown(object sender, EventArgs e)
{
    Thread.Sleep(1000);
}

The panel is shown in white for 1000 ms just before my owner-drawn paint code is being called.

My question

How can I avoid the initial white displaying when having an owner drawn/custom drawn panel?

My goal is to have the control "know" its initial background color (black in my example) right from the start, not after it is initially shown.

Some thoughts

I've tried to play with all kind of things including the CreateParams property, but with no visible success.

My initial idea was to provide some initial background color through the WNDCLASSEX structure, but after digging through the Reference Source, I still have no clue whether this is possible and would help.

Whole code

Just to be safe, following is my whole code.

MyForm.cs:

public partial class MyForm : Form
{
    public MyForm()
    {
        InitializeComponent();

        base.BackColor = Color.Black;
    }

    private void MyForm_Shown(object sender, EventArgs e)
    {
        Thread.Sleep(1000);
    }
}

MyPanel.cs:

public class MyPanel : Panel
{
    public MyPanel()
    {
        base.DoubleBuffered = true;

        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

        UpdateStyles();
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
        e.Graphics.Clear(Color.Black);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.Clear(Color.Black);
    }
}
Uwe Keim
  • 38,279
  • 56
  • 171
  • 280
  • 1
    the problem is with the form's background, not the panel's. the form will have a clip region where the panel goes, until it learns that the panel will all-WM-paint itself, thus not painting that black initially. this is done in windows forms framework code. could you apply the same styles to the panel's parent form? – Cee McSharpface Dec 02 '18 at 16:54
  • Thanks,@dlatikay, I've just tried that. Now [the _whole_ form is initially white](https://i.imgur.com/acSGUb2.png), before switching to black after the 1000 ms. Any chance to tell the form the initial color black? – Uwe Keim Dec 02 '18 at 16:58
  • 1
    The only way to reproduce this is to keep the parent Form message queue busy. Inserting `Thread.Sleep(1000);` in the `Shown` event will prevent the immediate painting of any control on the Form, leaving their clipping region white-ish for the time it sleeps. You can prevent it, for example, setting your Panel's visibility to `false` and re-setting to `true` as the last line in the `Shown` event. – Jimi Dec 02 '18 at 17:02
  • Thanks, @Jimi, is the white a hard-coded color value of WinForms or Windows itself? – Uwe Keim Dec 02 '18 at 17:05
  • 1
    I'm not really sure why it *looks* white-ish. Maybe it's just the result of the absence of a color in the internal container or the action of the VisualStyleRenderer. `Form.OnPaint` calls `base.OnPaint()`, where base is `ContainerControl`, which calls `ScrollableControl`, which calls `Control` and that's not a public object. Maybe Hans knows what's behind it. – Jimi Dec 02 '18 at 17:29
  • 1
    the [source code](https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,b27f0a18a7b66617,references) comment "if you do not want the default Windows behavior you must set `event.Handled` to true" is interesting, but obviously too late. another promising approach would be to follow the documentation for a 100% transparent form which avoids most of the background drawing, also the white-by-default if you're lucky (since transparency does not mean that you can't ownerdraw an opaque rectangle in onpaintbackground) – Cee McSharpface Dec 02 '18 at 17:34
  • 1
    If you set `AllPaintingInWmPaint` to `false` it will start black. I'm not sure whether that scuppers your double-buffering goals, but you could experiment with it, perhaps ignoring the WM_ERASEBKGND (except the first one) explicitly yourself in an overridden `WndProc()` instead. Otherwise I suspect registering a new class is the way to go, to override the default background colour which is otherwise "Control". As you point-out this is doable but quite a pain (https://stackoverflow.com/questions/128561/registering-a-custom-win32-window-class-from-c-sharp). – sellotape Dec 02 '18 at 17:45
  • 1
    @sellotape This could be a good idea. You could keep `this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);` in the Panel's constructor and disable the Form's `DoubleBuffering`. This should allow to paint both Form and Panel with the same color even when the message pump is busy. – Jimi Dec 02 '18 at 18:07
  • 1
    If a delay in the message pump processing a painting of the panel is the issue, possibly calling its Refresh method in the form's shown event would be sufficient. If not, a possible messy hack would be to hook the ParentChanged event to determine the containing form, set Bounds to zero and use FormShown to reset the Bounds. – TnTinMn Dec 03 '18 at 15:52
  • 1
    Maybe @HansPassant can give a hint on how to configure a core window (class) that its "default" color/brush is always black and not white? – Uwe Keim Jan 21 '19 at 11:56

0 Answers0