In fact , I want to draw circle in new position each time double-click and without remove before circle ,It should be noted that, I used PictureBox.
public Point postionCursor { get; set; }
List<Point> points = new List<Point>();
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
postionCursor = this.PointToClient(new Point(Cursor.Position.X - 25, Cursor.Position.Y - 25));
points.Add(postionCursor);
pictureBox1.Invalidate();
pictureBox1.Paint += new PaintEventHandler(pic_Paint);
}
private void pic_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
foreach (Point pt in points)
{
Pen p = new Pen(Color.Tomato, 2);
SolidBrush myb = new SolidBrush(Color.White);
g.DrawEllipse(p, postionCursor.X, postionCursor.Y, 20, 20);
g.FillEllipse(myb, postionCursor.X, postionCursor.Y, 20, 20);
p.Dispose();
}
}
You're not using the pt variable in the foreach loop.
foreach (Point pt in points)
{
using(Pen p = new Pen(Color.Tomato, 2))
using(SolidBrush myb = new SolidBrush(Color.White))
{
g.FillEllipse(myb, pt.X, pt.Y, 20, 20);
g.DrawEllipse(p, pt.X, pt.Y, 20, 20);
}
}
In your code, you were just overwriting the circle in the same location for every Point in the points list.
Also, as Reza mentioned in the comments, you don't need to attach the PaintEventHandler event hanlder every time the PictureBox is clicked, you just need to do it once.
So I got to thinking, and then Visual Studio-ing, that perhaps we don't even need the foreach loop. I still maintain a List so we know where the user has clicked, but there's no need to loop through it and redraw everything every time.
I realize this doesn't handle the case where the underlying list is modified, but nor does the original sample. Here's my entire Form1 class:
public partial class Form1 : Form
{
private const int CircleDiameter = 20;
private const int PenWidth = 2;
private readonly List<Point> _points = new List<Point>();
public Form1()
{
InitializeComponent();
pictureBox1.Paint += (sender, args) =>
{
_points.ForEach(p => DrawPoint(p, args.Graphics));
};
}
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
var cursorLocation = pictureBox1.PointToClient(Cursor.Position);
_points.Add(cursorLocation);
var circleArea = new Rectangle(
cursorLocation.X - CircleDiameter/2 - PenWidth,
cursorLocation.Y - CircleDiameter/2 - PenWidth,
CircleDiameter + PenWidth*2,
CircleDiameter + PenWidth*2);
pictureBox1.Invalidate(circleArea);
}
private static void DrawPoint(Point point, Graphics graphics)
{
point.X -= CircleDiameter / 2;
point.Y -= CircleDiameter / 2;
using (var pen = new Pen(Color.Tomato, PenWidth))
using (var brush = new SolidBrush(Color.White))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawEllipse(pen, point.X, point.Y, CircleDiameter, CircleDiameter);
graphics.FillEllipse(brush, point.X, point.Y, CircleDiameter, CircleDiameter);
}
}
}
Update 1:
So I updated the code to use the Paint event which has the foreach loop. However, I don't Invalidate (and Paint) every time a circle is added - there's no need for that. Just adding a circle by drawing means the control only invalidates and re-paints the region where the new circle was added.
Try setting a breakpoint on the DrawAllPoints method. You'll see it only happens during full invalidation operations such as minimizing and restoring.
Update 2:
After further chat, I agree the Invalidate method is superior. Code updated to use Invalidate with a rectangle to invalidate.
And now it's looking very much like the OP :)
Related
You know, we can easily to make line cursor for Chart (ex: Fig). But with PictureBox, how can I do it? Is there anyone has the solution?
You can intercept the MouseMove and the Paint events. Just draw the cross on the paint.
The advantage of using the Paint method, is that the original image is not changed, so no need to restore the overwritten pixels by the crosshair.
Here's an example:
I dropped a picturebox on a winform and linked some events.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace MouseCrosshair
{
public partial class Form1 : Form
{
// to store the latest mouse position
private Point? _mousePos;
// the pen to draw the crosshair.
private Pen _pen = new Pen(Brushes.Red);
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
// when the mouse enters the picturebox, we just hide it.
Cursor.Hide();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
var pictureBox = (PictureBox)sender;
// on a mouse move, save the current location (to be used when drawing the crosshair)
_mousePos = e.Location;
// force an update to the picturebox.
pictureBox.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// if the mousepos is assigned (meaning we have a mouse pos, draw the crosshair)
if (_mousePos.HasValue)
{
var pictureBox = (PictureBox)sender;
// draw a vertical line
e.Graphics.DrawLine(_pen, new Point(_mousePos.Value.X, 0), new Point(_mousePos.Value.X, pictureBox.Height));
// draw a horizontal line
e.Graphics.DrawLine(_pen, new Point(0, _mousePos.Value.Y), new Point(pictureBox.Width, _mousePos.Value.Y));
}
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
// when the mouse is outside the picturebox, clear the mousepos
_mousePos = null;
// repaint the picturebox
pictureBox1.Invalidate();
// show the mouse cursor again.
Cursor.Show();
}
}
}
Because the events are using the sender, you can link multiple pictureboxes to these events.
It's also possible to inherit from the PictureBox, and write a new CrosshairPictureBox control, which has a crosshair by default.
If you want to draw charts in a PictureBox, use a Bitmap and draw on that using the Graphics.FromImage(bitmap) and put it in the PictureBox.Image. Don't forget to dispose the Graphics object.
You can achieve this by storing the position of the last point received, and then draw a line using the Graphics.DrawLine method between the old position and the new one.
Please also note, that when the mouse is moving, the Control.MouseMove event for every single pixel traveled by the mouse pointer isn't received for every single move. You do receive the Control.MouseMove events at a fairly consistent time interval. That means that the faster the mouse moves, the further apart the points you'll be actually receiving.
Check out this walkthrough for some examples - https://www.c-sharpcorner.com/UploadFile/mahesh/drawing-lines-in-gdi/
If I understand the question correctly, you are interested to draw x-axis and y-axis for a chart, but not using a chat control.
In this case, what you need to do is: Handle the Paint event of the PictureBox and draw the line from top middle to bottom middle and from left middle to right middle.
Here is the code which I write to produce above chart, y = Sin(x)
:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var axisWidth = 3;
var axisColor = Color.Red;
var chartLineWidth = 2;
var chartLineColor = Color.Blue;
var scale = 90;
var gridSize = 45;
var gridLineWidth = 1;
var gridLineColor = Color.LightGray;
var g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var w = pictureBox1.ClientRectangle.Width / 2;
var h = pictureBox1.ClientRectangle.Height / 2;
g.TranslateTransform(w, h);
g.ScaleTransform(1, -1);
//Draw grid
for (int i = -w / gridSize; i <= w / gridSize; i++)
using (var axisPen = new Pen(gridLineColor, gridLineWidth))
g.DrawLine(axisPen, i * gridSize, -h, i * gridSize, h);
for (int i = -h / gridSize; i <= h / gridSize; i++)
using (var axisPen = new Pen(gridLineColor, gridLineWidth))
g.DrawLine(axisPen, -w, i * gridSize, w, i * gridSize);
//Draw axis
using (var axisPen = new Pen(axisColor, axisWidth))
{
g.DrawLine(axisPen, -w, 0, w, 0); //X-Asxis
g.DrawLine(axisPen, 0, -h, 0, h); //Y-Asxis
}
//Draw y = Sin(x)
var points = new List<PointF>();
for (var x = -w; x < w; x++)
{
var y = System.Math.Sin(x * Math.PI / 180);
points.Add(new PointF(x, scale * (float)y));
}
using (var chartLinePen = new Pen(chartLineColor, chartLineWidth))
{
g.DrawCurve(chartLinePen, points.ToArray());
}
g.ResetTransform();
}
You also need the following piece of code to handle resizing of the picture box:
private void MyForm_Load(object sender, EventArgs e)
{
this.pictureBox1.GetType().GetProperty("ResizeRedraw",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance).SetValue(
this.pictureBox1, true);
}
You can also add a crosshair and rubber-band rectangle to the control, like the following image:
I created in a Window Form 4 PictureBoxes (pictureBox1, pictureBox2, pictureBox3 and pictureBox4). I also created a function to draw a rectangle on a pictureBox like this:
To create the rectangle:
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
var x = e.X;
var y = e.Y;
var width = 10;
var height = 10;
FwRect = new[]
{
new PointF(x, y), new PointF(x, y + height), new PointF(x + width, y + height),
new PointF(x + width, y)
};
FwRectan = new Rectangle((int)x, (int)y, (int)width, (int)height);
Refresh();
}
Then I added this event for each pictureBox:
this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox3.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
this.pictureBox4.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
To draw the rectangle:
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, FwRectan);
}
}
Eventually, if I move the mouse inside the pictureBox1 and draw a rectangle, it also draw a rectangle for each pictureBox. How can I draw a rectangle only on the pictureBox that the mouse is located at?
Thank you very much!
You have 4 PictureBox, so you need 4 Rectangle to draw current mouse movement:
Rectangle[] _rectangle = new Rectangle[4];
then in both common PictureBox_MouseMove and PictureBox_Paint events you need to identify which value to use, index of picturebox. It can be done by using Tag property or by putting all pictureboxes into array so that their index there will match:
PictureBox _control = new PictureBox[] { pictureBox1, pictureBox2, pictureBox3, pictureBox4 };
The event handles will looks like this
void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
var x = e.X;
var y = e.Y;
var width = 10;
var height = 10;
FwRect = new[]
{
new PointF(x, y), new PointF(x, y + height), new PointF(x + width, y + height),
new PointF(x + width, y)
};
var index = _control.IndexOf(sender);
_rectangle[index] = new Rectangle((int)x, (int)y, (int)width, (int)height);
_rectangle[index].Invalidate();
}
void PictureBox_Paint(object sender, PaintEventArgs e)
{
var index = _control.IndexOf(sender);
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rectangle[index]);
}
}
Edit:
Actually above solution will remember rectangle for each picturebox. Might not be what you want. The simple fix would be to clear other rectangles in mousemove. Though the more proper solution would be to remember sender from mousemove and only paint matching sender in paint.
Here's an example showing how to store the Rectangle in the .Tag property as mentioned by Sinatr in his post. This example also clears the Rectangle when the mouse leaves so you only ever have one Rectangle being drawn in the current PictureBox:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.pictureBox1.MouseMove += this.PictureBox_MouseMove;
this.pictureBox2.MouseMove += this.PictureBox_MouseMove;
this.pictureBox3.MouseMove += this.PictureBox_MouseMove;
this.pictureBox4.MouseMove += this.PictureBox_MouseMove;
this.pictureBox1.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox2.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox3.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox4.MouseLeave += this.pictureBox_MouseLeave;
this.pictureBox1.Paint += this.PictureBox_Paint;
this.pictureBox2.Paint += this.PictureBox_Paint;
this.pictureBox3.Paint += this.PictureBox_Paint;
this.pictureBox4.Paint += this.PictureBox_Paint;
}
private int bxWidth = 10;
private int bxHeight = 10;
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.Tag = new Rectangle(e.X, e.Y, bxWidth, bxHeight);
pb.Invalidate();
}
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (pb.Tag != null && pb.Tag is Rectangle)
{
Rectangle rc = (Rectangle)pb.Tag;
using (var pen = new Pen(Color.Black, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, rc);
}
}
}
private void pictureBox_MouseLeave(object sender, EventArgs e)
{
PictureBox pb = (PictureBox)sender;
pb.Tag = null; // if you want the box to disappear when the mouse leaves?
pb.Invalidate();
}
}
Here's what it looks like running:
I found out how to draw Rectangles and some code to find when two rectangles overlap but I can't connect these procedures.
I have the two rectangles that I wanted but then a cannot determine whether these intersect, to then add this information to a ListBox.
Here is my code:
public partial class Form1 : Form
{
Graphics g;
Pen p;
Point cursor;
int k = 0;
Point[] tocke = new Point[2];
public Form1()
{
InitializeComponent();
g = this.CreateGraphics();
p = new Pen(Color.Black, 3);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
cursor = this.PointToClient(Cursor.Position);
statusMisa.Text = "X: " + cursor.X + " Y: " + cursor.Y;
}
private void Form1_Click(object sender, EventArgs e)
{
bool Preklapanje(int l1x, int l1y, int l2x, int l2y, int r1x, int r1y, int r2x, int r2y)
{
if (l1x >= r2x || l2x >= r1x)
{
return false;
}
if (l1y <= r2y || l2y <= r1y)
{
return false;
}
return true;
}
List<int> pozX = new List<int>();
List<int> pozY = new List<int>();
if (checkCrtanje.Checked == true)
{
Rectangle rect = new Rectangle(cursor.X - 50, cursor.Y - 50, 100, 100);
if (k < 2)
{
g.DrawRectangle(p, rect);
tocke[k++] = new Point(cursor.X, cursor.Y);
listBox1.Items.Add("X: " + cursor.X + " Y: " + cursor.Y);
pozX.Add(cursor.X);
pozY.Add(cursor.Y);
}
else
{
MessageBox.Show("Možeš nacrtati samo dva kvadrata!");
}
}
if (k == 3)
{
if (Preklapanje(pozX[0] - 50, pozY[0] - 50, pozX[0] + 50, pozY[0] + 50, pozX[1] - 50, pozY[1] - 50, pozX[1] + 50, pozY[1] + 50))
{
listBox1.Items.Add("Preklapaju se.");
}
else
{
listBox1.Items.Add("Ne preklapaju se.");
}
}
}
}
► As noted, you shouldn't use CreateGraphics() to draw on a Control's surface: this object becomes invalid (belongs to the past) as soon as the Control where the drawing is performed is invalidated (is repainted).
All Controls that have a drawable surface, raise a Paint event and have an OnPaint method that we can override to perform custom painting (the OnPaint method is responsible to raise the Paint event, so we need to handle just one).
The argument of both the event and the method, represents a PaintEventArgs object, which provides a fresh Graphics object that we can use to paint.
We can call the Invalidate() method to repaint a Control when needed. This method causes the generation of a new PaintEventArgs object (thus, a new Graphics object). After, the OnPaint method is called, which - in turn - raises the Paint event.
► To determine whether a Rectangle intersects another (or more than one), we can use the Rectangle.IntersetWith() method (it returns true or false) and the Rectangle.Interset() method → this is used to generate a Rectangle that represents the intersection of two other rectangles.
See also:
→ Rectangle.Contains([Rectangle])
→ Rectangle.Union([Rectangle a], [Rectangle b]).
Here, I'm using a few collections to store the shapes currently drawn and their intersections (just rectangles, but you can build more complex shapes using GraphicsPath objects):
A List<Rectangle> (rects) which stores the Rectangles already created.
A List<Rectangle> (intersections), to store the intersections which belong to the past (intersections already drawn).
A List<Rectangle> (currentIntersects), used to temporarily store the intersection generated when a new Rectangle shaped is being drawn, so we can use different colors (as soon as we release the Mouse Button, this collection is fixed and added to the intersections collection).
A Rectangle structure (currentRect) which represents the Rectangle that is currently being drawn on the surface (when the Mouse Button is released, this object is added to the rects collection).
A Point structure (startPosition), used to store the initial position of the Rectangle currently drawn. It's reset when the OnMouseDown method is called (when a new Rectangle shape is generated).
► To use this code, create a new Form and paste the code you find here in its Code file. No need to subscribe to any event: since we're drawing on a Form, I'm overriding its methods (OnPaint, OnMouseDown, OnMouseUp, OnMouseMove), no event is used.
You can do the same with a Custom Control or a UserControl.
To add these collection, or just the intersections collection, to e.g., a ListBox, to handle the collections visually, see here (the currentIntersects and intersections collections already contain the information):
How to call a method that uses PaintEventArgs and coordinates variables
NOTE:
► Here, in the OnPaint method override, I'm not calling base.OnPaint(), so the event is not generated. This speeds up the process a bit, but keep in mind that subscribing to the Form's Paint event is useless.
► You need to activate double-buffering: (set DoubleBuffered = true), otherwise you'll notice a lot of flickering (this is quite normal).
This is how it works:
public partial class FormDrawings : Form
{
private List<Rectangle> rects = new List<Rectangle>();
private List<Rectangle> intersections = new List<Rectangle>();
private List<Rectangle> currentIntersects = new List<Rectangle>();
private Rectangle currentRect = Rectangle.Empty;
private Point startPosition = Point.Empty;
private float penSize = 2.0f;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left) {
startPosition = e.Location;
currentRect = new Rectangle(startPosition, Size.Empty);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left) {
if (e.Y < startPosition.Y) { currentRect.Location = new Point(currentRect.X, e.Y); }
if (e.X < startPosition.X) { currentRect.Location = new Point(e.X, currentRect.Y); }
currentRect.Size = new Size(Math.Abs(startPosition.X - e.X), Math.Abs(startPosition.Y - e.Y));
currentIntersects.Clear();
foreach (var rect in rects) {
if (currentRect.IntersectsWith(rect)) {
currentIntersects.Add(Rectangle.Intersect(currentRect, rect));
}
}
this.Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (currentRect.Size != Size.Empty) rects.Add(currentRect);
if (currentIntersects.Count > 0) {
intersections.AddRange(currentIntersects);
}
}
protected override void OnPaint(PaintEventArgs e)
{
using (var borderPen = new Pen(Color.LightGreen, penSize))
using (var iBrush = new SolidBrush(Color.FromArgb(128, Color.Orange)))
using (var crBrush = new SolidBrush(Color.FromArgb(128, Color.DeepSkyBlue))) {
intersections.ForEach(r => e.Graphics.FillRectangle(iBrush, r));
currentIntersects.ForEach(r => e.Graphics.FillRectangle(crBrush, r));
e.Graphics.DrawRectangle(borderPen, currentRect);
rects.ForEach(r => e.Graphics.DrawRectangle(borderPen, r));
}
}
}
I have found this question (as a few others), but this is the one I have implemented so far:
Crosshair cursor with additional lines in C#
As it states, I can use a stock cursor "cross" directly in the IDE. This is a really good way to do things. The answer specified in the answer above draws a cross on the screen at the given width / height. Eg:
private Cursor crossCursor(Pen pen, Brush brush, int x, int y)
{
var pic = new Bitmap(x, y);
Graphics gr = Graphics.FromImage(pic);
var pathX = new GraphicsPath();
var pathY = new GraphicsPath();
pathX.AddLine(0, y / 2, x, y / 2);
pathY.AddLine(x / 2, 0, x / 2, y);
gr.DrawPath(pen, pathX);
gr.DrawPath(pen, pathY);
IntPtr ptr = pic.GetHicon();
var c = new Cursor(ptr);
return c;
}
My issue is that I want my cross hairs to extend to the Bounds of the viewing area. To provide context here, I have:
//Form
//TableLayoutPanel
//UserControl (fills the TableLayoutPanel visible area)
So how can I adjust my cursor so that the lines extend (much like in CAD pacakages)?
Thanks.
Update: I have tried calling the method from here:
protected override void OnLoad(System.EventArgs e)
{
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
But it is not Ok because at this point in time Bounds is returning a dimension of 150 by 150 which is not the size of the TableLayoutPanel.
Update: I have adjuted it to use the Resize handler instead and it does improve things:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
The only problem now (and it kind of makes sense I suppose) is that the cursor will only take the full width and height of the view when it is central to the view. As soon as I move about in the view that cursor does not adjust. I always want a horizontal/vertical line through the mouse position (not just the initial cross).
See:
The crosshairs need extending (the thicker red lines). Either I need to constantly create the cursor as the mouse moves or construct the two lines another way. What to do?
I came across this:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bdbad6d-1f65-461b-8f0c-6ef4f243fa6b/crosshair-cursor-using-c?forum=csharpgeneral
So, instead of changing the cursor object I now draw lines in the controls MouseMove handler:
Region r = new Region();
r.Union(new Rectangle(0, lastY, this.Width, 1));
r.Union(new Rectangle(lastX, 0, 1, this.Height));
this.Invalidate(r);
this.Update();
Graphics g = Graphics.FromHwnd(this.Handle);
g.DrawLine(Pens.White, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.White, e.X, 0, e.X, this.Height);
int intDiameter = 20;//the diameter of this circle
g.DrawEllipse(Pens.White, e.X - intDiameter / 2, e.Y - intDiameter / 2, 20, 20);
//to draw the circle
lastX = e.X;
lastY = e.Y;
It works, but I get noticiable screen flicker doing it this way.
You don't need to create a cursor. You can create a double buffered control and draw cross over control.
using System;
using System.Drawing;
using System.Windows.Forms;
public class DrawingSurface : Control
{
Pen crossPen;
Pen rectanglePen;
Brush rectangleBrush;
public DrawingSurface()
{
this.DoubleBuffered = true;
this.ResizeRedraw = true;
crossPen = new Pen(Color.Red, 2);
rectangleBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
rectanglePen = new Pen(Color.Blue, 1);
}
bool mouseDown = false;
Point startPoint = Point.Empty;
Point endPoint = Point.Empty;
protected override void OnMouseDown(MouseEventArgs e)
{
startPoint = e.Location;
mouseDown = true;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mouseDown = false;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
endPoint = e.Location;
this.Invalidate();
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
if (this.ClientRectangle.Contains(endPoint))
DrawCross(e.Graphics, endPoint);
if (mouseDown)
DrawRectangle(e.Graphics, startPoint, endPoint);
}
void DrawCross(Graphics g, Point point)
{
g.DrawLine(crossPen, new Point(0, point.Y), new Point(Width, point.Y));
g.DrawLine(crossPen, new Point(point.X, 0), new Point(point.X, Height));
}
void DrawRectangle(Graphics g, Point point1, Point point2)
{
var rectangle = new Rectangle(
Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y));
g.FillRectangle(rectangleBrush, rectangle);
g.DrawRectangle(rectanglePen, rectangle);
}
protected override void Dispose(bool disposing)
{
crossPen.Dispose();
rectanglePen.Dispose();
rectangleBrush.Dispose();
base.Dispose(disposing);
}
}
I have a PictureBox as UserControl. I added this User Control on the main form. Now I have to press a button and create a line on the user control. On my project, every time I press this button, I want to send to user control parameters of two PointF(x and y) and draw a new line, in addition to the existent one. I have so far the Paint event when picturebox is loaded.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Pen graphPen = new Pen(Color.Red, 2);
PointF pt1D = new PointF();
PointF pt2D = new PointF();
pt1D.X = 0;
pt1D.Y = 10;
pt2D.X = 10;
pt2D.Y = 10;
e.Graphics.DrawLine(graphPen, pt1D, pt2D);
}
Assuming that you want to draw the line on the click of the button, here's a modified version of your code:
List<PointF> points = new List<PointF>();
Pen graphPen = new Pen(Color.Red, 2);
private void btnDrawLines_Click(object sender, EventArgs e)
{
Graphics g = picBox.CreateGraphics();
PointF pt1D = new PointF();
PointF pt2D = new PointF();
pt1D.X = 0;
pt1D.Y = 10;
pt2D.X = 10;
pt2D.Y = 10;
g.DrawLine(graphPen, pt1D, pt2D);
points.Add(pt1D);
points.Add(pt2D);
}
private void picBox_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < points.Count; i+=2)
e.Graphics.DrawLine(graphPen, points[i], points[i + 1]);
}
Note that you can get a Graphics object through the PictureBox class's CreateGraphics() method which is the same as the e.Graphics object in the Paint event handler.
If you are adding lines to be drawn, the you probably want a little Line class:
public class Line {
public Point Point1 { get; set; }
public Point Point2 { get; set; }
public Line(Point point1, Point point2) {
this.Point1 = point1;
this.Point2 = point2;
}
}
And then you can just add these "lines" to a list:
private List<Line> _Lines = new List<Line>();
and add to them and tell the control to update it's drawing:
_Lines.Add(new Line(new Point(10, 10), new Point(42, 42)));
_Lines.Add(new Line(new Point(20, 40), new Point(20, 60)));
pictureBox1.Invalidate()
then in your drawing:
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.Clear(Color.White);
foreach (Line l in _Lines) {
e.Graphics.DrawLine(Pens.Red, l.Point1, l.Point2);
}
}