Search code examples
c#winformsgraphicspanelgdi+

Save Panel to Bitmap c# Winforms


I have a Graphics object and a Panel. The Graphics object is instantiated with a Handler from the Panel. Then, the Panel is updated with the Paint action. Inside the Paint action, the Graphics object is used to paint. Sometimes through out the code I use the Invalidate() to update the panel.

I want to save the content of the Graphics object or the content of the Panel into a file. Whenever I try to do it, the image file is created, but blank.

Here are some snippets of the code:

I initiate Graphics object as a class variable:

Graphics GD_LD;

Then in the constructor I use the following to instantiate the object using the panel handler:

GD_LD = Graphics.FromHwnd(panelDrawLD.Handle);

Then I have the drawing function that is used on the Paint action of the Panel, were I use the Graphics object to make the all the drawings:

private void panelDrawLD_Paint(object sender, PaintEventArgs e)
{
    ..... some code ....
    //Code example
    GD_LD.FillPolygon(blackBrush, getPoints(min, sizeGP, scaleX, scaleY));
    GD_LD.FillPolygon(blackBrush, getPoints(max, sizeGP, scaleX, scaleY));
    ..... some code ....
}

The above works fine to draw in the Panel and keep it always with the drawing.

The issue is when trying to save the Panel to a Image file:

Bitmap I_LD = new Bitmap(panelDrawLD.Size.Width, panelDrawLD.Size.Height);
panelDrawLD.DrawToBitmap(I_LD, new Rectangle(0,0, panelDrawLD.Size.Width, panelDrawLD.Size.Height));
I_LD.Save(tempPath + "I_LD.bmp",ImageFormat.Bmp);

The image file is created but without content. Just blank.

I saw some threads about this subject, but I could not adapt it to my situation.

What am I doing wrong? What's a possible solution?


Solution

  • What you really should be doing, is refactoring your Paint event in to a subroutine that takes a Graphics object as an argument called target. Do all your drawing against target. Then you can call that and pass e.Graphics from panelDrawLD_Paint, and call it from your other function with a Graphics you create with Graphics.FromImage(I_LD).

    Also, if you create a Graphics (or any other GDI object), you MUST destroy it, or you will get a memory leak.

    Like this:

    private void panelDrawLD_Paint(object sender, PaintEventArgs e)
    {
        //e.Graphics does NOT need to be disposed of because *we* did not create it, it was passed to us by the control its self.
        DrawStuff(e.Graphics);
    }
    
    private void Save()
    {
        // I_LD, and g are both GDI objects that *we* created in code, and must be disposed of.  The "using" block will automatically call .Dispose() on the object when it goes out of scope.
        using (Bitmap I_LD = new Bitmap(panelDrawLD.Size.Width, panelDrawLD.Size.Height))
        {
            using (Graphics g = Graphics.FromImage(I_LD))
            {
                DrawStuff(g);
            }
            I_LD.Save(tempPath + "I_LD.bmp", ImageFormat.Bmp); 
        }   
    }
    
    
    private void DrawStuff(Graphics target)
    {
        //Code example
        target.FillPolygon(blackBrush, getPoints(min, sizeGP, scaleX, scaleY));
        target.FillPolygon(blackBrush, getPoints(max, sizeGP, scaleX, scaleY));
    }