When drawing on a Control's surface, you always use the Paint
event of that Control or override the OnPaint
method of a Custom/User Control.
Do not try to store its Graphics
object: it becomes invalid as soon as the Control is invalidated (repainted).
Use the Graphics
object provided by the PaintEventArgs
object.
When a more complex procedure is required to draw different shapes, you can pass the e.Graphics
object to different methods which will use this object to perform specialized drawings.
In the example, the coordinates and other properties of each drawn shape are assigned to a specialized class, DrawingRectangle
(a simplified structure here, it can hold more complex functionalities).
A List<DrawingRectangle>()
stores the references of all the DrawingRectangle
objects generated in a session (until the drawing is cleared).
Each time a Left MouseDown
event is generated on the Control's surface, a new DrawingRectangle
object is added to the List.
The e.Location
is stored both as the DrawingRectangle.StartPoint
(a value that doesn't change) and the DrawingRectangle.Location
: this value will be updated when the mouse pointer is moved.
When the Mouse is moved, the current e.Location
value is subtracted from the starting point coordinates previously stored. A simple calculation allows to draw the shapes from all sides.
This measure determines the current Size of the Rectangle.
To remove a Rectangle from the drawing, you just need to remove its reference from the List and Invalidate()
the Control that provides the drawing surface.
To clear the drawing surface, clear the List<DrawingRectangle>()
(drawingRects.Clear()
) and call Invalidate()
.
Some other examples here:
Transparent Overlapping Circular Progress Bars
GraphicsPath and Matrix classes
Connecting different shapes
Drawing Transparent/Translucent Custom Controls
// Assign the Color used to draw the border of a shape to this Field
Color SelectedColor = Color.LightGreen;
List<DrawingRectangle> drawingRects = new List<DrawingRectangle>();
public class DrawingRectangle
{
public Rectangle Rect => new Rectangle(Location, Size);
public Size Size { get; set; }
public Point Location { get; set; }
public Control Owner { get; set; }
public Point StartPosition { get; set; }
public Color DrawingcColor { get; set; } = Color.LightGreen;
public float PenSize { get; set; } = 3f;
}
private void form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
DrawingRects.Add(new DrawingRectangle() {
Location = e.Location,
Size = Size.Empty,
StartPosition = e.Location,
Owner = (Control)sender,
DrawingcColor = SelectedColor // <= Shape's Border Color
});
}
private void form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
var dr = DrawingRects[DrawingRects.Count - 1];
if (e.Y < dr.StartPosition.Y) { dr.Location = new Point(dr.Rect.Location.X, e.Y); }
if (e.X < dr.StartPosition.X) { dr.Location = new Point(e.X, dr.Rect.Location.Y); }
dr.Size = new Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y));
this.Invalidate();
}
private void form1_MouseUp(object sender, MouseEventArgs e)
{
// The last drawn shape
var dr = DrawingRects.Last();
// ListBox used to present the shape coordinates
lstPoints.Items.Add($"{dr.Location}, {dr.Size}");
}
private void form1_Paint(object sender, PaintEventArgs e)
{
DrawShapes(e.Graphics);
}
private void DrawShapes(Graphics g)
{
if (DrawingRects.Count == 0) return;
g.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var dr in DrawingRects) {
using (Pen pen = new Pen(dr.DrawingcColor, dr.PenSize)) {
g.DrawRectangle(pen, dr.Rect);
};
}
}
// A Button used to save the current drawing to a Bitmap
private void btnSave_Click(object sender, EventArgs e)
{
using (var bitmap = new Bitmap(panCanvas.ClientRectangle.Width, panCanvas.ClientRectangle.Height))
using (var g = Graphics.FromImage(bitmap)) {
DrawShapes(g);
bitmap.Save(@"[Image Path]", ImageFormat.Png);
// Clone the Bitmap to show a thumbnail
}
}
// A Button used to clear the current drawing
private void btnClear_Click(object sender, EventArgs e)
{
drawingRects.Clear();
this.Invalidate();
}
