I'm currently working on a battleships game,but i've ran into an issue.So far i've managed to draw the grid.the purpose of the draw method is to draw an image(don't know how to/if i can color a certain surface) inside a square in the grid,when i left-click.
the problem here is that,even if the image's size is 25x25(the size of a square) it occupies like half the screen,and that's when it works.50% of the times when i run nothing happens,and the other 50% it draws a huge image in the middle of the screen,regardless of where the cursor is located or if i left-click.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(form1_paint);
}
private void form1_paint(object sender, PaintEventArgs e)
{
draw(e);
}
int x,y;
private void draw(PaintEventArgs e)
{
if (MouseButtons.Left != 0)
{
x = Cursor.Position.X;
y = Cursor.Position.Y;
Image poza = Image.FromFile("D://C//12E//c#//yellow4.jpg");
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(poza, coltz);
}
}
}
Does anyone know how i can solve this?or if someone has a better idea for a battleships grid, I am open to suggestions.Thanks!
Fisrt of all, this line of code: Cursor.Position.X gives you the global position of the cursor on the screen, not in the game window. I suggest you to handle MouseMove event to get the position relative to the content of your app.
The second thing is that you are loading the image from file on your computer. I think it's better to add the image to your app's resources, so you can load it easier just calling it by name, e.g.: AppName.Properties.Resources.ImageName - it returns Image object you can immediately use.
One more thing. This if (MouseButtons.Left != 0) won't check whether left mouse button is pressed or not. You have to check if MouseButtons property equals System.Windows.Forms.MouseButtons.Left.
Here's the full code that works for me:
public partial class Form1 : Form
{
private int x, y;
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
MouseDown += Form1_MouseMove;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Draw(e);
}
private void Draw(PaintEventArgs e)
{
if (MouseButtons == System.Windows.Forms.MouseButtons.Left)
{
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(AppName.Properties.Resources.ImageName, coltz);
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
Invalidate();
}
}
And here's the result:
I also subscribed MouseDown event to show yellow rectangle when user clicks the button without moving the cursor.
the thing is,before adding your code,the draw worked fine.now,every time i move the mouse,the grid gets redrawn,and it looks like it's constantly refreshing,bleeping somehow.not sure how to phrase this
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
MouseDown += Form1_MouseMove;
}
private int x,y;
void Form1_Paint(object sender, PaintEventArgs e)
{
Draw(e);
}
private void Draw(PaintEventArgs e)
{
if (MouseButtons == System.Windows.Forms.MouseButtons.Left)
{
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(battleships.Properties.Resources.yellow4, coltz);
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g;
g = e.Graphics;
Pen pen = new Pen(Color.Black);
pen.Width = 1;
for (int i = 25; i <= 300; i = i + 25)
{
g.DrawLine(pen, i, 25, i, 300);
g.DrawLine(pen, 25, i, 300, i);
}
}
}
Related
How to move a pictureBox inside a Panel by Mouse.
Visual Studio 2015 C# Winsows Forms Application.
I've made a primitive slider to control the volume of my WindowsMediaPlayer.
A panel as the background and a pictureBox inside as the slider-knopf.
And it works well.
But purely visually it does not work that good.
I'v searched all around, but can't I find an answer to this little funny problem.
Here is my code:
int posY;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
posY = e.Y; ;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
PictureBox box = sender as PictureBox;
if (e.Button == MouseButtons.Left)
{
box.Top += e.Y - posY;
}
if (box.Top < 0)
{
box.Top = 0;
}
if (box.Top > 100)
{
box.Top = 100;
}
int n = box.Top;
n = n * - 1 + 100;
label1.Text = n.ToString();
}
When I move the pictureBox out of the edge of the little panel, the pictureBox somehow 'shrinks' in the panel.
But when I release the mouse, the pictureBox restore its size.
Slider.gif
Why is that.?
And how can I avoid it.?
Thanks.
I found a solution.
It's not optimal, but it can be used.
I changed the code to:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragging = true;
startPoint = e.Location;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
Debug.WriteLine("mousemove X: " + e.X + " Y: " + e.Y);
pictureBox1.Location = new Point(0, pictureBox1.Top + e.Location.Y - startPoint.Y);
if (pictureBox1.Location.Y < 0)
{
pictureBox1.Location = new Point(0, 0);
dragging = false;
}
if (pictureBox1.Location.Y > 100)
{
pictureBox1.Location = new Point(0, 100);
dragging = false;
}
this.Refresh();
}
int n = pictureBox1.Location.Y;
n = n * -1 + 100;
label1.Text = n.ToString();
mediaPlayer1.settings.volume = n;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
gif
I still need to put an 'if' to correct, when the pictureBox1 is pulled out of the panel.
And to avoid flicker, I have had to put a 'dragging = false'.
However, it results in the pictureBox1 is getting frozen to the edge, so I have to release the mouse, and re-click to continue.
But well - it's to live with.
Thanks.
My program can draw lines using canvas.Drawline(). How to click line and change this color (select line)?
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas=panel1.CreateGraphics();
}
Coordinate line stored in coordFirs & coodLast.
Here is a suitable Line class:
class Line
{
public Color LineColor { get; set; }
public float Linewidth { get; set; }
public bool Selected { get; set; }
public Point Start { get; set; }
public Point End { get; set; }
public Line(Color c, float w, Point s, Point e)
{ LineColor = c; Linewidth = w; Start = s; End = e; }
public void Draw(Graphics G)
{ using (Pen pen = new Pen(LineColor, Linewidth)) G.DrawLine(pen, Start, End); }
public bool HitTest(Point Pt)
{
// test if we fall outside of the bounding box:
if ((Pt.X < Start.X && Pt.X < End.X) || (Pt.X > Start.X && Pt.X > End.X) ||
(Pt.Y < Start.Y && Pt.Y < End.Y) || (Pt.Y > Start.Y && Pt.Y > End.Y))
return false;
// now we calculate the distance:
float dy = End.Y - Start.Y;
float dx = End.X - Start.X;
float Z = dy * Pt.X - dx * Pt.Y + Start.Y * End.X - Start.X * End.Y;
float N = dy * dy + dx * dx;
float dist = (float)( Math.Abs(Z) / Math.Sqrt(N));
// done:
return dist < Linewidth / 2f;
}
}
Define a List for the lines, probably at class level:
List<Line> lines = new List<Line>();
Here is how you can initialize it with a few lines:
for (int i = 0; i < 20; i++) lines.Add(new Line(Color.Black, 4f,
new Point(R.Next(panel1.Width), R.Next(panel1.Height)),
new Point(R.Next(panel1.Width), R.Next(panel1.Height))));
Here is the result of clicking on a crossing:
Whenever you add, change or remove a line you need to make the Panel reflect the news by triggering the Paint event:
panel1.Invalidate();
Here is the Paint event of the Panel:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
foreach (Line L in lines) L.Draw(e.Graphics);
}
In the MouseClick event you do the test:
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
foreach(Line L in lines)
L.LineColor = L.HitTest(e.Location) ? Color.Red : Color.Black;
panel1.Invalidate();
}
To avoid flicker don't use the basic Panel class as it isn't doublebuffered. Instead use either a PictureBox or a Label (with AutoSize=false) or a doublebuffered Panel subclass:
class DrawPanel : Panel
{ public DrawPanel () { DoubleBuffered = true; } }
Notes:
There is no such thing as a 'Line' in WinForms, only pixels of various colors. So to select a line you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking.
The above example shows how to do it in math.
Instead one could test each line by drawing it onto a bitmap and test the pixel the mouse has clicked. But drawing those bitmaps would have to do math behind the scenes as well and also allocate space for the bitmaps, so the math will be more efficient..
Yes the Line class looks a little long for such a simple thing a s a line but look how short all the event codes now are! That's because the responsiblities are where they belong!
Also note the the first rule of doing any drawing in WinForms is: Never cache or store a Grahics object. In fact you shouldn't ever use CreateGraphics in the first place, as the Graphics object will never stay in scope and the graphics it produces will not persist (i.e. survive a Minimize-maximize sequence)..
Also note how I pass out the e.Graphics object of the Paint event's parameters to the Line instances so they can draw themselves with a current Graphics object!
To select thinner lines it may help to modify the distance check a little..
The Math was taken directly form Wikipedia.
You can change the color of everything on click. By using click event of particular object.
I give you an example for button. If you click on button then panal’s color will be change. You can modify the code as per your requirement.
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas = panel1.CreateGraphics();
}
private void panel1_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.Blue;
}
private void nonSelectableButton3_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.BurlyWood;
}
I have a GroupBox that has multiple controls, specifically 4 PictureBoxes.
These are pieces of an image puzzle. Please note that the number of images can change. I want to allow a drag and drop effect to each of them. To mark the source and the destination I did the following:
foreach (var box in boxes)
{
box.DragEnter += new DragEventHandler(box_DragEnter);
box.MouseDown += new MouseEventHandler(box_MouseDown);
box.DragDrop += new DragEventHandler(box_DragDrop);
box.AllowDrop = true;
}
When a box is clicked it will be marked to a global variable (that will be the source) and when a box raises the DragDrop event that will be the destination.
As I previously mentioned, since the number of the boxes can be changed I do not want to add separate methods as handlers for each event.
My issue here is that I do not know how to "detect" which box has raised what event.
Example:
*If I press on the left-top image I want to know that I pressed on it to mark the source (let's say global picSource = 1) and if I drop on right-bottom image I want to know that as a destination (picDestination = 4). All that must occur in the handlers. *
Tests:
Adding a parent panel (same size as the GroupBox) with a Click event & doing a comparison between a Point issued with the X&Y provided by the event handler and a rectangle designated with the X,Y, width, height of the panel.
Adding handlers to the GroupBox (does not work either)
Notes:
e.X and e.Y refer to the position of the mouse
void box_MouseDown(object sender, MouseEventArgs e)
The purpose of this is to switch boxes (source <--> destination)
You can just detect puzzle element by mouse's coordinates in the controls container's MouseDown and MouseUp event as follows:
public partial class PuzzleForm : Form
{
private readonly Image[,] Images;
private readonly int Nx;
private readonly int Ny;
private int sourceIndexX;
private int sourceIndexY;
private int destinationIndexX;
private int destinationIndexY;
private PuzzleForm()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
public PuzzleForm(Image[,] images)
: this()
{
Images = images;
Nx = Images.GetLength(0);
Ny = Images.GetLength(1);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Graphics g = e.Graphics)
{
for (int j = 0; j < Ny; j++)
for (int i = 0; i < Nx; i++)
{
Rectangle rect = new Rectangle(ClientSize.Width * i / Nx, ClientSize.Height * j / Ny, ClientSize.Width / Nx - 1, ClientSize.Height / Ny - 1);
g.DrawImage(Images[i, j], rect);
}
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button != MouseButtons.Left)
return;
sourceIndexX = e.X * Nx / ClientSize.Width;
sourceIndexY = e.Y * Ny / ClientSize.Height;
Cursor = Cursors.Hand;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button != MouseButtons.Left)
return;
destinationIndexX = e.X * Nx / ClientSize.Width;
destinationIndexY = e.Y * Ny / ClientSize.Height;
Cursor = Cursors.Default;
if (sourceIndexX != destinationIndexX || sourceIndexY != destinationIndexY)
{
swapImages();
MessageBox.Show(String.Format("From [{0}, {1}] to [{2}, {3}]", sourceIndexX, sourceIndexY, destinationIndexX, destinationIndexY));
}
}
private void swapImages()
{
Image tmp = Images[sourceIndexX, sourceIndexY];
Images[sourceIndexX, sourceIndexY] = Images[destinationIndexX, destinationIndexY];
Images[destinationIndexX, destinationIndexY] = tmp;
Invalidate();
}
}
Usage:
Image[,] images = new Image[2, 2];
// Fill array with images:
images[0, 0] = Bitmap.FromFile(#"...");
images[0, 1] = Bitmap.FromFile(#"...");
images[1, 0] = Bitmap.FromFile(#"...");
images[1, 1] = Bitmap.FromFile(#"...");
PuzzleForm puzzleForm = new PuzzleForm(images);
// Show form or whatever you want.
...
I have a PictureBox with some graphics drawn, able to zoom by mousewheel. To keep the graphics at the (approximately) same position, not to have to move each time after zooming, I translate the graphics after each zoom. Here is my zooming code:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(pictureBox1.BackColor);
float _step = 1.0f;
if (todo == "zoom out")
{
float step = 0;
if (CurrentRate >= 0.60f) step = 0.05f;
else if (CurrentRate >= 0.40f && CurrentRate < 0.60f) step = 0.025f;
else if (CurrentRate >= 0.05f && CurrentRate < 0.40f) step = 0.0125f;
CurrentRate -= step; // current rate is 1.0 on startup
_step = step;
//pictureBox1.Location = new Point((int)(pictureBox1.Location.X + step * 1500), (int)(pictureBox1.Location.Y + step * 1500));
translateX += step * 10500; //achieved these numbers after few dozens of tries, it actually keeps the graphics at the same position..
translateY += step * 8500;
todo = null;
}
else if (todo == "zoom in")
{
float step = 0;
if (CurrentRate >= 1.80f && CurrentRate <= 1.95f) step = 0.0125f;
else if (CurrentRate >= 0.80f && CurrentRate < 1.80f) step = 0.025f;
else if (CurrentRate >= 0.03f && CurrentRate < 0.80f) step = 0.05f;
CurrentRate += step;
_step = step;
translateX -= step * 10500;
translateY -= step * 8500;
//pictureBox1.Location = new Point((int)(pictureBox1.Location.X - step * 1500), (int)(pictureBox1.Location.Y - step * 1500));
todo = null;
}
e.Graphics.TranslateTransform(translateX, translateY); //move it to keep same position
e.Graphics.ScaleTransform(CurrentRate, CurrentRate); //rescale according to the zoom
//the drawing itself (of everything, also the things mentioned below)
Now, what I am trying to do. The user clicks the picturebox, a small rectangle should be drawn at the click position. When he clicks again, another rectangle is drawn, and the rectangles are connected by a line. And on and on to lets say 50 connected rectangles.
Now, the rectangles connect correctly, but everything is drawn with a horrible offset. I believe this is caused by the translation. So I tried to translate the click coordinates as well:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
//MessageBox.Show("e.Location: " + e.Location.ToString() + "to client e.location: " + PointToClient(e.Location).ToString() + "cursor position: " + Cursor.Position.ToString() + "to client cursor position:" + PointToClient(Cursor.Position).ToString() + "/nto screen cursor position: " + PointToScreen(Cursor.Position).ToString());
if (trackDrawing)
{
Point[] rectanglePos = new Point[1];
rectanglePos[0] = new Point(e.Location.X + (int)(translateX), e.Location.Y + (int)translateY);
drawBuffer.Add(rectanglePos);
drawBuffertype.Add("DRAWTRACKRECTANGLE");
if (trackDrawingBuffer.Count > 0)
{
Point[] linePos = new Point[2];
linePos[0] = trackDrawingBuffer[trackDrawingBuffer.Count - 1];
linePos[1] = new Point(e.Location.X + (int)translateX, e.Location.Y + (int)translateY); ;
drawBuffer.Add(linePos);
drawBuffertype.Add("DRAWTRACKLINE");
}
trackDrawingBuffer.Add(new Point(e.Location.X + (int)translateX, e.Location.Y + (int)translateY));
pictureBox1.Invalidate();
}
//some more unrelated code
But that doesn't work. I have tried also without the translates here at the MouseDown event, but still it draws with offset. I am not quite sure how to describe the behavior properly, so I have done a short vid (about 30s) to explain the offset..
The video
Any ideas? Thank you in advance
**
EDIT
**
Now, after edits done according to the answers, my code looks this:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (trackDrawing)
{
Matrix m = transform.Clone();
m.Invert();
Point[] rectanglePos = new Point[1];
rectanglePos[0] = new Point(e.Location.X - 3, e.Location.Y - 3);
m.TransformPoints(rectanglePos);
drawBuffer.Add(rectanglePos);
drawBuffertype.Add("DRAWTRACKRECTANGLE");
if (trackDrawingBuffer.Count > 0)
{
Point[] linePos = new Point[2];
linePos[0] = trackDrawingBuffer[trackDrawingBuffer.Count - 1];
linePos[1] = new Point(e.Location.X, e.Location.Y );
m.TransformPoints(linePos);
drawBuffer.Add(linePos);
drawBuffertype.Add("DRAWTRACKLINE");
}
trackDrawingBuffer.Add(rectanglePos[0]);
pictureBox1.Invalidate();
}
Now, here the translating part, including the code where I get the matrix offset
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(pictureBox1.BackColor);
transform.Translate(-translateX, -translateY);
float _step = 1.0f;
if (todo == "zoom out")
{
float step = 0;
if (CurrentRate >= 0.60f) step = 0.05f;
else if (CurrentRate >= 0.40f && CurrentRate < 0.60f) step = 0.025f;
else if (CurrentRate >= 0.05f && CurrentRate < 0.40f) step = 0.0125f;
CurrentRate -= step;
_step = step;
translateX += step * 10500;
translateY += step * 8500;
todo = null;
}
else if (todo == "zoom in")
{
float step = 0;
if (CurrentRate >= 1.80f && CurrentRate <= 1.95f) step = 0.0125f;
else if (CurrentRate >= 0.80f && CurrentRate < 1.80f) step = 0.025f;
else if (CurrentRate >= 0.03f && CurrentRate < 0.80f) step = 0.05f;
CurrentRate += step;
_step = step;
//pictureBox1.Scale((1f + step), (1f + step));
translateX -= step * 10500;
translateY -= step * 8500;
todo = null;
}
transform.Translate(translateX, translateY); // transform is the Matrix
e.Graphics.Transform = transform;
e.Graphics.ScaleTransform(CurrentRate, CurrentRate);
and here the drawing itself:
for (int i = 0; i < drawBuffer.Count; i++)
{
//...
else if (drawBuffertype[i].ToUpper().Contains("DRAWTRACKRECTANGLE"))
{
e.Graphics.FillRectangle(new SolidBrush(Color.Red), drawBuffer[i][0].X, drawBuffer[i][0].Y, 6, 6);
}
else if (drawBuffertype[i].ToUpper().Contains("DRAWTRACKLINE"))
{
e.Graphics.DrawLine(new Pen(Color.OrangeRed, 2), drawBuffer[i][0], drawBuffer[i][1]);
}
And still drawing like in the first part of video. I just have to be missing something really basic here...
Not my area of expertise...
...but you can keep a class level Matrix to represent the current state of the "world". You can translate, scale, and/or rotate that Matrix to manipulate the world. Just assign that Matrix to e.Graphics.Transform before drawing everything.
Now, when the user clicks, you can clone that Matrix and Invert() it, allowing you to use its TransformPoints() method. This will convert from the screen coords to the equivalent world coords. Store the converted world coords in a List so you can reuse them in the Paint() event.
Play with this example. Add two buttons to a blank form and wire up their click events to the respective typical method names I've got below. Run it and click a few points on the screen. Now hit the first button to rotate, and/or the second button to zoom in. Now try adding a few more points by clicking some more on the form. Hit the buttons and see what happens. Everything should stay relative to each other (I hope):
public partial class Form1 : Form
{
private Matrix MyMatrix = new Matrix();
private List<Point> Points = new List<Point>();
public Form1()
{
InitializeComponent();
this.WindowState = FormWindowState.Maximized;
this.Shown += new EventHandler(Form1_Shown);
}
void Form1_Shown(object sender, EventArgs e)
{
Point Center = new Point(this.ClientRectangle.Width / 2, this.ClientRectangle.Height / 2);
MyMatrix.Translate(Center.X, Center.Y);
this.MouseDown += new MouseEventHandler(Form1_MouseDown);
this.Paint += new PaintEventHandler(Form1_Paint);
}
void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Transform = MyMatrix;
// draw the origin in the center of the form:
e.Graphics.DrawLine(Pens.Red, new Point(-10, 0), new Point(10, 0));
e.Graphics.DrawLine(Pens.Red, new Point(0, -10), new Point(0, 10));
// draw our stored points (that have already been converted to world coords)
foreach (Point pt in Points)
{
Rectangle rc = new Rectangle(pt, new Size(1, 1));
rc.Inflate(10, 10);
e.Graphics.DrawRectangle(Pens.Black, rc);
}
}
void Form1_MouseDown(object sender, MouseEventArgs e)
{
Matrix m = MyMatrix.Clone();
m.Invert();
Point[] pts = new Point[] {new Point(e.X, e.Y)};
m.TransformPoints(pts);
Points.Add(pts[0]);
this.Refresh();
}
private void button1_Click(object sender, EventArgs e)
{
MyMatrix.Rotate(10);
this.Refresh();
}
private void button2_Click(object sender, EventArgs e)
{
MyMatrix.Scale(1.1f, 1.1f);
this.Refresh();
}
}
Maybe this simple code can help you. This is all done without translation or scaling of any kind. But if you apply scaling to your graphics, it will work the same.
this.pictureBox1.Scale(new SizeF(2.5f, 2.5f));
So...
Actually I defined the side of the rectangle to 15 unit wide.
private int RectSideLen = 15;
So every time I click in the picture box I assume that I click the center of my rectangle to be drawn. This mean that our rectangle will start at the click location minus half rectangle side.
int cornerOffset = RectSideLen / 2;
Point newUpLeftCorner = e.Location;
newUpLeftCorner.Offset(-cornerOffset, -cornerOffset);
Then I add it to a list of rectangle and refresh the picture box to redraw it with the new rectangle added.
pictureBox1.Refresh();
and inside the paint event of the picture box I simply draw the precalculated rectangle.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 1))
{
foreach (Rectangle r in DrawBuffer)
{
e.Graphics.DrawRectangle(pen, r);
}
}
}
So here is the complete sample.
public partial class Form1 : Form
{
private int RectSideLen = 15;
private IList<Rectangle> DrawBuffer = new List<Rectangle>();
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int cornerOffset = RectSideLen / 2;
Point newUpLeftCorner = e.Location;
newUpLeftCorner.Offset(-cornerOffset, -cornerOffset);
DrawBuffer.Add(new Rectangle(newUpLeftCorner, new Size(RectSideLen, RectSideLen)));
pictureBox1.Refresh();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Red, 1))
{
foreach (Rectangle r in DrawBuffer)
{
e.Graphics.DrawRectangle(pen, r);
}
}
}
}
Any suggestions how to create a line by clicking two new points then draw a line between them?
I am trying to create a distance tool like the one in adobe acrobat.
Image Example
Problem Solved!
EDIT:
Here's the code:
private Point p1, p2;
List<Point> p1List = new List<Point>();
List<Point> p2List = new List<Point>();
private void Panel1_MouseDown(object sender, MouseEventArgs e)
{
if (p1.X == 0)
{
p1.X = e.X;
p1.Y = e.Y;
}
else
{
p2.X = e.X;
p2.Y = e.Y;
p1List.Add(p1);
p2List.Add(p2);
Invalidate();
p1.X = 0;
}
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
using(var p = new Pen(Color.Blue, 4))
{
for(int x = 0; x<p1List.Count; x++){
e.Graphics.DrawLine(p, p1List[x], p2List[x]);
}
}
}
You can handle the mouse click event on the panel (for example) and retrieve the location of the click (using the event args). Store this location in an attribute. Do that for as many points as you need.
In the panel paint event, call the parent paint, then draw the lines between your points.
Something like this should do it:
Point firstPoint;
Point seondPoint;
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (this.firstPoint == null) {
this.firstPoint = e.Location;
}
if (this.secondPoint == null) {
this.secondPoint = e.Location;
}
panel1.Invalidate();
}
private void panel1_Paint_1(object sender, PaintEventArgs e)
{
Using (pn as new Pen(Color.Blue, 5))
{
e.Graphics.DrawLine(pn, firstPoint, secondPoint);
}
}
EDIT: You also dont need to do CreateGraphics to draw the line - in the Paint event you have a graphics object already.