1

I am working on a MS paint like program that is programmed entirely in c#. It's very basic, but I have stumbled upon a problem. So I saw another SO post regarding MS paint mock ups. It was about how to save the end result as a .bmp file. I tried using the solutions and answers provided and it worked.

The file saved. However when it saved, it only saved the blank panel ( im making a forms app) . I have only seen one SO post that deals with this issue but I couldn't incorporate to allow the user to interact. The following is my code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

    namespace Paint
    {
    public partial class Form1 : Form
    {
    Graphics g;
    Pen p = new Pen(Color.Black, 1);
    Point sp = new Point(0, 0);
    Point ep = new Point(0, 0);
    int k = 0;

    public Form1()
    {
        InitializeComponent();
    }

    private void pictureBox2_Click(object sender, EventArgs e)
    {
        p.Color = red.BackColor;
        default1.BackColor = red.BackColor;


    }

    private void blue_Click(object sender, EventArgs e)
    {
        p.Color = blue.BackColor;
        default1.BackColor = blue.BackColor;
    }

    private void green_Click(object sender, EventArgs e)
    {
        p.Color = green.BackColor;
        default1.BackColor = green.BackColor;
    }



    private void panel2_MouseDown(object sender, MouseEventArgs e)
    {
        sp = e.Location;
        if (e.Button == MouseButtons.Left)
            k = 1;
    }

    private void panel2_MouseUp(object sender, MouseEventArgs e)
    {
        k = 0;
    }

    private void panel2_MouseMove(object sender, MouseEventArgs e)
    {
        if (k == 1)
        {
            ep = e.Location;
            g = panel2.CreateGraphics();
            g.DrawLine(p, sp, ep);

        }
        sp = ep;
    }



    private void panel2_MouseLeave(object sender, EventArgs e)
    {
        k = 0;        }

    private void panel2_Paint(object sender, PaintEventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        SaveFileDialog dialog = new SaveFileDialog();
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            int width = Convert.ToInt32(panel2.Width);
            int height = Convert.ToInt32(panel2.Height);
            Bitmap bmp = new Bitmap(width, height);
            panel2.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));
            bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
        }
    }
}
}

So my question is... How do I Succesfully save a .bmp image in my c# forms app , as in how do i not make it save blank. Thanks in advance :)

edit

So I have tried the first answer and also im trying the ideas suggested by the individual in the comments and some how, instead of just saving a blank canvas. the application literally just saves a black image. Here is the code I ended up with. Where did I go wrong?

using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks; 
using System.Windows.Forms;

namespace Paint
{
public partial class Form1 : Form
{
    Graphics g;
    Graphics h;
    Pen p = new Pen(Color.Black, 1);
    Point sp = new Point(0, 0);
    Point ep = new Point(0, 0);
    int k = 0;
    Bitmap bmp =null;

    public Form1()
    {
        InitializeComponent();
    }

    private void pictureBox2_Click(object sender, EventArgs e)
    {
        p.Color = red.BackColor;
        default1.BackColor = red.BackColor;


    }

    private void blue_Click(object sender, EventArgs e)
    {
        p.Color = blue.BackColor;
        default1.BackColor = blue.BackColor;
    }

    private void green_Click(object sender, EventArgs e)
    {
        p.Color = green.BackColor;
        default1.BackColor = green.BackColor;
    }



    private void panel2_MouseDown(object sender, MouseEventArgs e)
    {
        sp = e.Location;
        if (e.Button == MouseButtons.Left)
            k = 1;
    }

    private void panel2_MouseUp(object sender, MouseEventArgs e)
    {
        k = 0;
    }

    private void panel2_MouseMove(object sender, MouseEventArgs e)
    {
        if (k == 1)
        {
            ep = e.Location;
            int width = Convert.ToInt32(panel2.Width);
            int height = Convert.ToInt32(panel2.Height);
            Bitmap bmp = new Bitmap(width, height);

            g = Graphics.FromImage(bmp);
            g.DrawLine(p, sp, ep);
            h = panel2.CreateGraphics();
            h.DrawLine(p, sp, ep);
        }
        sp = ep;
    }



    private void panel2_MouseLeave(object sender, EventArgs e)
    {
        k = 0;        }

    private void panel2_Paint(object sender, PaintEventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        SaveFileDialog dialog = new SaveFileDialog();
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            /*
            Bitmap bmp = Bitmap.FromHbitmap(panel2.CreateGraphics().GetHdc());
            panel2.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));*/
            int width = panel2.Width;
            int height = Convert.ToInt32(panel2.Height);
            if (bmp == null)

                bmp = new Bitmap(width, height);
            bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
        }
    }
}

}

TaW
  • 53,122
  • 8
  • 69
  • 111
