private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
points.Add(new PointF(e.X * xFactor, e.Y * yFactor));
pictureBox2.Invalidate();
label5.Visible = true;
label5.Text = String.Format("X: {0}; Y: {1}", e.X, e.Y);
counter += 1;
label6.Visible = true;
label6.Text = counter.ToString();
}
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
Pen p;
p = new Pen(Brushes.Green);
foreach (PointF pt in points)
{
e.Graphics.FillEllipse(Brushes.Red, pt.X, pt.Y, 3f, 3f);
}
foreach (PointF pt in points)
{
if (points.Count > 1)
{
e.Graphics.DrawLine(p, pt.X, pt.Y, 3f, 3f);
}
}
}
When i click on pictureBox1 its drawing a point on pictureBox2.
In the pictureBox2 paint im doing a loop over the List of points and drawing.
Then i did another loop and in the DrawLine i want to connect the last point with the next one with a line how can i do it ?
Tried this one now:
for (int i = 0; i < points.Count; i++)
{
if (points.Count > 1)
{
e.Graphics.DrawLine(p, points[i].X, points[i].Y, points[i+1].X, points[i+1].Y);
break;
}
}
But this will connect only the first two points not all the others.
I want that each time i make a click and draw a new point it will be automatic connected with a line to the last drawed point.
The break only allows one line to be drawn. Since you need to access 2 points at a time, you have to be careful to keep the array index within bounds. If you pick the 2nd point by doing i+1 then you need your for loop to stop when i < points.Count - 1 that gives you room at the end.
you should use the GraphicsPath take a look at this link class to do this for example to draw a line use this
private void AddLineExample(PaintEventArgs e)
{
//Create a path and add a symetrical triangle using AddLine.
GraphicsPath myPath = new GraphicsPath();
myPath.AddLine(30, 30, 60, 60);
myPath.AddLine(60, 60, 0, 60);
myPath.AddLine(0, 60, 30, 30);
// Draw the path to the screen.
Pen myPen = new Pen(Color.Black, 2);
e.Graphics.DrawPath(myPen, myPath);
}
Related
I've been trying to solve a problem I'm having with rotating an oval. The code that I'm posting works, but it's mimicking the kind of code I have to work with for real. Meaning, I can't use the OnPaint override and I'm limited to what I can do in the main code. I'm adding an oval to a graphic layer, and I need to be able to rotate, move and resize the oval. Move and resizing work flawlessly, but rotating doesn't work as I need it to.
If you click in the small box at the 9 oclock position,
the oval will rotate as expected:
The required behavior is to be able to click in the small box at the 12 oclock position, and have the oval rotate again. This does not occur. In order to get the oval to rotate again you need to click in the original 9 oclock position. What I'm really, really trying to find out is how to get the coordinates of the box at the 12 oclock position (or what ever position or location that box ends up after rotating) so that I can rotate it again. Thus far, I have been unable to figure it out. Please try and understand, I'm working with old code that was poorly written and I'm not allowed to change very much of it. What I write must integrate with what's already there. Below is the code snippet that demonstrates what I'm doing. I know I'm missing something obvious, just don't know what. Thanks.
public partial class Form1 : Form
{
int theAngle = 0;
Pen pen2 = new Pen(Color.FromArgb(255, 68, 125, 255), 4);
Pen pen3 = new Pen(Color.Green, 4);
Pen smallPen = new Pen(Color.Black, 1);
PointF center = new PointF(0, 0);
Rectangle rectangle = new Rectangle(20, 20, 100, 30);
Rectangle myRect2 = new Rectangle();
bool mouseBtnDown = false;
Graphics gw;
public Form1()
{
InitializeComponent();
this.MouseDown += mouseDown;
gw = this.CreateGraphics();
}
private void mouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (myRect2.Contains(e.Location))
theAngle+=90;
rotate();
}
if (e.Button == MouseButtons.Right)
{
//oval = false;
//mouseBtnDown = false;
theAngle = 0;
rectangle.X = e.X - 50;
rectangle.Y = e.Y - 15;
rectangle.Width = 100;
rectangle.Height = 30;
center.X = rectangle.Left + (0.5f * rectangle.Width);
center.Y = rectangle.Top + (0.5f * rectangle.Height);
myRect2.Size = new Size(15, 15);
myRect2.Location = new Point(rectangle.Left - 15, (rectangle.Top - (rectangle.Height / 2)) + 15);
drawstuff();
// Invalidate();
}
}
public void rotate()
{
var matrix = new Matrix();
matrix.RotateAt(theAngle, center);
gw.Transform = matrix;
drawstuff();
}
public void drawstuff()
{
gw.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
gw.DrawEllipse(pen2, rectangle);
gw.DrawRectangle(smallPen, myRect2);
}
}
You can use the Transform matrix to rotate points. So what I've done is get the 4 corner points of myRect2, rotate them using the same Transform matrix, then assign these to a rectangle. You can then use this new rectangle to check the location. I also changed the call to drawstuff() at the end of the right button click to call rotate(), this way the rotated rectangle can get updated when the ellipse is first placed, and the graphics transform angle gets updated to 0.
public partial class Form1 : Form
{
int theAngle = 0;
Pen pen2 = new Pen(Color.FromArgb(255, 68, 125, 255), 4);
Pen pen3 = new Pen(Color.Green, 4);
Pen smallPen = new Pen(Color.Black, 1);
PointF center = new PointF(0, 0);
Rectangle rectangle = new Rectangle(20, 20, 100, 30);
Rectangle myRect2 = new Rectangle();
Rectangle rotatedRect2 = new Rectangle();
bool mouseBtnDown = false;
Graphics gw;
public Form1()
{
InitializeComponent();
this.MouseDown += mouseDown;
gw = this.CreateGraphics();
}
private void mouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (rotatedRect2.Contains(e.Location))
theAngle += 90;
rotate();
}
if (e.Button == MouseButtons.Right)
{
//oval = false;
//mouseBtnDown = false;
theAngle = 0;
rectangle.X = e.X - 50;
rectangle.Y = e.Y - 15;
rectangle.Width = 100;
rectangle.Height = 30;
center.X = rectangle.Left + (0.5f * rectangle.Width);
center.Y = rectangle.Top + (0.5f * rectangle.Height);
myRect2.Size = new Size(15, 15);
myRect2.Location = new Point(rectangle.Left - 15, (rectangle.Top - (rectangle.Height / 2)) + 15);
rotate();
//drawstuff();
// Invalidate();
}
}
public void rotate()
{
var matrix = new Matrix();
matrix.RotateAt(theAngle, center);
gw.Transform = matrix;
// Get the 4 corner points of myRect2.
Point p1 = new Point(myRect2.X, myRect2.Y),
p2 = new Point(myRect2.X + myRect2.Width, myRect2.Y),
p3 = new Point(myRect2.X, myRect2.Y + myRect2.Height),
p4 = new Point(myRect2.X + myRect2.Width, myRect2.Y + myRect2.Height);
Point[] pts = new Point[] { p1, p2, p3, p4 };
// Rotate the 4 points.
gw.Transform.TransformPoints(pts);
// Update rotatedRect2 with those rotated points.
rotatedRect2.X = pts.Min(pt => pt.X);
rotatedRect2.Y = pts.Min(pt => pt.Y);
rotatedRect2.Width = pts.Max(pt => pt.X) - pts.Min(pt => pt.X);
rotatedRect2.Height = pts.Max(pt => pt.Y) - pts.Min(pt => pt.Y);
drawstuff();
}
public void drawstuff()
{
gw.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
gw.DrawEllipse(pen2, rectangle);
gw.DrawRectangle(smallPen, myRect2);
}
}
Something to note. Drawing to the screen like this (using the form's created graphics and drawing using your own draw function) means the ellipses/rectangles are only drawn once. i.e. if you draw a few then minimize your form, when you bring it back up the ellipses will be gone. Not sure if this is what you're after or not. One way to fix this would be to draw the ellipses to a Bitmap and then in the form's Paint event this bitmap is draw to the form.
I am getting
System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.
every time I want to remove an item from the array, so I could draw (fill) another rectangle at that place.
Everything is happening on MouseDown event on right click black rectangle should be drawn (filled) and on left white one.
I made 2 lists of rectangles, one for white ones, and one for black ones.
I am getting exception thrown when I try to draw (fill) rectangle of opposite color of the one that is.
Everything is written into a bitmap and later bitmap is draw as an image in Paint event.
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
using (Graphics rectGraphics = Graphics.FromImage(rectBitmap))
{
rBlack = new Rectangle((e.X / 20) * 20, (e.Y / 20) * 20, 20, 20);
rectGraphics.SmoothingMode = SmoothingMode.HighSpeed;
foreach (Rectangle r in whiteRectangles) // place where exception is thrown if I want to fill black rectangle on the place where white is
{
if (r.X - 1 == rBlack.X && r.Y - 1 == rBlack.Y)
{
int index = whiteRectangles.IndexOf(r);
whiteRectangles.RemoveAt(index);
}
rectGraphics.FillRectangle(brushWhite, r);
}
blackRectangles.Add(rBlack);
foreach (Rectangle r in blackRectangles)
{
rectGraphics.FillRectangle(brushBlack, r);
}
}
}
if (e.Button == MouseButtons.Left)
{
using (Graphics rectGraphics = Graphics.FromImage(rectBitmap))
{
rWhite = new Rectangle((e.X / 20) * 20 +1, (e.Y / 20) * 20 +1, 19, 19);
rectGraphics.SmoothingMode = SmoothingMode.HighSpeed;
foreach (Rectangle r in blackRectangles) // place where exception is thrown if I try to fill white rectangle on the place where black is
{
if (r.X + 1 == rWhite.X && r.Y + 1 == rWhite.Y)
{
int index = blackRectangles.IndexOf(r);
blackRectangles.RemoveAt(index);
}
rectGraphics.FillRectangle(brushBlack, r);
}
whiteRectangles.Add(rWhite);
foreach (Rectangle r in whiteRectangles)
{
rectGraphics.FillRectangle(brushWhite, r);
}
}
}
this.Refresh();
}
You probably need to use a for loop instead of a foreach. Set your upper limit using the Count of the list, then delete by index.
for (int i = 0; i < list.Count; i++)
{
//Delete list[i] here
}
Or, in reverse
for (int i = list.Count - 1; i >= 0; i--)
{
//Delete list[i] here
}
I'm drawing a rectangle with the mouse and now i want that while i'm drawing the rectangle it will draw points on each of the rectangle edges bottom,top,keft,right.
This is how it look like when i'm drawing just the rectangle:
And i want that while i'm drawing the rectangle in real time to add/pad each edge of the rectangle with X number of points. For example on each edge 10 green points with exactly space between them.
For example i added the points in paint just to show what i mean:
Just the green points should be in the thick of the red lines of the rectangle and on the red lines.
The green points should be filled.
And there should be exact space between the points.
I just drawed some points on the top but it should be on the left bottom and right too.
This is how i draw now the regular rectangle just the rectangle.
In the top of form1:
private Bitmap _bmpBU = null;
public static Rectangle mRect;
In the constructor:
this.DoubleBuffered = true;
_bmpBU = new Bitmap(#"D:\MyWeatherStation-Images-And-Icons\radar090.PNG");
pictureBox1 mouse move event:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
pictureBox1.Invalidate();
}
}
pictureBox1 mouse down event:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
Image iOLd = this.pictureBox1.Image;
Bitmap bmp = (Bitmap)_bmpBU.Clone();
this.pictureBox1.Image = bmp;
if (iOLd != null)
iOLd.Dispose();
pictureBox1.Invalidate();
}
And the pictureBox1 paint event:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, mRect);
}
}
I need that somehow in the paint event while it's drawing the rectangle it will also pad and add the green points to each edge.
This is just math. Add this function:
private void DrawPointsOnRectangle(Graphics g, int numberOfPoints)
{
var brush = new SolidBrush(Color.DarkGreen);
const int rectanglePenWidth = 2;
//North & South
int spacing = mRect.Width / (numberOfPoints - 1);
for (int x = 0; x < numberOfPoints; x++)
{
g.FillEllipse(brush, mRect.X + (x * spacing) - rectanglePenWidth - 5, mRect.Y - 7, 15, 15);
g.FillEllipse(brush, mRect.X + (x * spacing) - rectanglePenWidth - 5, mRect.Y - 7 + mRect.Height, 15, 15);
}
//East & West
spacing = mRect.Height/(numberOfPoints - 1);
for (int y = 0; y < numberOfPoints; y++)
{
g.FillEllipse(brush, mRect.X - rectanglePenWidth - 5, mRect.Y - 7 + (y * spacing), 15, 15);
g.FillEllipse(brush, mRect.X - rectanglePenWidth - 5 + mRect.Width, mRect.Y - 7 + (y * spacing), 15, 15);
}
}
And modify your pictureBox1_Paint function to call the new function:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, mRect);
DrawPointsOnRectangle(e.Graphics, 5);
}
}
That should do it! You can change the 5 parameter to however many points you want on each side.
private Point RectStartPoint;
private Image img;
private Image imgClone;
private Pen myPen;
private n = 10; //number of points
You have to initialize those objects, maybe in:
public Form1()
{
InitializeComponent();
myPen = new Pen(Brushes.Red, 2);
//Bitmap to hold the picturebox image
img = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g;
using (g = Graphics.FromImage(img))
{
g.DrawImage(imageOfPicturebox, 0, 0, pictureBox1.Width, pictureBox1.Height);
}
//image to hold the original picturebox. We need it to clear img to the original
//picturebox image
imgClone = (Bitmap)img.Clone();
//We draw always on img and then we invalidate
pictureBox1.Image = img;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
RectStartPoint = e.Location;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && e.Location != RectStartPoint)
{
DrawRectangle(e.Location);
}
}
private void DrawRectangle(Point pnt)
{
Graphics g = Graphics.FromImage(img);
int width, height, i, x, y;
g.SmoothingMode = SmoothingMode.AntiAlias;
//Clear img from the rectangle we drawn previously
g.DrawImage(imgClone, 0, 0);
if (pnt.X == RectStartPoint.X || pnt.Y == RectStartPoint.Y)
{
g.DrawLine(myPen, RectStartPoint.X, RectStartPoint.Y, pnt.X, pnt.Y);
}
else
{
g.DrawRectangle(myPen, Math.Min(RectStartPoint.X, pnt.X), Math.Min(RectStartPoint.Y, pnt.Y),
Math.Abs(RectStartPoint.X - pnt.X), Math.Abs(RectStartPoint.Y - pnt.Y));
//width of spaces between points
width = (int)((Math.Abs(RectStartPoint.X - pnt.X)) / (n - 1));
//height of spaces between points
height = (int)((Math.Abs(RectStartPoint.Y - pnt.Y)) / (n - 1));
//we always want the upper left x, y coordinates as a reference drawing clockwise
x = Math.Min(RectStartPoint.X, pnt.X);
y = Math.Min(RectStartPoint.Y, pnt.Y);
//Drawing the points. change the 3, 6 values for larger ones
for (i = 0; i < n - 1; i++)
{
//Up side
g.FillEllipse(Brushes.Green, new Rectangle(x - 3, Math.Min(RectStartPoint.Y, pnt.Y) - 3, 6, 6));
//Right side
g.FillEllipse(Brushes.Green, new Rectangle(Math.Min(RectStartPoint.X, pnt.X) + Math.Abs(RectStartPoint.X - pnt.X) - 3, y - 3, 6, 6));
//Bottom side
g.FillEllipse(Brushes.Green, new Rectangle(x - 3, Math.Min(RectStartPoint.Y, pnt.Y) + Math.Abs(RectStartPoint.Y - pnt.Y) - 3, 6, 6));
//Left side
g.FillEllipse(Brushes.Green, new Rectangle(Math.Min(RectStartPoint.X, pnt.X) - 3, y - 3, 6, 6));
x += width;
y += height;
}
g.FillEllipse(Brushes.Green, new Rectangle(Math.Min(RectStartPoint.X, pnt.X) + Math.Abs(RectStartPoint.X - pnt.X) - 3,
Math.Min(RectStartPoint.Y, pnt.Y) - 3, 6, 6));
g.FillEllipse(Brushes.Green, new Rectangle(Math.Min(RectStartPoint.X, pnt.X) + Math.Abs(RectStartPoint.X - pnt.X) - 3,
Math.Min(RectStartPoint.Y, pnt.Y) + Math.Abs(RectStartPoint.Y - pnt.Y) - 3, 6, 6));
}
g.Dispose();
//draw img to picturebox
pictureBox1.Invalidate();
}
In the top of form1 i did:
GraphicsPath gp = new GraphicsPath();
GraphicsPath redgp = new GraphicsPath();
Point p;
Then in pictureBox1 move event i did:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
label4.Visible = true;
label4.Text = String.Format("X: {0}; Y: {1}", e.X, e.Y);
if (panning)
{
movingPoint = new Point(e.Location.X - startingPoint.X,
e.Location.Y - startingPoint.Y);
pictureBox1.Invalidate();
}
if (checkBox2.Checked && e.Button == MouseButtons.Left)
{
gp.AddLine(e.X * xFactor, e.Y * yFactor, e.X * xFactor, e.Y * yFactor);//e.Location, e.Location);
redgp.AddEllipse((e.X) * xFactor, (e.Y) * yFactor, 3f, 3f);
p = e.Location;
pictureBox2.Invalidate();
}
}
Then in pictureBox2 paint event:
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
if (!checkBox1.Checked)
{
Pen p;
p = new Pen(Brushes.Green);
foreach (PointF pt in points)
{
e.Graphics.FillEllipse(Brushes.Red, pt.X, pt.Y, 3f, 3f);
}
for (int i = 0; i < points.Count - 1; i++)
{
if (points.Count > 1)
{
e.Graphics.DrawLine(p, points[i].X, points[i].Y, points[i + 1].X, points[i + 1].Y);
}
}
if (checkBox2.Checked)
{
using (Pen pp = new Pen(Color.Green, 2f))
{
pp.StartCap = pp.EndCap = LineCap.Round;
pp.LineJoin = LineJoin.Round;
e.Graphics.DrawPath(pp, gp);
}
using (Pen pp = new Pen(Color.Red, 2f))
{
pp.StartCap = pp.EndCap = LineCap.Round;
pp.LineJoin = LineJoin.Round;
e.Graphics.DrawPath(pp, redgp);
}
}
}
}
When checkbox2 checked and i click on the mouse left button pressed and then move the mouse around on picturebox1 it will draw a line in pictureBox2.
The line in pictureBox2 is in green.
I want that while im moving the mouse on picturebox1 it will draw the line in green on pictureBox2 and also automatic each 10 pixels space in picutreBox2 it will draw a point automatic on the green line.
So i added this code to the pictureBox1 move event:
redgp.AddEllipse((e.X) * xFactor, (e.Y) * yFactor, 3f, 3f);
And in the picturebox2 paint event:
using (Pen pp = new Pen(Color.Red, 2f))
{
pp.StartCap = pp.EndCap = LineCap.Round;
pp.LineJoin = LineJoin.Round;
e.Graphics.DrawPath(pp, redgp);
}
But what it does is just drawing wider line in Red in pictureBox2 on or near the green line. not what i wanted it to do.
How can i solve it ?
Solved it by this way.
Added to top of form1 a variable counter.
Then picturebox1 mouse move event:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
label4.Visible = true;
label4.Text = String.Format("X: {0}; Y: {1}", e.X, e.Y);
if (panning)
{
movingPoint = new Point(e.Location.X - startingPoint.X,
e.Location.Y - startingPoint.Y);
pictureBox1.Invalidate();
}
if (checkBox2.Checked && e.Button == MouseButtons.Left)
{
gp.AddLine(e.X * xFactor, e.Y * yFactor, e.X * xFactor, e.Y * yFactor);
pixelscounter += 1;
if (pixelscounter == 10)
{
redgp.AddEllipse((e.X) * xFactor, (e.Y) * yFactor, 3f, 3f);
pixelscounter = 0;
}
p = e.Location;
pictureBox2.Invalidate();
}
}
I make the counter to go up by 1.
If its 10 create a red point. And reset the counter to 0 again.
The line is Wider (Thick) because you have used PEN with 2f.
It Only Shows you RED as you are drawing two line of same thickness on same point,so what happens here is that you can only see the last line you drawn. In your case it is RED.
If you want both line to appear here then ;
User green Pen as it is:
using (Pen pp = new Pen(Color.Green, 2f))
But make red pen Thinner :
using (Pen pp = new Pen(Color.Red, 1f))
So, it will draw RED line On Green Line.
Hope it Helps!
I need to be able to draw a polygon using mouse click locations.
Here is my current code:
//the drawshape varible is called when a button is pressed to select use of this tool
if (DrawShape == 4)
{
Point[] pp = new Point[3];
pp[0] = new Point(e.Location.X, e.Location.Y);
pp[1] = new Point(e.Location.X, e.Location.Y);
pp[2] = new Point(e.Location.X, e.Location.Y);
Graphics G = this.CreateGraphics();
G.DrawPolygon(Pens.Black, pp);
}
Thanks
Ok here is some sample code:
private List<Point> polygonPoints = new List<Point>();
private void TestForm_MouseClick(object sender, MouseEventArgs e)
{
switch(e.Button)
{
case MouseButtons.Left:
//draw line
polygonPoints.Add(new Point(e.X, e.Y));
if (polygonPoints.Count > 1)
{
//draw line
this.DrawLine(polygonPoints[polygonPoints.Count - 2], polygonPoints[polygonPoints.Count - 1]);
}
break;
case MouseButtons.Right:
//finish polygon
if (polygonPoints.Count > 2)
{
//draw last line
this.DrawLine(polygonPoints[polygonPoints.Count - 1], polygonPoints[0]);
polygonPoints.Clear();
}
break;
}
}
private void DrawLine(Point p1, Point p2)
{
Graphics G = this.CreateGraphics();
G.DrawLine(Pens.Black, p1, p2);
}
First, add this code:
List<Point> points = new List<Point>();
On the object you are drawing on, capture the OnClick event. One of the arguments should have the X and Y coordinates of the click. Add them to the points array:
points.Add(new Point(xPos, yPos));
And then finally, where you're drawing the lines, use this code:
if (DrawShape == 4)
{
Graphics G = this.CreateGraphics();
G.DrawPolygon(Pens.Black, points.ToArray());
}
EDIT:
Ok, so the above code isn't exactly correct. First of all, its most likely a Click event instead of a OnClick event. Second, To get the mouse position, you need two variables declared up with the points array,
int x = 0, y = 0;
Then have a mouse move event:
private void MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
}
Then, in your Click event:
private void Click(object sender, EventArgs e)
{
points.Add(new Point(x, y));
}