I'm currently making simple programs to draw line on picturebox. Click for the first point then drag & release for the 2nd point.
Now I want to make that line can be edited rather than making new one. like when click & drag on the edge it will resized to the new drop point and when click & drag on the body it will move the entire item without changing the shape.
the current picturebox code is like this
public Form1()
{
InitializeComponent();
pictureBox1.BackgroundImage = Image.FromFile("C:/Users/RPC1940/Pictures/500px.jpg");
DrawArea = new Bitmap(pictureBox1.Size.Width, pictureBox1.Size.Height);
pictureBox1.Image = DrawArea;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen pen = new Pen(Color.Black, 3);
g.DrawLine(pen,x1,y1,x2,y2);
pen.Dispose();
pictureBox1.Refresh();
this.DoubleBuffered = true;
}
and the current mouse click & drag to draw line is like this
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (drawRadio.Checked == true)
{
x1 = e.Location.X;
y1 = e.Location.Y;
x1Box.Text = x1.ToString();
y1Box.Text = y1.ToString();
}
else if (editRadio1.Checked == true)
{
x1 = e.Location.X;
y1 = e.Location.Y;
x1Box.Text = x1.ToString();
y1Box.Text = y1.ToString();
pictureBox1.Refresh();
}
else if(editRadio2.Checked == true)
{
x2 = e.Location.X;
y2 = e.Location.Y;
x2Box.Text = x2.ToString();
y2Box.Text = y2.ToString();
pictureBox1.Refresh();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if(drawRadio.Checked == true)
{
x2 = e.Location.X; //Second x coordinate (mouse release)
y2 = e.Location.Y; //Second y coordinate (mouse release)
x2Box.Text = x2.ToString();
y2Box.Text = y2.ToString();
}
}
Anyone know how to edit the lines that has drawn in the box by dragging the edge or body of it? Thank you very much
**Edit
added few radio box. drawRadio is default to draw the line, editRadio1 & editRadio2 for edit the coordinates of point 1 & 2. there's 1 more radio box to move the etire line shape but I dont know how is it works yet. if anyone know please tell me. Thanks
Related
I have an X-Y plot in a .NET 4.0 WinForms chart control. I am trying to implement rubber-band selection, so that the user could click and drag the mouse to create a rectangle on the plot, thus selecting all the points that lie within this rectangle.
While I was able to code up the drawing of the rectangle, I am now trying to identify the Datapoints that lie within this rectangle. Here is the relevant code:
public partial class Form1 : Form
{
System.Drawing.Point _fromPosition;
Rectangle _selectionRectangle;
public Form1()
{
InitializeComponent();
}
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
// As the mouse moves, update the dimensions of the rectangle
if (e.Button == MouseButtons.Left)
{
Point p = e.Location;
int x = Math.Min(_fromPosition.X, p.X);
int y = Math.Min(_fromPosition.Y, p.Y);
int w = Math.Abs(p.X - _fromPosition.X);
int h = Math.Abs(p.Y - _fromPosition.Y);
_selectionRectangle = new Rectangle(x, y, w, h);
// Reset Data Point Attributes
foreach (DataPoint point in chart1.Series[0].Points)
{
point.BackSecondaryColor = Color.Black;
point.BackHatchStyle = ChartHatchStyle.None;
point.BorderWidth = 1;
}
this.Invalidate();
}
}
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
// This is the starting position of the rectangle
_fromPosition = e.Location;
}
private void chart1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Blue, 2), _selectionRectangle);
foreach (DataPoint point in chart1.Series[0].Points)
{
// Check if the data point lies within the rectangle
if (_selectionRectangle.Contains(???))))
{
// How do I convert DataPoint into Point?
}
}
}
}
What I am trying to do is query each DataPoint in the series and check if it lies within the Rectangle. Here, I am unable to transform each DataPoint into its corresponding Point. It seems pretty straightforward, so I am either missing something basic here or approaching the problem incorrectly.
I should also add that I referred to similar questions here and here, but they do not talk about how to actually identify DataPoints within the rectangle.
Any direction would be appreciated!
I have shown how to cheat the Chart into helping to get at the coordinates of DataPoints in the Paint event here.
But as you want to pick them up in the Paint event anyway, no cheating is needed..:
I define a List to collect the lassoed DataPoints:
List<DataPoint> dataPoints = new List<DataPoint>();
And I clear it on each new selection:
void chart1_MouseDown(object sender, MouseEventArgs e)
{
_fromPosition = e.Location;
dataPoints.Clear();
}
At the end I can write out the results:
void chart1_MouseUp(object sender, MouseEventArgs e)
{
foreach(DataPoint pt in dataPoints)
Console.WriteLine("found:" + pt.ToString() +
" at " + chart1.Series[0].Points.IndexOf(pt));
}
And in the Paint event we make use of the ValueToPixelPosition method of the two axes:
void chart1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Blue, 2) // dispose of my Pen
{DashStyle = System.Drawing.Drawing2D.DashStyle.Dot})
e.Graphics.DrawRectangle(pen, _selectionRectangle);
foreach (DataPoint point in chart1.Series[0].Points)
{ // !! officially these functions are only reliable in a paint event!!
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(point.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(point.YValues[0]);
PointF pt = new PointF((float)x,(float)y);
// Check if the data point lies within the rectangle
if (_selectionRectangle.Contains(Point.Round(pt)))
{
if (!dataPoints.Contains(point)) dataPoints.Add(point);
}
}
}
My task is to draw a graph in chart control using mouse and retrieve the (X,Y) points from the Graph.
I tried of drawing a graph with mouse.
Here is the normal Graph looks like.
After drawing with mouse, it looks like :
The code which i used to draw graph is :
private void Form1_Load(object sender, EventArgs e)
{
chart1.ChartAreas[0].AxisX.Minimum =0170101;
chart1.ChartAreas[0].AxisX.Maximum =0175951;
chart1.ChartAreas[0].AxisY.Minimum=0780101;
chart1.ChartAreas[0].AxisY.Maximum=0785951;
double range = chart1.ChartAreas[0].AxisX.Maximum - chart1.ChartAreas[0].AxisX.Minimum;
chart1.ChartAreas[0].AxisX.Interval = range / 5;
range = chart1.ChartAreas[0].AxisY.Maximum - chart1.ChartAreas[0].AxisY.Minimum;
chart1.ChartAreas[0].AxisY.Interval = range / 5;
}
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
if (!(FirstPoint == null))
{
Graphics g = chart1.CreateGraphics();
Pen ErasePen = new Pen(Color.Transparent);
g.DrawLine(ErasePen, FirstPoint, TempPoint);
TempPoint = new Point(e.X, e.Y);
this.Refresh();
}
}
private void chart1_MouseDown_1(object sender, MouseEventArgs e)
{
FirstPoint = new Point(e.X, e.Y);
TempPoint = new Point(e.X, e.Y);
}
private void chart1_MouseUp_1(object sender, MouseEventArgs e)
{
LineEndPoints Line = new LineEndPoints
{
StartPoint = FirstPoint,
endPont = new Point(e.X, e.Y)
};
LinesList.Add(Line);
// FirstPoint = null;
this.Refresh();
}
private void chart1_Paint_1(object sender, PaintEventArgs e)
{
foreach (LineEndPoints line in LinesList)
{
e.Graphics.DrawLine(Pens.Green, line.StartPoint, line.endPont);
}
if (!(FirstPoint == null))
{
e.Graphics.DrawLine(Pens.Red, FirstPoint, TempPoint);
}
}
When I used to draw a graph it is moving away from the max and min values of the chart control.
Now what I need to know is:
1) My graph should not move away from the X and Y axis points of the chart control.
2) I need to know the X,Y points of the graph which is drawn with respect to chart axis but not with form axis.
I use C# VS 2010 Win-forms.
Chart uses a different coordinate system for its content than its Control surface, ie the mouse loacation; there are conversion functions but they come with a caveat: They are only guaranteed to work in the Paint events..
PixelPositionToValue
ValueToPixelPosition
Here is an example that translates the pixel points to chart point values. You can see the two graphics overlaying very nicely: The DataPoints are connected in blue lines and the pixel points by dotted red lines..:
public Form1()
{
InitializeComponent();
chart1.Series[0].ChartType = SeriesChartType.Line;
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisX.Maximum = 500;
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 500;
}
List<Point> points = new List<Point>();
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
points.Add(e.Location);
chart1.Invalidate();
}
private void chart1_Paint(object sender, PaintEventArgs e)
{
chart1.Series[0].Points.Clear();
foreach(Point pt in points)
{
double dx = chart1.ChartAreas[0].AxisX.PixelPositionToValue(pt.X);
double dy = chart1.ChartAreas[0].AxisY.PixelPositionToValue(pt.Y);
chart1.Series[0].Points.AddXY(dx, dy);
}
if (points.Count > 1)
using (Pen pen = new Pen(Color.Red, 2.5f))
e.Graphics.DrawLines(pen, points.ToArray());
}
Note that this will always clear the DataPoints and recreate them from the pixel points list, according to the current chart layout using the PixelPositionToValue method. The layout will always change when things like label sizes, other scaling, other minimum/maximum values etc change.
Maybe you really want to work the other way round, that is change the clicked points using the ValueToPixelPosition.
Here is the modified example that keeps the DataPoints and recalculates the pixel points:
List<Point> points = new List<Point>();
Point lastPoint = Point.Empty;
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
lastPoint = e.Location;
chart1.Invalidate();
}
private void chart1_Paint(object sender, PaintEventArgs e)
{
// if we have a new point, convert to DataPoint and add to Series.Points:
if (lastPoint != Point.Empty)
{
double dx = chart1.ChartAreas[0].AxisX.PixelPositionToValue(lastPoint.X);
double dy = chart1.ChartAreas[0].AxisY.PixelPositionToValue(lastPoint.Y);
chart1.Series[0].Points.AddXY(dx, dy);
}
lastPoint = Point.Empty;
// now recalculate all pixel points:
points.Clear();
foreach (DataPoint pt in chart1.Series[0].Points)
{
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(pt.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(pt.YValues[0]);
points.Add(new Point((int)x, (int)y));
}
if (points.Count > 1)
using (Pen pen = new Pen(Color.Red, 2.5f))
{
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
e.Graphics.DrawLines(pen, points.ToArray());
}
}
This makes a lot more sense, since the DataPoints are always bound to the chart's scaling, so they are the 'real thing'. When you resize the Chart the DataPoints and the Graphic they make up are scaled as usual and the drawn pixel points follow perfectly:
(When you resize the first version you can see how nothing is being scaled up or down and only the chart's grid lines change..)
Note that I set up a few things to start with, so that not every point I add enforces too many layout changes. Also note that sometimes there still occurs a feedback loop when the new points change e.g. the label sizes, which enforces a layout change and the paint loop.. To fix this you should probably control the labels' formats!
Also note that both conversion methods only work (correctly) in the Paint event(s), probably because only then the current layout is being settled.
In For1 i have this code:
private void timer1_Tick(object sender, EventArgs e)
{
try
{
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Load(file_array_satellite[file_indxs_satellite]);
file_indxs_satellite = file_indxs_satellite - 1;
if (file_indxs_satellite < 0)
{
file_indxs_satellite = file_array_satellite.Length - 1;
}
}
catch
{
timer1.Enabled = false;
}
}
private void satellitesToolStripMenuItem_Click(object sender, EventArgs e)
{
file_array_satellite = Directory.GetFiles(UrlsPath, "RainImage*.*");
if (file_array_satellite.Length > 0)
{
DateTime[] creationTimes8 = new DateTime[file_array_satellite.Length];
for (int i = 0; i < file_array_satellite.Length; i++)
creationTimes8[i] = new FileInfo(file_array_satellite[i]).CreationTime;
Array.Sort(creationTimes8, file_array_satellite);
file_indxs_satellite = 0;
file_indxs_satellite = file_array_satellite.Length - 1;
timer1.Enabled = true;
}
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12,
27);
}
In the original the picturebox1 size is 100x100 and each image i stretch to fit in the pictureBox.
When it's 100x100 everything is ok i see the animation of each image in the pictureBox.
Now i did an event that when i enter with the mouse to the pictureBox area it should move to the center of the form resize to 500x500 stretch the images and show the same animation.
And when i leave the pictureBox area it should return to it's original size and location.
When i enter with the mouse to the pictureBox1 area the pictureBox just vanish i don't see it anywhere once i leave the pictureBox area i see it 100x100 in it's original place and size.
Why when i enter with the mouse to the pictureBox1 area it's vanish i don't see it in the center of the form on size 500x500 ?
file_array_satellite is string[] and file_indxs_satellite is int.
RainImage*.* are the files names on the hard disk after downloaded them.
The idea is not to convert/change the files sizes on the hard disk each time i enter or leave so i wanted that once i enter the pictureBox1 area it will stretch the current image in the pictureBox and show it . It's working when it's 100x100 but not on 500x500.
When you mouse over the PictureBox and move it to the center of the form, you are moving it out from under the mouse cursor. This causes the MouseLeave event to immediately trigger, which places it back under your mouse cursor again, which causes the MouseEnter event to trigger again, etc.
You can do something like this:
bool suppressMouseLeave;
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
suppressMouseLeave = true;
this.pictureBox1.Size = new Size(500, 500);
pictureBox1.Location = new Point(this.Bounds.Width / 2,
this.Bounds.Height / 2);
this.pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.BringToFront();
//point the cursor to the new Position so that it's still kept on the pictureBox1
//This is important because it makes your idea acceptable.
//Otherwise you have to move your mouse onto your pictureBox and leave the
//mouse from it then to restore the pictureBox
Cursor.Position = PointToScreen(new Point(pictureBox1.Left + 250, pictureBox1.Top + 250));
suppressMouseLeave = false;
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
if(suppressMouseLeave) return;
this.pictureBox1.Size = new Size(100, 100);
pictureBox1.Location = new Point(12, 27);
}
I would venture a guess that this.Bounds.Width and this.Bounds.Height are not what you expect them to be, so the PictureBox isn't vanishing, you are just setting it to some location that is offscreen/off your form. Run Visual Studio in Debug mode and put a breakpoint around that line and see what this.Bounds is equal to. This may give you a clue as to the proper location you need to set.
How about in "in place" zoom like this?
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(200, 200);
pictureBox1.Bounds = rc;
pictureBox1.BringToFront();
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
Rectangle rc = pictureBox1.Bounds;
rc.Inflate(-200, -200);
pictureBox1.Bounds = rc;
}
I am creating an application, in which user will be able to Upload an image and then Zoom in and Out the image on a particular location (current mouse pointer).
Also user should be able to drag the image to see other parts of the image when the image is zoomed.
I have implemented some functionality to achieve it but i am scaling the complete image. I want to know that how i can scale a particular portion of image, or scale the complete image and then point to that location where my current mouse pointer is placed.
Code:
private void DisplayIsdDiagram(BO.IsdDiagram IsdDiagram)
{
DoubleBuffered = true;
zoomFac = 1;
translateX = 0;
translateY = 0;
transStartX = 0f;
transStartY = 0f;
picIsdDiagram.BorderStyle = BorderStyle.Fixed3D;
bmp = new Bitmap(Image.FromStream(new MemoryStream(IsdDiagram.Image.ToArray())));
if (bmp.Width > bmp.Height)
{
ratio = (float)picIsdDiagram.Width / (float)bmp.Width;
translateRatio = (float)bmp.Width / (float)picIsdDiagram.Width;
}
else
{
ratio = (float)picIsdDiagram.Height / (float)bmp.Height;
translateRatio = (float)bmp.Height / (float)picIsdDiagram.Height;
}
//picIsdDiagram.Image = bmp;
picIsdDiagram.Refresh();
picIsdDiagram.MouseWheel += new MouseEventHandler(picIsdDiagram_MouseWheel);
}
private void picIsdDiagram_MouseWheel(object sender, MouseEventArgs e)
{
IsZooming = true;
if (e.Delta < 0)
{
if (zoomFac > 1)
zoomFac = zoomFac - (float)0.1;
}
else
{
if (zoomFac <= 5)
zoomFac = zoomFac + (float)0.1;
}
picIsdDiagram.Refresh();
IsZooming = false;
}
private void picIsdDiagram_MouseDown(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = true;
transStartX = e.X;
transStartY = e.Y;
}
private void picIsdDiagram_MouseUp(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = false;
translateX = translateX + ((e.X - transStartX) * (translateRatio / zoomFac));
translateY = translateY + ((e.Y - transStartY) * (translateRatio / zoomFac));
picIsdDiagram.Refresh();
}
private void picIsdDiagram_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
if (IsZooming == false && IsMouseDown == true)
g.TranslateTransform(translateX, translateY);
g.DrawImage(bmp, 0, 0);
}
I tried to get the current mouse position from MouseHover event and tried to Translate the picture when only Zoom is done, but that is not working.
Also the Picture Box has some other multiple Picture boxes inside it, to show some representation on the big image. While scaling the Big Image, small images (inside images) should not be scaled. Although there position needs to be recalculated to show them at their real places even after zooming on the Big image.
So in above i am facing two problems:
1) To Zoom an Image at any particular location (current mouse pointer)
by scrolling.
2) To regenerate the coordinates of the sub images while
zooming and translating.
Any help that can guide me in correct direction.
Also if by any other means, i could be able to achieve this functionality.
Application : Windows
Control : Picture Box (Please suggest if any other control can be used, if not possible with this)
Language : C#
Waiting for response!
PictureEdit control provided by DevExpress 13.2
I need to update scroll bar position when I click on image and move picturebox. It is always at the beggining, it only moving on the right side (horizontall) and down (vertical).
private void pictureBox1_MouseMove_1(object sender, MouseEventArgs e)
{
....
Point currentMousePos = e.Location;
int distanceX = currentMousePos.X - mouseX;
int distanceY = currentMousePos.Y - mouseY;
int newX = pictureBox1.Location.X + distanceX;
int newY = pictureBox1.Location.Y + distanceY;
if (newX + pictureBox1.Image.Width + 10 < pictureBox1.Image.Width && pictureBox1.Image.Width + newX + 10 > panel1.Width)
{
pictureBox1.Location = new Point(newX, pictureBox1.Location.Y);
}
if (newY + pictureBox1.Image.Height + 10 < pictureBox1.Image.Height && pictureBox1.Image.Height + newY + 10 > panel1.Height)
{
pictureBox1.Location = new Point(pictureBox1.Location.X, newY);
}
}
I think you need to change the AutoScrollPosition of the parent panel and not play around with the Location points of the PictureBox. After all, the scroll bars of the parent panel are already taking care of the position of the PictureBox.
Try something like this (by the way, my code only does this when a button is pressed, otherwise, I think it would be a weird user interface design):
private Point _StartPoint;
void pictureBox1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left)
_StartPoint = e.Location;
}
void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
Point changePoint = new Point(e.Location.X - _StartPoint.X,
e.Location.Y - _StartPoint.Y);
panel1.AutoScrollPosition = new Point(-panel1.AutoScrollPosition.X - changePoint.X,
-panel1.AutoScrollPosition.Y - changePoint.Y);
}
}
LarsTech's code isn't 100% correct. 2 notes:
Note that if slider is moved, then the same point on screen changes it's coordinates relative to pictureBox1 (as pictureBox moved with moved slider). Therefore we want to use the screen coordinates (Control.MousePosition instead of e.Location).
Changing panel1.AutoScrollPosition causes the move of pictureBox relative to mouseCursor, so the pictureBox1.MouseMove event is fired again, even if cursor didn't move on the screen. Adding _StartPoint = Control.MousePosition prevents unwanted scrolling.