Michael Ilie
  • 449
  • 2
  • 16
  • 1
    _g = panel2.CreateGraphics();_ Winforms graphics basic rule #1 : Never use `control.CreateGraphics`! Never try to cache a `Graphics` object! Either draw into a `Bitmap bmp` using a `Graphics g = Graphics.FromImage(bmp)` or in the `Paint` event of a control, using the `e.Graphics` parameter.. - You may use the code for temporary stuff but for real, persistent stuff, which will be used in the DrawToBitmap you NEED to draw in the Paint event and with its e.Graphics obejct. To do so collect each element you draw in a List.. – TaW Aug 18 '17 at 17:26
  • Ok so, how would i implement your first suggestion : `Graphics g = Graphics.FromImage(bmp)` into my code. Im new to this so im sorry if im testing your patience. ( by this i mean where in the code do i put it and what else do i need to modify) – Michael Ilie Aug 18 '17 at 17:33
  • You could simply replace `g = panel2.CreateGraphics();` by `g = Graphics.FromImage(bmp);` after creating a bitmap bmp at, say, class level. This is ok but will not allow you to implement something nice as an undo, which so nice for a paint app... So it is definitely better to go for the larger solution/rewrite. [Here](https://stackoverflow.com/search?q=user%3A3152130+drawaction) are a whole bunch of examples.. – TaW Aug 18 '17 at 17:40
  • when i replace it it says bmp does not exist in the current context. This is because I havent defined it yet so do i move that up or what do i do with the button click function – Michael Ilie Aug 18 '17 at 17:49
  • Yes, define it a class level and create it in the form load. Or before using it in a condition (`if (bmp==null) bmp= new Bitmap(, w, h);`) – TaW Aug 18 '17 at 17:51
  • So i am new to c# and i am having a hard time following you so i would greatly appreciate it should you write a full fledged answer that has a solid solution or a revised edition of the code i provided you... Thanks :) Like, i have tried your code but cannot determine what youre telling me to do. – Michael Ilie Aug 18 '17 at 19:18
  • Maybe I will tomorrow, it is getting late here.. – TaW Aug 18 '17 at 19:20
  • Just saw your update. You are always creating a new Bitmap. This can't work. Create is once and use it all the time later.. (change to : `InitializeComponent(); bmp = new Bitmap(panel1.Clientsize.Width, panel1.Clientsize.Height);` and delete all the `Bitmap bmp = new Bitmap(width, height);` lines! (My conditional creation code in my comment was meant before first using it, not right before saving it!) – TaW Aug 18 '17 at 20:31

2 Answers2

0

Here is a revised version, pretty much what I told you in the comments..:

public partial class Form2 : Form
{
    Graphics g;
    Graphics h;
    Pen p = new Pen(Color.Black, 1);
    Point sp = new Point(0, 0);
    Point ep = new Point(0, 0);
    int k = 0;
    Bitmap bmp =null;

    public Form2()
    {
        InitializeComponent();
        bmp = new Bitmap(panel2.ClientSize.Width, panel2.ClientSize.Height);
        g = Graphics.FromImage(bmp);
        g.Clear(panel2.BackColor);
    }

    private void pictureBox2_Click(object sender, EventArgs e)
    {
        p.Color = red.BackColor;
        default1.BackColor = red.BackColor;
     }

    private void color_Click(object sender, EventArgs e)
    {
        Control ctl = sender as Control;
        p.Color = ctl.BackColor;
        default1.BackColor = ctl.BackColor;
    }

    private void panel2_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            ep = e.Location;
            g.DrawLine(p, sp, ep);
            h = panel2.CreateGraphics();
            h.DrawLine(p, sp, ep);
        }
        sp = ep;
    }

    private void panel2_MouseDown(object sender, MouseEventArgs e)
    {
        sp = e.Location;
    }


    private void button1_Click(object sender, EventArgs e)
    {
        SaveFileDialog dialog = new SaveFileDialog();
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            bmp.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
        }
    }
}

A few notes:

  • I mapped all the clicks of the palette control into one.
  • I have eliminated the flag k by doing the button test in the move.
  • I have kept the cached Graphics g; this is usually not recommended, but as we keep drawing into one and the same bitmap is is ok.
  • I have remove all duplicate declaration of the bitmap bmp.
  • I don't know what the picturebox does, so I didn't touch the code.

Drawbacks of the soution:

  • Since you don''t keep track of all the moves you can't do a good undo.
  • Since all lines are drawn separately you can't get good results with broader and/or semi-transparent Pens because the overlapping endpoints will look bad.

For a better solution of simple doodeling see here and after you have studied it you can tackle the even better solution, which will allow you to use all sort of drawing tools here..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • a quick question (ps thanks for your answer): This now saves to a black background. How do i make it save to a white backgorund? – Michael Ilie Aug 19 '17 at 18:34
  • `g.Clear(panel2.BackColor);` Actually it saves to the Panel's Backcolor. Note that panels by default are transparent and also that many viewers, e.g. Irfanview are not able to display transparency. (IrfanView displays it as black!) So maybe what you see is in fact transparent?? You can either change the Panel to be white or simply clear to Color.White.. – TaW Aug 19 '17 at 18:38
-1

Use Graphics.GetHdc Method and save it like this:

Bitmap bitMap = Bitmap.FromHbitmap(g.GetHdc());
bitMap.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
Ali Adlavaran
  • 3,697
  • 2
  • 23
  • 47
  • i have rewritten my function :` if (dialog.ShowDialog() == DialogResult.OK) { int width = Convert.ToInt32(panel2.Width); int height = Convert.ToInt32(panel2.Height); Bitmap bitMap = Bitmap.FromHbitmap(g.GetHdc()); bitMap.Save(dialog.FileName, System.Drawing.Imaging.ImageFormat.Bmp); }` – Michael Ilie Aug 18 '17 at 16:32
  • and a 'System.InvalidOperationException' occurred in System.Drawing.dll error. it says that the object (which is g) is being used elsewhere – Michael Ilie Aug 18 '17 at 16:39
  • Try it with this : `Bitmap bitMap = Bitmap.FromHbitmap(panel2.CreateGraphics());` – Ali Adlavaran Aug 18 '17 at 17:00
  • it gives me the error that it cannot convert System.drawing.graphics to system.IntPtr – Michael Ilie Aug 18 '17 at 17:15
  • Oh sorry it was typo. try with `Bitmap bitMap = Bitmap.FromHbitmap(panel2.CreateGraphics().GetHdc())` – Ali Adlavaran Aug 18 '17 at 17:22