0

Currently working on a rewrite of older code where I'm using Graphics functions to draw an interactive vector-based map based off location data.

Back when this was using .NET 4.7, I could simply draw my stuff into a Bitmap, do Graphics gr = Panel.CreateGraphics(), then gr.DrawImage(). I never enabled double buffering, and it always worked fine.

Now I'm in .NET 5, still with WinForms, and trying to do things a bit better based off a Paint event and invalidating the component whenever it's interacted with.

This is the core of my problem here

Bitmap Buffer; //this is initialized elsewhere, don't worry

public void MapPlotPanel_Paint(object sender, PaintEventArgs e)
{
//... Drawing code removed, it's just a novel Graphics object drawing into the Bitmap ...
e.Graphics.DrawImage(Buffer, Point.Empty);
}

While this draws the image into the panel fine, I get terrible flickering, as I can see the bitmap being drawn into the panel in real time. Even if I Panel.CreateGraphics() inside the event handler and use that instead of the PaintEventArgs object, exactly as I did previously, the same thing happens.

Within a Form load event handler, I have

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

Which has no effect.

The darnedest thing, though, is if instead of applying the image the panel using Graphics, I do Panel.BackgroundImage = Buffer, it'll never flicker, but then of course, what's drawn only shows the next time the component is invalidated.

I seriously can't think what I might be missing here, any help would be appreciated.

Ampera
  • 440
  • 4
  • 13
  • 4
    Don't use `Control.CreateGraphics()` anywhere in your code. Then, use a double-buffered Control as a PictureBox or a flat Label. Not clear why you have this: `MapPlotGraphics.DrawImage(Buffer, Point.Empty);` in the Paint event of a Control. Shouldn't you draw the Image onto the Control's DC? What is this method? Why don't you have `e.Graphics.DrawImage(Buffer, ...)` there? -- Remove all those `SetStyle()` stuff from the Form's constructor (`Load` event?). – Jimi Jan 15 '21 at 14:55
  • Ah, my bad, that was an experiment I forgot to change back.. I am /not/ using Control.CreateGraphics() anywhere in my code, I used to, now I am reimplenting it. I can try a PictureBox real quick, I've been using a Panel, so maybe that's for some reason the problem. – Ampera Jan 15 '21 at 14:57
  • 1
    You need to do this `SetStyle(ControlStyles.OptimizedDoubleBuffer, true);` for the drawing canvas not the Form. The Panel in your case. Subclass and enable that or just use a PictureBox, double-buffered by default. – dr.null Jan 15 '21 at 15:00
  • 1
    Panels are not double-buffered. You use these for painting, using the `OptimizedDoubleBuffer` feature, to get some *special effects* (a *mild persistence* of the Graphics). You can see one here: [Transparent Overlapping Circular Progress Bars (Custom Control)](https://stackoverflow.com/a/53379442/7444103): you won't get the same result with a Control that uses the standard BufferedGraphics. -- Post the real code. (BTW, `SetStyle()` is called from the **Constructor** of a Control, not the `Load` event) – Jimi Jan 15 '21 at 15:02
  • Ah, that makes perfect sense, and is exactly the problem. Many thanks, if anyone wants to write a quick answer, I'll accept the first one I see. Appreciate everyone's quick help. – Ampera Jan 15 '21 at 15:04
  • 1
    For drawing I recommend either PBox or (!) Label. Both are double-buffered out of the box. Label is the simpler control, PBox has Imge and BackgroundImage in addition to its surface. Panel is a container and not really good for drawing on.. – TaW Jan 15 '21 at 15:50

1 Answers1

0

As already mentioned in the comments - to enable Double Buffering of the panel you need to SetStyle() within the panel constructor, not in the form load event. To do so, you have to create your own panel. Bellow the sample code of custom panel class.

public partial class CustomPanel : UserControl
{
    public CustomPanel()
    {
        InitializeComponent();

        // Add Double Buffering
        SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
}
Creek Drop
  • 464
  • 3
  • 13