-1

I have tried to get rid of the flickering effect when updating the graphical content of a panel in my application. Simplified, the Mainform.cs looks like this:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsAppRefreshTest
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            var g = panel1.CreateGraphics();
            g.FillRectangle(Brushes.Red, 20, 20, panel1.Width - 40, panel1.Height - 40);
            if(panel1.Width>100 && panel1.Height>100) g.FillRectangle(Brushes.Blue, 40, 40, panel1.Width - 80, panel1.Height - 80);
        }

        private void panel1_SizeChanged(object sender, EventArgs e)
        {
            panel1.Refresh();
        }
    }
}

If I set a breakpoint on the first row of panel1_Paint the panel will be cleared to the BackColor as the event is called. This is not what I want - I want the panel to be painted only by the panel1_Paint method, since I draw the whole area every time with a bitmap that has been prepared in my real application.

How do I get Windows Forms NOT to clear the panel with BackColor before the code can paint it?

Dharman
  • 30,962
  • 25
  • 85
  • 135
user3505708
  • 64
  • 2
  • 12
  • 4
    You will have to subclass the `Panel` class and override the `OnPaintBackground()` method. – Mark Benningfield Sep 04 '21 at 15:15
  • 3
    First thing, remove `var g = panel1.CreateGraphics();` from the Paint event (of any Control). The Graphics object is provided by PaintEventArgs, `e.Graphics`. Then, ditch the Panel and use a double buffered Control as *canvas* as a PictureBox, a flat Label or a Custom Control derived from Control that enables double buffering. You can also set `ControlStyles.AllPaintingInWmPaint` in the constructor. Note that you can remove the Background entirely. – Jimi Sep 04 '21 at 15:22
  • 3
    Remove that call to `Refresh()` in `SizeChanged`, too. Use `ControlStyles.ResizeRedraw` or set the `ResizeRedraw` property of your Custom Control. – Jimi Sep 04 '21 at 15:28
  • Thanks for your answers. Jimi: The reason why I use panel1.CreateGraphics instead of e.Graphics is that I want to redraw the full area of panel1, not just the invalidated part. e.Graphics refers to the invalidated part from what I understand. I will also try out the PictureBox instead of Panel in any future designs. Mark: Yes, overriding the OnPaintBackground() was what I needed to do. I created my own class DoublebufferedPanel and inherited from Panel, then changed from new Systems.Windows.Form.Panel to new DoublebufferedPanel in the generated code in MainForm.Designer.cs. Thanks! – user3505708 Sep 05 '21 at 00:27
  • __If__ need be you can always Invalidate the whole Panel. CreateGraphics will always return a short-lived, ie potentially invalid object. To avoid flicker double-buffer the Panel. Or use a Picturebox or even a Label! - Btw: __You__ always draw the whole control anyway; it may just be that less pixels get copied to the screen by the system, which really ought never be of interest for you.. – TaW Sep 05 '21 at 09:46
  • That's not how it works. PaintEventArgs ALSO references the clipping rectangle, but your Graphics object is instead related to the HDC of the Control. Hence, that's all you need. -- If you **don't** want to paint the Background, use `SetStyle()` to set `ControlStyles.Opaque`: this will remove the background entirely, OnPaintBackground is not called (hence the related event is not raised). Now you can paint your own background - even semi-transparent - or no background at all. If you want your control to be transparent, override CreateParams to add `WS_EX_TRANSPARENT` to the extended styles. – Jimi Sep 05 '21 at 18:11
  • Overriding OnPaintBackground to not call `base` is not a real solution, since the underlying Control performs that job anyway, you just discard what it has already been created. ++ Don't double buffer a Panel. Either derive from PictureBox or Label or Control. Enable double-buffering on the latter. A double.-buffered Panel **is not the same thing**: it has a form of persistence of the Graphics. It doesn't have the same effect. Try this: [Transparent Overlapping Circular Progress Bars (Custom Control)](https://stackoverflow.com/a/53379442/7444103) with a standard PictureBox. – Jimi Sep 05 '21 at 18:19

1 Answers1

0

Using Panel, I was able to solve this issue using the suggestion from Mark, creating a subclass to Panel looking like this:

public class NoBackgroundPanel : Panel
{
    public NoBackgroundPanel() { }

    protected override void OnPaintBackground(PaintEventArgs e) { }
}

and using this instead of System.Windows.Forms.Panel in MainForm.Designer.cs - InitializeComponent(): this.panel1 = new NoBackgroundPanel();

This was for me the desired solution since I am manually updating a Bitmap used as a double buffer in my real application, then drawing that to the full area of the panel when it has to be redrawn.

Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
user3505708
  • 64
  • 2
  • 12