1

How can I preserve the painting I drew on a picturebox?

I draw a circle, and fill it via the ExtFloodFill API. This works fine.

When I resize the form (or minimize it) and resize it back to its original size part of the painting is gone.

CompleteResizedMissing part

When I refresh the picturebox the painting will be gone completely

I tried to repaint it in the Paint event, but this caused it to be repainted continuously as the painting itself triggered the Paint event as well.

See below for a test project.

  • When you click on the picturebox the painting will be drawn.
  • When you doubleclick the picturebox will be refreshed.

[1 form with 1 picturebox named pictureBox1]

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace FloodFill
{
    public partial class Form1 : Form
    {
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateSolidBrush(int crColor);
        [DllImport("gdi32.dll")]
        public static extern bool ExtFloodFill(IntPtr hdc, int nXStart, int nYStart, int crColor, uint fuFillType);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll")]
        public static extern int GetPixel(IntPtr hdc, int x, int y);
        public static uint FLOODFILLSURFACE = 1;

        public Form1()
        {
            InitializeComponent();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            DrawCircle();
            FillGreen();
        }

        private void DrawCircle()
        {
            Graphics graBox = Graphics.FromHwnd(pictureBox1.Handle);
            graBox.DrawEllipse(Pens.Red, 10, 10, 100, 100);
        }

        private void FillGreen()
        {
            Graphics graBox = Graphics.FromHwnd(pictureBox1.Handle);
            IntPtr ptrHdc = graBox.GetHdc();
            IntPtr ptrBrush = CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
            SelectObject(ptrHdc, ptrBrush);
            ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
            DeleteObject(ptrBrush);
            graBox.ReleaseHdc(ptrHdc);
        }

        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            pictureBox1.Refresh();
        }

    }
}

How can I preserve the painting I make when my form or the picturebox is resized or in any other way to be refreshed?

[EDIT]

I changed my Paint event to the following:

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        DrawCircle();
        FillGreen();
    }

And now the circle is being redrawn after a resized, but the FloodFill isn't

paint event

(I also gave the picturebox a lightblue background for another test)

[EDIT2]

I changed the Paint event to use the Graphics g as follows:

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        DrawCircle(g);
        FillGreen(g);
    }

    private void DrawCircle(Graphics g)
    {
        g.DrawEllipse(Pens.Red, 10, 10, 100, 100);
    }

    private void FillGreen(Graphics g)
    {
        IntPtr ptrHdc = g.GetHdc();
        IntPtr ptrBrush = CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
        SelectObject(ptrHdc, ptrBrush);
        ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
        DeleteObject(ptrBrush);
        g.ReleaseHdc(ptrHdc);
    }

But when I resize back to the original size some lines of the FloodFill are skipped, especially when I resize slowly

graphics missing lines

Hrqls
  • 2,944
  • 4
  • 34
  • 54
  • You need to either draw in or from the paint event using e.Graphics from the Paint params or into the image bitmap of the picturebox. using a Graphics created from the bitmap! Anything else does not persist as you have seen. I also fail to see why you don't use gdi+ drawing, assuming you are using winforms.. – TaW Mar 03 '15 at 11:58
  • I come from VB6 and am trying to convert my application to C# ... I edited my question to add a new Paint event I tried, and its result : the circle is now being redrawn but the floodfill isn't – Hrqls Mar 03 '15 at 12:52
  • Could you explain how to use the e.Graphics from the Paint params? And what about gdi+ drawing compared to what I do? – Hrqls Mar 03 '15 at 12:54
  • The resize `Paint` errors can be easily avoided by calling `pictureBox1.Invalidate();` in the form's `Resize` event! – TaW Mar 03 '15 at 15:13

1 Answers1

1

Using GDI+ methods for drawing the code is simply:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    Rectangle rect = new Rectangle(10, 10, 100, 100);
    e.Graphics.FillEllipse(Brushes.Green, rect);
    using (Pen pen = new Pen(Color.Red, 2f))
    {
      e.Graphics.DrawEllipse(pen , rect);
    }
}

None of your DllImport or constants is needed at all.

Note that I chose to use a Pen with a width of 2f to demonstrate the use of the using clause to correctly create and dispose of the GDI+ (non-standard) object Pen

This will persist as it is drawn whenever needed. To draw it initially you must call pictureBox1.Invalidate(); once!

If you want to change the coordinates you should move the variable rect to class level, set it to new data and the call Invalidate on the PictureBox! The same goes for Colors or the Pen.Width: use class level variables and after each change trigger the Paint event by calling Invalidate()..

Once you understand that, the most important part of learning drawing in GDI+ is done..

For filling any drawings you have three options:

  • simple shapes draw with DrawXXX methods all have a FillXXX method.
  • complex shapes can be created with a GraphicsPath, which also has both a DrawXXX methods and a FillXXX method.
  • areas not created by shapes but by drawn pixels must be filled with a floodfill method. There is non buit-in in GDI+, but can simply write your own. maybe like the Fill4 in this answer..

Update: If you feel better using the FloodFill from gdi32.dll you can do so, but please change the code to use the Graphics object from the Paint event:

   FillGreen(e.Graphics);

private void FillGreen(Graphics graBox)
{
    IntPtr ptrHdc = graBox.GetHdc();
    IntPtr ptrBrush =  CreateSolidBrush(ColorTranslator.ToWin32(Color.Green));
    SelectObject(ptrHdc, ptrBrush);
    ExtFloodFill(ptrHdc, 50, 50, GetPixel(ptrHdc, 50, 50), FLOODFILLSURFACE);
    DeleteObject(ptrBrush);
    graBox.ReleaseHdc(ptrHdc);
}

for better flexibility you can probably make the params all dynamic, but I'm not used to that old library..

Note that the order of calls matters: FillXXX wil cover some of the pixels drawn by DrawXXX, so it must come first. FloodFill however depends on the bounding lines, in your case the circle, being drawn first, so it must come after..

Community
  • 1
  • 1
TaW
  • 53,122
  • 8
  • 69
  • 111
  • thanks for the example! ... Eventually I want to draw a bitmap (png file) on the picturebox (or its Graphics), and dynamically fill certain odd-shaped regions of that bitmap ... the fillcolor will depend on data i receive from a via a tcp connection – Hrqls Mar 03 '15 at 14:36
  • for my java applet i did create my own floodfill method which works fine, but in my VB6 program i use the ExtFloodFill API ... the VB6 program is much further developped than the java applet, so i thought of converting that one ... and it was quite easy to use the ExtFloodFill API in VB6 :-) – Hrqls Mar 03 '15 at 14:39
  • The DrawImage method is used for the drawing on a Bitmap. for filling you will probably indeed need a floodfill method, like the one in the link.. – TaW Mar 03 '15 at 14:40
  • BTW: You also should decide on a basic question: You can draw __onto__ the surface or __into__ the bitmap image of the PictureBox. [See here!](http://stackoverflow.com/questions/27337825/picturebox-paintevent-with-other-method/27341797?s=5|0.7547#27341797) and [here](http://stackoverflow.com/questions/28714411/update-a-drawing-without-deleting-the-previous-one/28716887?s=1|0.0000#28716887) – TaW Mar 03 '15 at 14:45
  • See my updated answer for using the gdi32.dll call correctly! – TaW Mar 03 '15 at 14:56
  • I got the FloodFill working in the paint event on the Graphics, but it somehow skips some lines (especially when i resize slowly) ... I will update my question to show what I mean – Hrqls Mar 03 '15 at 15:05
  • Thanks for all your help and information. I learned a lot here :) – Hrqls Mar 04 '15 at 09:21