I recently started programming in C#
obviously and was trying to do a simple WinForms
app that takes mouse coordinates and scales a Rectangle accor
When drawing on a Control's surface, you always use the Paint event of that Control.
Do not ever try to store it's Graphics
object (it isn't valid anymore as soon as the Control is invalidated (repainted)).
Don't try to use its PaintEventArgs in some exotic way. Use the Graphics
object it references inside the Paint
event only.
When a more complex procedure is required to draw more, different, shapes, you can pass the e.Graphics
object provided by the Paint
event to different methods, which will use the e.Graphics
object to perform specialized drawings.
In the example, I'm storing each drawn Rectangle's coordinates in a specialized class (DrawingRectangle
, a simplified structure here) and I use a List<DrawingRectangle>()
to access these references.
When the MouseDown
event reports that the Left button is pressed on the Control's surface, the e.Location
is stored as the DrawingRectangle.Location
(this value can change, depending on the mouse pointer direction) and the DrawingRectangle.StartPoint
(a reference measure that doesn't change).
When the Mouse is moved, the current e.Location
coordinates values are subtracted from the stored Rectangle starting point coordinates (plus some calculations that allow to draw the Rectangle from all sides). This measure is the Size of the Rectangle.
Each time a Left MouseDown
is detected, a new class element is added to the List.
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
List<DrawingRectangle> drawingRects = new List<DrawingRectangle>();
public class DrawingRectangle
{
public Point Location { get; set; }
public Size Size { 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) {
drawingRects.Add(new DrawingRectangle() {
Location = e.Location, Size = Size.Empty, StartPosition = e.Location
});
}
}
private void form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DrawingRectangle rect = drawingRects.Last();
if (e.Y < rect.StartPosition.Y) { rect.Location = new Point(rect.Location.Y, e.Y); }
if (e.X < rect.StartPosition.X) { rect.Location = new Point(e.X, rect.Location.Y); }
rect.Size = new Size(Math.Abs(rect.StartPosition.X - e.X), Math.Abs(rect.StartPosition.Y - e.Y));
drawingRects[drawingRects.Count - 1] = rect;
this.Invalidate();
}
}
private void form1_Paint(object sender, PaintEventArgs e)
{
if (drawingRects.Count == 0) return;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var rect in drawingRects) {
using (Pen pen = new Pen(rect.DrawingcColor, rect.PenSize)) {
e.Graphics.DrawRectangle(pen, new Rectangle(rect.Location, rect.Size));
};
}
}
private void btnClear_Click(object sender, EventArgs e)
{
drawingRects.Clear();
this.Invalidate();
}
app that takes mouse coordinates and scales rectangle according to the coordinates
I'd expect to see something like this (pseudocode):
Point _point;
void Form1_MouseMove(object sender, MouseEventArgs e)
{
... // calculate new coordinates/scale factor/whatever here
_point = ... ; // store results in fields
Invalidate(); // this will cause repaint every time you move mouse
}
void Form1_Paint(object sender, PaintEventArgs e)
{
... // take values from fields
e.Graphics.DrawRectangle(pen, rect); // draw
}
It's pretty simply. Painting is a combination of Invalidate()
calls, which rise up paint event. The variables you pass using fields.
Drawing in WinForms application works in a slightly different way then you probably expect. Everything on screen now is considered to be temporary, if you e.g. minimize and restore your window, the onscreen stuff will be erased and you'll be asked to repaint it again (your window's Paint event will be fired by the system).
That's why that DrawRect method expects PaintEventArgs argument: it is supposed to be called only withing your Paint event handler. If you call it from outside (like it is suggested in other answer) your rectangles might behave inconsistently.
I would suggest remember your rectangles in some internal variable and then repaint them when asked for that by the system:
private Point pointToDrawRect = new Point(0,0);
public void Form1_MouseMove(object sender, MouseEventArgs e)
{
int x = e.X;
int y = e.Y;
String data = (x.ToString() + " " + y.ToString());
pointToDrawRect= new Point(x, y);
Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(pointToDrawRect.X != 0 || pointToDrawRect.Y != 0)
{
DrawRect(e, pointToDrawRect.X, pointToDrawRect.Y);
}
}
public void DrawRect(PaintEventArgs e, int rey, int rex)
{
using (Pen pen = new Pen(Color.Azure, 4))
{
Rectangle rect = new Rectangle(0, 0, rex, rey);
e.Graphics.DrawRectangle(pen, rect);
}
}
The PaintEventArgs
allows you to access to the Graphics
object, you need that one to draw something.
If you don't want to use the PaintEventArgs
, i suggest that you call the CreateGraphics()
method of your Form
, and it will allow you to draw the rectangle.
To improve performance, i suggest that you use the using(...){ } keywork in order to dispose the Graphics
object and the Pen
object.
You need to include System.Drawing
in order to use Graphics
and Pen
.
You're code will look like that :
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
Point _coordinates;
public Form1()
{
this._coordinates = new Point();
this.InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public void Form1_MouseMove(object sender, MouseEventArgs e)
{
this._coordinates = new Point(e.X, e.Y);
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Don't draw on first Paint event
if(this._coordinates.X != 0 && this._coordinates.Y != 0)
{
this.DrawRect(e);
}
}
public void DrawRect(PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Azure, 4))
{
Rectangle rect = new Rectangle(0, 0, this._coordinates.X, this._coordinates.Y);
e.Graphics.DrawRectangle(pen, rect);
}
}
}
}