2

I made a paint program, and the drawing content (from System.Drawing) is drawn on the panel. I attempted this method to do a simple save for now, and I only get a blank image.

My bitmap has its property .RawData to 0. Don't know if that matters.

When I hide the screen, and show it again, the panel becomes blank.

On a side note, when I call the panel's pnlPaint.Refresh(), the panel goes blank. The drawing is lost. Is this a double buffer thing, like it's not retaining the values?

   private bool Save()
    {
        Bitmap bmpDrawing; 
        Rectangle rectBounds;

        try
        { 
            // Create bitmap for paint storage
            bmpDrawing = new Bitmap(pnlPaint.Width, pnlPaint.Height);

            // Set the bounds of the bitmap
            rectBounds = new Rectangle(0, 0, bmpDrawing.Width, bmpDrawing.Height);

            // Move drawing to bitmap
            pnlPaint.DrawToBitmap(bmpDrawing, rectBounds);

            // Save the bitmap to file
            bmpDrawing.Save("a.bmp", ImageFormat.Bmp);
        }
        catch (Exception e)
        {
            MessageBox.Show("Error on saving. Message: " + e.Message);
        }

        return true;
    }
Phil
  • 607
  • 1
  • 8
  • 22
  • Implementation questions belong on Stack Overflow. – Robert Harvey Nov 14 '14 at 17:38
  • 4
    The problem is located in code you did not post. The way you draw the panel is wrong. Use its Paint event, do not use CreateGraphics(). – Hans Passant Nov 14 '14 at 18:05
  • Even so, I'm drawing with the left mouse button down, which would need to grab the device context while moving.. so I figured this was best done by handing it within the mouse down event. I'm currently unsuccessful with using the panel's paint event in addition. – Phil Nov 14 '14 at 18:54
  • __No.__ After a `MouseDown` has started the drawing the `MouseMove` can __collect__ points but the drawing __must__ happen in or be be triggered from the `Paint` event or else the `DrawToBitmap` call leads to nothing. – TaW Nov 14 '14 at 18:58
  • Okay. I have the points collected. How to I call the paint event after the mouse moved? pnlPaint.Refresh()/Invalidate() keeps the lines for a split second until the panel goes blank again. – Phil Nov 14 '14 at 19:20
  • You call it, whenever neceessary, by a `pnlPaint.Invalidate();` If the panel goes blank after that you have code somewhere that does just that. You should have __no__ drawing outside or in parallel to the `Paint` event. You should nowhere at all have things like `pnlPaint.CreateGraphics()`..! If you can't find the reason, please append the Paint code to your question and we'll see..! – TaW Nov 14 '14 at 20:35
  • @TaW, All my drawing is currently in the paint event. When I draw, I can see some lines, but they disappear very quickly, then back to the white panel. Is there a way to retain the previously drawn? In the Win32 API, I used a windows style of SaveBits, kind of similar to what I need here. Think of drawing on MS Paint and the previous drawn stuff stays there. – Phil Nov 14 '14 at 20:43
  • Have a look at my example code! If you want to, do show me the code in your paint event! – TaW Nov 14 '14 at 21:29
  • _When I draw, I can see some lines, but they disappear very quickly, then back to the white panel._ Sounds like you clear those points? You must not do that; they are needed each time the paint is called from either you or the window manager.. – TaW Nov 14 '14 at 22:53

3 Answers3

2

This is a minimal doodle program which lets you draw persistent lines:

List<Point> curPoints = new List<Point>();
List<List<Point>> allPoints = new List<List<Point>>();

private void pnlPaint_MouseDown(object sender, MouseEventArgs e)
{
    if (curPoints.Count > 1)
    {
        // begin fresh line or curve
        curPoints.Clear();
        // startpoint
        curPoints.Add(e.Location);
    }
}

private void pnlPaint_MouseUp(object sender, MouseEventArgs e)
{
    if (curPoints.Count > 1)
    {
        // ToList creates a copy
        allPoints.Add(curPoints.ToList());
        curPoints.Clear();
    }
}

private void pnlPaint_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left) return;
    // here we should check if the distance is more than a minimum!
    curPoints.Add(e.Location);
    // let it show
    pnlPaint.Invalidate();
}

