1

I'm very new(read 3 weeks exp) to C#(programming in general),started with html/css and javascript and now on my way with C#.

I'm trying to make my own simple 'Paint' application in windows form. But i've encountered an issue and just cant wrap my head around it, doesnt matter how much i read or follow other mans code, i'm stuck. The following code works fine but when resizing the application window the drawing dissappears.

As a solution ive read that declaring the Graphics method within the panel1_Paint event this should be resolved And here is my issue. See last code sample, ive come up with this(yes like i said, im new to this)and its not drawing anything.

ive simply tried to recreate the first example under the panel1_Paint event but i guess something went wrong during the mouseMove event and i cant figure out what it is.

Could someone explain to me what i am missing here, that would be very appreciated. thanks in advance.

[Old code]

namespace Painter
{
public partial class Form1 : Form
{
    Graphics graphics;
    Pen pen = new Pen(Color.Black, 1);
    Point startingPoint = new Point(0, 0);
    Point endPoint = new Point(0, 0);
    bool mousePaint = false;



    public Form1()
    {
        InitializeComponent();
        this.DoubleBuffered = true;

    }

    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        startingPoint = e.Location;
        if (e.Button == MouseButtons.Left)
        {
            mousePaint = true;
        }
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if(mousePaint == true)
        {
            endPoint = e.Location;
            graphics = panel1.CreateGraphics();
            graphics.DrawLine(pen, startingPoint, endPoint);
        }
        startingPoint = endPoint;
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        mousePaint = false;
    }


  }
}

[New Code]

namespace Painter
{
public partial class Form1 : Form
{
    Pen pen = new Pen(Color.Black, 1);
    Point startingPoint = new Point(0, 0);
    Point endPoint = new Point(0, 0);
    bool mousePaint = false;



    public Form1()
    {
        InitializeComponent();
        this.DoubleBuffered = true;

    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        Graphics graphics = panel1.CreateGraphics();

        if (mousePaint == true)
        {
            graphics.DrawLine(pen, startingPoint, endPoint);
        }
    }


    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        startingPoint = e.Location;
        if (e.Button == MouseButtons.Left)
        {
            mousePaint = true;
        }
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if(mousePaint == true)
        {
            endPoint = e.Location;
        }
        startingPoint = endPoint;
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        mousePaint = false;
    }


   }
 }
  • 1
    `private void panel1_Paint(object sender, PaintEventArgs e) { Graphics graphics = panel1.CreateGraphics();` This is nonsense! Always and only use the `e.Graphics` object from the `Paint` param!! Also: To trigger the `Paint` event do a `panel1.Invalidate` wheneve your drawing data have changed! Also: Make sur you understand just what your `mousePaint` flag is supposed to control: the mouse painting or the regular painting!? – TaW Aug 14 '16 at 17:16

2 Answers2

1

The following code works fine but when resizing the application window the drawing dissappears.

This happens because resizing the application window invalidates portion of your panel which causes the portion to be redrawn.

Reason why your second approach is not working (the one labelled as [NEW CODE]) is because the Paint event is called only when relevant component is redrawn. You could partially solve this by forcing redraw of the panel in your MouseDown/MouseMove event handlers but you would still lose your previously painted stuff.

Possible solution is to create instance of Bitmap and paint there. Then just set this Bitmap as BackgroundImage of the panel. You can find more information on that here. Of course you would need to think about stuff like resizing and what should happen to the bitmap if application window gets shrunk or enlarged.

Here is some code that I quickly put together to get you started:

namespace WinForms_PaintTest
{
    public partial class Form1 : Form
    {
        private Pen pen;
        private Bitmap bitmap;

        public Form1()
        {
            InitializeComponent();
            this.pen = new Pen(Color.Black, 1);
            this.bitmap = new Bitmap(this.panel1.Width, this.panel1.Height);
            this.panel1.BackgroundImage = this.bitmap;
        }

        private void panel1_MouseMove(Object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                using (Graphics g = Graphics.FromImage(this.bitmap))
                {
                    g.DrawRectangle(this.pen, e.Location.X, e.Location.Y, 1, 1);
                }
                this.panel1.Refresh();
            }
        }

        private void Form1_FormClosed(Object sender, FormClosedEventArgs e)
        {
            this.pen.Dispose();
            this.bitmap.Dispose();
        }
    }
}

Also regarding this:

this.DoubleBuffered = true;

I believe your intention was to prevent the flickering when relevant control is being redrawn? If that is case you need to set this property against the panel and not against the form itself. It is little bit tricky though because DoubleBuffered property of the panel is protected so you will need to either inherit from the panel or resort to reflection. You can find more information here .

Community
  • 1
  • 1
Markkknk
  • 406
  • 6
  • 13
