Hello I have got this code:
private Label newLabel = new Label();
Int32 mouseX;
Int32 mouseY;
private void form_MouseMove(object sender, MouseEventArgs e)
{
mouseY = Cursor.Position.Y;
mouseX = Cursor.Position.X;
}
private void button1_Click(object sender, EventArgs e)
{
int txt = Int32.Parse(textBox1.Text);
for (int i = 0; i < txt; i++)
{
newLabel = new Label();
newLabel.Location = new Point(mouseY, mouseX);
newLabel.Size = new System.Drawing.Size(25, 25);
newLabel.Text = i.ToString();
newLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
newLabel.ForeColor = Color.Red;
newLabel.Font = new Font(newLabel.Font.FontFamily.Name, 10);
newLabel.Font = new Font(newLabel.Font, FontStyle.Bold);
newLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
newLabel.MouseMove += new MouseEventHandler(this.MyControl_MouseMove);
newLabel.MouseDown += new MouseEventHandler(this.MyControl_MouseDown);
this.Controls.Add(newLabel);
}
}
And I try to make it create a label according to where the mouse is, but it seems that it is creating with location within the whole display. I thought that if I assign the coordinates to form mouse move it would get coordinates within the form.
May someone help me solve this out please?
The Cursor.Position coordinate is relative to the whole screen. You need a position relative to the upper left of your form. You could simply get that info from the MouseEventArgs passed to your MouseMove event handler
private void form_MouseMove(object sender, MouseEventArgs e)
{
mouseY = e.Location.Y;
mouseX = e.Location.X;
}
The MouseEventArgs.Location property is (according to MSDN)
A Point that contains the x- and y- mouse coordinates, in pixels,
relative to the upper-left corner of the form.
Steve is correct, and in order to convert screen coordinates to control or form coordinates you can use method described here:
How to convert screen coordinates to form relative coordinates (winforms)?
In your case:
Point clientPoint = PointToClient( new Point( e.X, e.Y ) );
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:
Now my purpose is that after I draw a rectangle I want to clear it. But I don't know how to do this !
So I have try to search on the stackoverflow ,but they seems not meet my questions.
and now what my code functions is draw a rectangle on the imagebox , when the mouse down , it give the start point to the rectangle also the status of mouse move! And when the mouse up , it would have the endpoint .
SO using
{Draw(Rectangle,TColor,Int32,LineType,Int32);}
then it would generate a new rectangle;
but I don't know how to remove the rectangle after that
here my code
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)//mousedown
{
if (img != null)
{
mouseStatus = true;
startPoint.X = e.X;
startPoint.Y = e.Y;
//A new rectangle resets in a new coordinate
minStartX = e.X;
minStartY = e.Y;
maxEndX = e.X;
maxEndY = e.Y;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)//mousemove
{
string StringX, StringY, StringSX, StringSY;
if (mouseStatus)
{
endPoint.X = e.X;
endPoint.Y = e.Y;
//This section is to get the top and bottom left coordinates of the rectangle to be drawn. If not, the rectangle can only be drawn from the top left to the bottom right.
int realStartX = Math.Min(startPoint.X, endPoint.X);
int realStartY = Math.Min(startPoint.Y, endPoint.Y);
int realEndX = Math.Max(startPoint.X, endPoint.X);
int realEndY = Math.Max(startPoint.Y, endPoint.Y);
minStartX = Math.Min(minStartX, realStartX);
minStartY = Math.Min(minStartY, realStartY);
maxEndX = Math.Max(maxEndX, realEndX);
maxEndY = Math.Max(maxEndY, realEndY);
currRect = new Rectangle(realStartX, realStartY, realEndX - realStartX, realEndY - realStartY);
StringX = Convert.ToString(realStartX);
StringY = Convert.ToString(realStartY);
StringSX = Convert.ToString(realEndX - realStartX);
StringSY = Convert.ToString(realEndY - realStartY);
textBox1.Text = StringX;
textBox2.Text = StringY;
textBox3.Text = StringSX;
textBox4.Text = StringSY;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)/mouseup
{
mouseStatus = false;
endPoint.X = e.X;
endPoint.Y = e.Y;
img.Draw(currRect, new Bgr(Color.Red), 1);
pictureBox1.Image = img.Bitmap;
}
I'm drawing a line in a picturebox this way:
horizontalstart = new Point(0, e.Y); //Start point of Horizontal line.
horizontalend = new Point(picbox_mpr.Width, e.Y); //End point of Horizontal line.
verticalstart = new Point(e.X, 0); //Start point of Vertical line
verticalend = new Point(e.X, picbox_mpr.Height); //End point of Vertical line.
Then on the paint event I do this:
e.Graphics.DrawLine(redline, horizontalstart, horizontalend); //Draw Horizontal line.
e.Graphics.DrawLine(redline, verticalstart, verticalend); //Draw Vertical line.
Pretty simple, now, my image can zoom and here's where I struggle..
How do I keep the line in the same spot that was drawn even if I zoom the image?
Instead of storing an absolute integer coordinate, store a decimal value representing the "percentage" of that coord with respect to the width/height of the image. So if the X value was 10 and the width was 100, you store 0.1. Let's say the image was zoomed and it was now 300 wide. The 0.1 would now translate to 0.1 * 300 = 30. You can store the "percentage" X,Y pairs in PointF() instead of Point().
Here's a quick example to play with:
public partial class Form1 : Form
{
private List<Tuple<PointF, PointF>> Points = new List<Tuple<PointF, PointF>>();
public Form1()
{
InitializeComponent();
this.Shown += new EventHandler(Form1_Shown);
this.pictureBox1.BackColor = Color.Red;
this.pictureBox1.SizeChanged += new EventHandler(pictureBox1_SizeChanged);
this.pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
}
void Form1_Shown(object sender, EventArgs e)
{
// convert absolute points:
Point ptStart = new Point(100, 25);
Point ptEnd = new Point(300, 75);
// to percentages:
PointF ptFstart = new PointF((float)ptStart.X / (float)pictureBox1.Width, (float)ptStart.Y / (float)pictureBox1.Height);
PointF ptFend = new PointF((float)ptEnd.X / (float)pictureBox1.Width, (float)ptEnd.Y / (float)pictureBox1.Height);
// add the percentage point to our list:
Points.Add(new Tuple<PointF, PointF>(ptFstart, ptFend));
pictureBox1.Refresh();
}
private void button1_Click(object sender, EventArgs e)
{
// increase the size of the picturebox
// watch how the line(s) change with the changed picturebox
pictureBox1.Size = new Size(pictureBox1.Width + 50, pictureBox1.Height + 50);
}
void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (Tuple<PointF, PointF> tup in Points)
{
// convert the percentages back to absolute coord based on the current size:
Point ptStart = new Point((int)(tup.Item1.X * pictureBox1.Width), (int)(tup.Item1.Y * pictureBox1.Height));
Point ptEnd = new Point((int)(tup.Item2.X * pictureBox1.Width), (int)(tup.Item2.Y * pictureBox1.Height));
e.Graphics.DrawLine(Pens.Black, ptStart, ptEnd);
}
}
}
I'm trying to draw out pictureboxes during runtime as I can do right from the toolbox. That is, set the location at the mouselocation, resize it as I hold down the button and drag it across the form. All that I've accomplished in the code. But as I start the draw the second picturebox the first one disappears, I want to keep adding more pictureboxes to the form, if I remove the MouseMove event and move PictureBox pb1 = new PictureBox(); down to the MouseDown event it lets me add more buttons, but then I can't resize them obviously.
int cellSize = 10;
int numOfCells = 500;
PictureBox pb1 = new PictureBox();
int Mx, My;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
}
public void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Point p = new Point(e.X, e.Y);
Mx = p.X;
My = p.Y;
int xSnap = (Mx / cellSize) * cellSize;
int ySnap = (My / cellSize) * cellSize;
pb1.BackColor = (Color.Red);
if (e.Button == MouseButtons.Left)
{
pb1.Size = new Size(xSnap - pb1.Left, ySnap - pb1.Top);
}
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
Point p = new Point(e.X, e.Y);
Mx = p.X;
My = p.Y;
int xSnap = (Mx / cellSize) * cellSize;
int ySnap = (My / cellSize) * cellSize;
pb1.Location = new Point(xSnap, ySnap);
pictureBox1.Controls.Add(pb1);
}
You're always re-using the same PictureBox instance.
You need to create a new instance every time you want to add a new one, by writing new PictureBox().