I need some help with my code as I'm doing something wrong! I tried to fix and do everything in the proper way but I was make more problems... The code below is the prototype of a Pain-like program which I want to create after I know everything what I need and the prototype works perfectly.
I share the full code here and I also give a link to download my full project/solution, so you can run it to see what's happening or modify to test.
DOWNLOAD: PainPrototype_BushWookie
The main issues:
- I want to create rectangles by mouse button in the way how this function used to work in the drawing apps. This means when my mouse down I start to draw rect, when I move the mouse in the canvas the rect dynamically change its size to adapt. When my mouse up, the rect is done. I'm sure you know what I'm talking about as this is the way how almost all of the paint software draw shapes including rectangles. I could to code this, but I'm not sure I did on the proper way, and I also has an issue with this! After I draw a rect, and I click again to draw another one, the previous rect is vanished/deleted.. I need to fix this with your help!
Worth to note when I use my "graphics" object (commented in code) and not "e.Graphics" to draw the rect this will draw the shape every thick/frame/update.. so when I move the mouse I will be hundreds of rect as this draw continuously..
I also have "brush" tool which can draw on the canvas like the brush tool in Photoshop or pen tool in the basic Windows Paint. This part is working fine! But I draw from the 'MouseMove' event and not from the Paint. I saw this in a tutorial video but everybody say "draw only from Pain event".. When I tried to do this, the functionality of the brush has ruined. This means, it draw straight line and not "chase" the mouse cursor and draw after its path. Can you help me clarify this, and how this should work from the Paint event?
My last issue is emerged when I clicked my "Clear" button, which should clear down the whole canvas! This worked fine with my Brush (only when I draw from MouseMove) and Pixel tool (draw a single pixel) and cleared the canvas properly.. The rectangle what I draw from Paint event is not cleared. Also the rect stay there after I push "new" button to get new empty canvas. This is big problem! If I clear it from Paint event it will do, but this cause the Paint event call in an infinite loop..
This are my main issues and I need help with them! Also I have some question about the drawing/graphics what I can't understand properly from the MSDN:
- Why should I draw only from Pain event? or turn outside the question, what's wrong if I draw from MouseMove?
- What is the OnPaint() and why is it different from Paint()?
- Invalidate().. I know this will call the Paint event, but anything else to know? Why is it different from any other method which cause the Paint event to redraw?
I also would like to ask to overview every line of my code to help learn what I can improve as I'm quite new in C#! For instance, when I try to release the old picturebox from the "New" button.. all of my lines are necessary? or did I miss something? etc etc.. or my code logic is good, or should I do in other way?
Thanks in advance for your time! Would be the best if you download my full solution from the link above to see through easier and run/test it! I just want to give help to fix my issues and learn coding the proper way! Thanks again, I appreciate any help!
Here my code, please notice the difference between pbx_canvas and bmp_canvas! (I want to draw on a bitmap as I want to save the created images later).
namespace NewFile___PixelDraw___Pen___Shapes
{
public partial class Form1 : Form
{
PictureBox pbx_usedColorBox;
PictureBox pbx_canvas;
Bitmap btm_canvas;
Graphics graphics;
Pen pen;
Button btn_activeTool = new Button();
Point initialMouse = new Point(-1, -1);
Color chosenColor = Color.White;
bool mouseDown;
Rectangle rect;
public Form1()
{
InitializeComponent();
pen = new Pen(chosenColor, (float)num_drawWidth.Value)
{
StartCap = System.Drawing.Drawing2D.LineCap.Round,
EndCap = System.Drawing.Drawing2D.LineCap.Round,
};
tbx_canvasX.MaxLength = 5;
tbx_canvasY.MaxLength = 5;
pbx_usedColorBox = pbx_cBlack;
pnl_cPalette.BackColor = chosenColor;
pbx_cWhite.BorderStyle = BorderStyle.None;
}
// NEW
private void btn_new_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(tbx_canvasX.Text) && !string.IsNullOrEmpty(tbx_canvasY.Text))
{
Point offset = new Point(7, 19);
Point canvasSize = new Point(int.Parse(tbx_canvasX.Text), int.Parse(tbx_canvasY.Text));
Point canvasLocation = new Point((Size.Width / 2) - (canvasSize.X / 2) - offset.X, (Size.Height / 2) - (canvasSize.Y / 2) - offset.Y);
if (canvasSize.X != 0 && canvasSize.Y != 0)
{
if (pbx_canvas != null && btm_canvas != null)
{
pbx_canvas.MouseDown -= Pbx_canvas_MouseDown;
pbx_canvas.MouseMove -= Pbx_canvas_MouseMove;
pbx_canvas.MouseUp -= Pbx_canvas_MouseUp;
Controls.Remove(pbx_canvas);
pbx_canvas.Dispose();
btm_canvas.Dispose();
graphics.Dispose();
}
pbx_canvas = new PictureBox
{
Size = new Size(canvasSize.X, canvasSize.Y),
BorderStyle = BorderStyle.FixedSingle,
Anchor = AnchorStyles.None,
Location = canvasLocation,
BackColor = Color.White,
Cursor = Cursors.Cross,
};
Controls.Add(pbx_canvas);
pbx_canvas.MouseDown += Pbx_canvas_MouseDown;
pbx_canvas.MouseMove += Pbx_canvas_MouseMove;
pbx_canvas.MouseUp += Pbx_canvas_MouseUp;
pbx_canvas.Paint += Pbx_canvas_Paint;
btm_canvas = new Bitmap(canvasSize.X, canvasSize.Y);
graphics = Graphics.FromImage(btm_canvas);
if (cbx_smoothing.Checked)
{
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
}
}
else
{
tbx_canvasX.Text = "";
tbx_canvasY.Text = "";
}
}
}
// PAINT
private void Pbx_canvas_Paint(object sender, PaintEventArgs e)
{
Debug.WriteLine("IN PAINT EVENT!");
e.Graphics.DrawRectangle(pen, rect);
//graphics.DrawRectangle(pen, rect);
}
// M > DOWN
private void Pbx_canvas_MouseDown(object sender, MouseEventArgs e)
{
switch (btn_activeTool.Name)
{
case "btn_toolBrush":
mouseDown = true;
initialMouse = e.Location;
break;
case "btn_toolRect":
mouseDown = true;
initialMouse = e.Location;
rect = new Rectangle(initialMouse, new Size(0, 0));
break;
}
}
// M > MOVE
private void Pbx_canvas_MouseMove(object sender, MouseEventArgs e)
{
switch (btn_activeTool.Name)
{
case "btn_toolBrush":
if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
{
graphics.DrawLine(pen, initialMouse, e.Location);
pbx_canvas.Image = btm_canvas;
initialMouse = e.Location;
}
break;
case "btn_toolRect":
if (mouseDown == true && initialMouse.X != -1 && initialMouse.Y != -1)
{
rect = new Rectangle(Math.Min(e.X, initialMouse.X),
Math.Min(e.Y, initialMouse.Y),
Math.Abs(e.X - initialMouse.X),
Math.Abs(e.Y - initialMouse.Y));
pbx_canvas.Image = btm_canvas;
//Invalidate(rect);
}
break;
}
}
// M > UP
private void Pbx_canvas_MouseUp(object sender, MouseEventArgs e)
{
switch (btn_activeTool.Name)
{
case "btn_toolPixel":
btm_canvas.SetPixel(e.X, e.Y, chosenColor);
pbx_canvas.Image = btm_canvas;
break;
case "btn_toolBrush":
mouseDown = false;
initialMouse.X = -1;
initialMouse.Y = -1;
break;
case "btn_toolRect":
mouseDown = false;
initialMouse.X = -1;
initialMouse.Y = -1;
break;
}
}
// PIXEL
private void btn_toolPixel_Click(object sender, EventArgs e)
{
btn_activeTool.BackColor = Color.GhostWhite;
btn_activeTool = (Button)sender;
btn_activeTool.BackColor = Color.Beige;
}
// BRUSH
private void btn_toolBrush_Click(object sender, EventArgs e)
{
btn_activeTool.BackColor = Color.GhostWhite;
btn_activeTool = (Button)sender;
btn_activeTool.BackColor = Color.Beige;
}
// RECT
private void btn_toolRect_Click(object sender, EventArgs e)
{
btn_activeTool.BackColor = Color.GhostWhite;
btn_activeTool = (Button)sender;
btn_activeTool.BackColor = Color.Beige;
}
// CLEAR
private void btn_clear_Click(object sender, EventArgs e)
{
graphics.Clear(pbx_canvas.BackColor);
pbx_canvas.Image = null;
}
// Color change
private void G_pbx_colors_Click(object sender, EventArgs e)
{
PictureBox pbx_sender = (PictureBox)sender;
pbx_usedColorBox.BorderStyle = BorderStyle.FixedSingle;
pbx_sender.BorderStyle = BorderStyle.None;
pbx_usedColorBox = pbx_sender;
chosenColor = pbx_sender.BackColor;
pnl_cPalette.BackColor = chosenColor;
pen.Color = chosenColor;
}
// CanvasSize
private void G_tbx_canvasSize_Enter(object sender, EventArgs e)
{
BeginInvoke(new Action(() => (sender as TextBox).SelectAll()));
}
private void G_tbx_canvasSize_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar);
}
// DrawWidth
private void num_drawWidth_ValueChanged(object sender, EventArgs e)
{
pen.Width = (float)num_drawWidth.Value;
}
}
}