private void pnlPaint_Paint(object sender, PaintEventArgs e)
{
    // here you can use DrawLines or DrawCurve
    // current line
    if (curPoints.Count > 1) e.Graphics.DrawCurve(Pens.Red, curPoints.ToArray());
    // other lines or curves
    foreach (List<Point> points in allPoints)
        if (points.Count > 1) e.Graphics.DrawCurve(Pens.Red, points.ToArray());
}

private void btn_undo_Click(object sender, EventArgs e)
{
    if (allPoints.Count > 1)
    {
        allPoints.RemoveAt(allPoints.Count - 1);
        pnlPaint.Invalidate();
    }
}

private void btn_save_Click(object sender, EventArgs e)
{
    string fileName = @"d:\test.bmp";
    Bitmap bmp = new Bitmap(pnlPaint.ClientSize.Width, pnlPaint.ClientSize.Width);
    pnlPaint.DrawToBitmap(bmp, pnlPaint.ClientRectangle);
    bmp.Save(fileName, ImageFormat.Bmp);
}

Add your save code and if you have problems just say so..

Update: I have added two code pieces which do a save and an (unlimited) undo..

Note 1: Make sure to use a DoubleBiffered Control: Either a PictureBox or a Label or maybe a Panel with DoubleBuffered on.

Note 2: This doesn't support single clicks to create Points. As there is no DrawPoint anyway one would have to workaround if needed:

  • Either add an extra point 1 pixel away when upon MouseUp only one points is in the current curve;
  • or allow single paoints and add a FilleCircle to display them in the Paint event.
TaW
  • 53,122
  • 8
  • 69
  • 111
  • For an implementation, head over https://screentogif.codeplex.com/SourceControl/latest `GifRecorder/Controls/FreeDrawPanel.cs` – Nicke Manarin Nov 14 '14 at 22:29
  • 1
    There are ca. 2.45 trillion ready made solutions out there. Learning by studying a minimal solution is the better way, imo. – TaW Nov 14 '14 at 22:39
  • `a minimal solution` >> It's the main key of that code. ;) – Nicke Manarin Nov 14 '14 at 23:18
  • I feel this is the best solution to the problem, though it limits to drawing lines. Drawing images, circles, etc. leads to more serious problems with this. But based on my question alone, this fits. I'll want to look for easier controls though that can be easier for all painting problems. I guess what I'm trying to do is similar to MS Paint with throwing images on, drawing lines, pixels, shapes, and sell it for $999. I like this for the problem mentioned above. Thanks. – Phil Nov 14 '14 at 23:38
  • This is just what it is but you can use it as a base. Learn and expand on it! To move on to more powerful drawing actions you need to create a class `DrawingAction` and replace the `List` with `List`.. The properties of a `DrawingAction` will include: Type, Color, Pen&Brush, Text etc etc. Just take it one step at a time..! (Been there done that..) – TaW Nov 15 '14 at 01:07
  • - No other Control is necessary although it may well be a good idea to replace the `Panel` with a `PictureBox`. But __not__ for easier saving! But there you can have a nice image for a background paper and still draw on its surface with the __very same code__.. – TaW Nov 15 '14 at 01:10
0

Head over this code ScreenToGif on GitHub.

There is an implementation in the folder GifRecorder\Controls\FreeDrawPanel.cs, it suports square and round brushes, eraser and saving the output image.

Nicke Manarin
  • 3,026
  • 4
  • 37
  • 79
-1

I would skip using the panel it's not designed for graphics as much as the ImageBox is - move on to that and then you can save the contents easily.

UPDATE PictureBox. I haven't used WinForms for a while :D

Dave Gordon
  • 1,815
  • 4
  • 30
  • 52
  • No such thin as an Imagebox in Winforms. – TaW Nov 14 '14 at 20:51
  • A PictureBox is a powerful thing with zoom and triple layered display etc, but at the moment the problem is basic drawing, which is pretty similar there, not the slightly simpler saving.. – TaW Nov 14 '14 at 21:38
  • The question was about "saving" drawing and blanking the panel was a side-note as stated by the OP. – Dave Gordon Nov 14 '14 at 22:57
  • Well, on the surface, yes. But the cause of the problem was the wrong way of drawing. The saving code was/is correct.. – TaW Nov 14 '14 at 23:07