1
private void panel1_Paint(object sender, PaintEventArgs e)
{
    Graphics graphics = panel1.CreateGraphics();

This is nonsense! Always and only use the e.Graphics object from the Paint param!!

Also: To trigger the Paint event do a panel1.Invalidate(); whenever your drawing data have changed!

Also: Make sure you understand just what your mousePaint flag is supposed to control: the mouse painting (i.e. adding new shapes to draw) or the regular painting (i. all shape previously drawn)!? Note that all drawing, current and previous needs to be done from the Paint event, whenever necessary i.e. over and over again!

To be able to do so: Collect all the shpes' data in a List<T>..

To Doublebuffer a Panel you need to subclass it. Your code turns on DoubleBuffering for the Form, which fine but won't help the Panel..

Instead simply use a PictureBox, which is control meant for drawing on!

A DoubleBuffered Panel subclass is as simple as this:

class DrawPanel : Panel 
{
   public DrawPanel()
    {
        DoubleBuffered = true;
    }
}

Update: Instead you can also use a Label (with Autosize=false); it also has the DoubleBuffered property turned on out of the box and supports drawing better than Panels do.

TaW
  • 53,122
  • 8
  • 69
  • 111
  • Thank you for this, ill have a go at it today. – Alex Turner Aug 15 '16 at 09:07
  • Good luck! Keep the winforms __Graphics Matra__ in mind: _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.._ – TaW Aug 15 '16 at 09:51
  • Hello Taw, i had one more question, im having a hard time making the e.graphics.drawline under the paint event follow my mousemove event. How would i go about doing that? Drawing a line with fixed coordiantes is fine but having e.graphics under the paint event follow my mouse movement seems impossible to me atm. – Alex Turner Aug 15 '16 at 13:22
  • The trick is to code the mousemove to store the current position as the current endpoint and the mouse down as the startpoint. Both points should be stored at class level and after storing it the mousemove must call panl.Invalidate() to trigger the painting. More complex drawnig will need maybe a Listy for the current drawing and a List> for the previous drawings. All depending on what you want to draw.. – TaW Aug 15 '16 at 13:31
  • See [this post](http://stackoverflow.com/questions/38414334/how-to-draw-an-updating-line/38419518?s=11|0.3356#38419518) for an example of an updating line and [this one](http://stackoverflow.com/questions/31988079/copying-free-hand-drawing-from-panel-in-visual-studio-2013/32112426?s=4|0.4099#32112426) for a discussion plus example of free-hand drawing, which you can easily adapt to drawing lines or other shapes.. – TaW Aug 15 '16 at 13:36
  • Sorry to bother you again, this has been keeping me busy(which isnt a bad thing, im enjoying it atm) ive been able to draw a free-hand line following you instructions [here](http://stackoverflow.com/questions/31988079/copying-free-hand-drawing-from-panel-in-visual-studio-2013/32112426#32112426) and you were talking about easily adapting to draw other shapes. If id want to draw rectangles, how would i go and use the list there as coordinate for the rectangle? thanks again – Alex Turner Aug 15 '16 at 19:29
  • Yes, it should be fun! To adapt for more shapes I suggest taking one step back and look at where you want to go tomorrow: Shapes and other Tools, Freehand Lines Polygons, Text etcetc. And what about different Colors? Pens (width, dashstyles) etcetc.. So what you will want and should plan for is a class that can do all that, ie has enough fields and while you're at it also helpful functions to help you. Let's think of the class as a 'DrawAction' class. Look at [these posts](http://stackoverflow.com/search?q=user%3A3152130+drawaction) for discussions of such a class and how to use it.. – TaW Aug 15 '16 at 19:48
  • To tag along on this, i would very much like to add the ability to draw rectangles, the way you showed the free-hand lines. Ive read the 'drawactions' class discussions but seems abit much for me at this stage. Im able to draw rectangles with the mouse but its not saving the drawings, everytime i draw a new rectangle the old one get deleted. is there any way i can show you? help me out tweaking it? thanks again. – Alex Turner Aug 16 '16 at 14:09
  • The question is: Do you want to store them in a separate list? If yes, then is is easier, but eventually you will miss the optin of controlling the order of the drwn shapes. If no you need to create a (simple) class that has a field for the type of shape and a List for the point data. The simpler solution with a separate List can store a List.. – TaW Aug 16 '16 at 14:19
  • .. Here the challenge is to write different code for the mouse events: Freehand creates many points during mouse move, all of which a collected. When drawing a Line or Rectangle you only store one start on MouseDown and one during MouseMove, not collecting it. So you do need a tool type field in your form you can query in the mouse events to decide what needs to be done. Maybe a radiobuttun group to control it. Write function that converts two point to a rectangle using Math.Abs and call it in the paint while drawing the rectanlge. – TaW Aug 16 '16 at 14:23
  • the answer is yes, i have made such list but it seems its not storing it properly. I followed your example of storing points to a list and list>. I have a feeling that im adding the wrong informatie to that list however. – Alex Turner Aug 16 '16 at 14:29
  • [see code here](http://collabedit.com/bwhwf) this is what ive come up with so far. if im being to much of a hassle and bothering you, my apologies. – Alex Turner Aug 16 '16 at 14:39
  • Could i bother you for one last request/question? knowing your skills, it wont take long. Im trying to add a way to draw straight lines which only are drawn vertically or horizontally over the x,x or y,y on mouse input. Im over at [here again](http://collabedit.com/bwhwf) – Alex Turner Aug 16 '16 at 18:18
  • Im trying to finish this little application by adding color to the pen but im having an issue(as always), as soon as i change the pen color on a button_click all of the previous drawn graphics are also changed. I know a drawaction class would solve this but that something i wanna practice in the next project. So when changing colors, i would need to add this to the list? [still here](http://collabedit.com/bwhwf) – Alex Turner Aug 17 '16 at 08:14