so I'm trying to find a way to draw a straight line between two buttons that I have clicked on (There are multiple source->destination lines to draw). I am currently using this code.
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = e.Graphics)
{
foreach (Connection c in connections)
{
Point pt1 = c.source.Location;
Point pt2 = c.destination.Location;
using (Pen p = new Pen(Brushes.Black))
{
g.DrawLine(p, pt1, pt2);
}
}
}
}
Now this works, but obviously it is drawing on my form canvas and it hidden behind all my buttons that are on my form. Here is what the layout looks like:
Is there anyway I can fix this?
Thanks.
Each button knows its relative position on parent and each can handle its Paint event. If you store your lines in some colletion in form of their line equation that goes through two points (x2-x1)(y-y1)=(y2-y1)(x-x1), you will be able to iterate through them in button Paint handler and calculate whether the line crosses the button's edges or not. Each button should have own equation of its edges with respect to its parent coordinates.
Related
This question already has answers here:
How to draw rectangle on MouseDown/Move c#
(4 answers)
Closed 7 years ago.
I'm trying to create a windows forms application in which, when the user clicks anywhere on a picture box, a rectangle appears at the position where the image was clicked.
However, if I click anywhere on the image, the rectangle will appear at some random position regardless of where I clicked. It can appear either near or far away from the mouse click, and in some cases it never goes beyond the left half of the picture box.
May I have some guidance on how to resolve this issue? Specifically, I want the position where I clicked to be the center of the rectangle.
Thank you!
This is my code for reference:
private void pbImage_Click(object sender, EventArgs e)
{
//Note: pbImage is the name of the picture box used here.
var mouseEventArgs = e as MouseEventArgs;
int x = mouseEventArgs.Location.X;
int y = mouseEventArgs.Location.Y;
// We first cast the "Image" property of the pbImage picture box control
// into a Bitmap object.
Bitmap pbImageBitmap = (Bitmap)(pbImage.Image);
// Obtain a Graphics object from the Bitmap object.
Graphics graphics = Graphics.FromImage((Image)pbImageBitmap);
Pen whitePen = new Pen(Color.White, 1);
// Show the coordinates of the mouse click on the label, label1.
label1.Text = "X: " + x + " Y: " + y;
Rectangle rect = new Rectangle(x, y, 200, 200);
// Draw the rectangle, starting with the given coordinates, on the picture box.
graphics.DrawRectangle(whitePen, rect);
// Refresh the picture box control in order that
// our graphics operation can be rendered.
pbImage.Refresh();
// Calling Dispose() is like calling the destructor of the respective object.
// Dispose() clears all resources associated with the object, but the object still remains in memory
// until the system garbage-collects it.
graphics.Dispose();
}
UPDATE 12.55am, 16/8/2015 - I know why! The SizeMode property of the pictureBox was set to StretchImage. Changed it back to Normal mode and it worked fine. Not exactly sure why this is so, I'll definitely look into it.
To those who have replied, thank you so much for your help! :)
The first two arguments to the Rectangle constructor are the top-left (not center) coordinates.
And handle the mouse and paint events separately:
int mouseX, mouseY;
private void pbImage_MouseDown(object sender, MouseEventArgs e)
{
mouseX = e.X;
mouseY = e.Y;
pbImage.Refresh();
}
private void pbImage_Paint(object sender, PaintEventArgs e)
{
//... your other stuff
Rectangle rect = new Rectangle(mouseX - 100, mouseY - 100, 200, 200);
e.Graphics.DrawRectangle(whitePen, rect);
}
You are casting EventArgs to MouseEventArgs, I think that is incorrect. Are you tried with MouseDown or MouseUp events of the picture control? Those events provides you the information you need.
I'm experiencing a discrepancy between a GraphicsPath drawn in World coordinates on a UserControl and the results of GraphicsPath.IsVisible() to Hit Test the shape with the mouse.
I performed a little test that made a map of where IsVisible() returned true, relative to the GraphicsPath shape that was drawn. The results show a very "low resolution" version of the shape I'm drawing.
Link to shared Google Drive image showing the results:
http://goo.gl/zd6xiM
Is there something I'm doing or not doing correctly that's causing this?
Thanks!
Here's the majority of my OnMouseMove() event handler:
protected override void OnMouseMove(MouseEventArgs e)
{
//base.OnMouseMove(e);
debugPixel = Point.Empty;
PointF worldPosition = ScreenToWorld(PointToClient(Cursor.Position));
if (_mouseStart == Point.Empty) // Just moving mouse around, no buttons pressed
{
_objectUnderMouse = null;
// Hit test mouse position against each canvas object to see if we're overtop of anything
for (int index = 0; index < _canvasObjects.Count; index++) // Uses front to back order
{
NPCanvasObject canvasObject = _canvasObjects[index];
if (canvasObject is NPCanvasPart)
{
NPCanvasPart canvasPart = (canvasObject as NPCanvasPart);
NPPart part = canvasPart.Part;
GraphicsPath gp = canvasPart.GraphicsPath;
// Set the object under the mouse cursor, and move it to the "front" so it draws on top of everythign else
if (gp.IsVisible(worldPosition))
{
// DEBUG
debugPixel.X = e.X;
debugPixel.Y = e.Y;
_objectUnderMouse = canvasObject;
_canvasObjects.MoveItemAtIndexToFront(_canvasObjects.IndexOf(canvasObject));
break; // Since we're modifying the collection we're iterating through, we can't reliably continue past this point
}
}
}
}
else
{
...
}
}
Later in my drawing code I draw a pixel whenever debugPixel != Point.Empty . I temporarily suppressed clearing before drawing so I could see them all.
Some other info that may be asked, or could be helpful to troubleshoot:
I've tried different Graphics.InterpolationMode settings but that doesn't seem to have any effect
I've applied a TranslateTransform and ScaleTransform to the main drawing Graphics but the underlying HitTest map seems to scale and translate equal to the GraphicsPath
For my main drawing canvas, Graphics.PageUnit = GraphicsUnit.Inch, except when I'm doing pixel-based overlay stuff
I thought I had researched this thoroughly enough, but apparently not. Shortly after posting this question I did another search with slightly different terms and found this:
http://vbcity.com/forums/t/72877.aspx
...which was enough to clue me in that the GraphicsPath and my main drawing Graphics were not the same. Using the overloaded GraphicsPath.IsVisible(PointF, Graphics) solved this problem very nicely.
Essentially it was trying to check against a very aliased (pixelated) version of my shape that had been scaled to the same size but not smoothed.
How do I draw a circle and line in the picturebox?
or:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(
new Pen(Color.Red,2f),
new Point(0,0),
new Point(pictureBox1.Size.Width, pictureBox1.Size.Height ));
e.Graphics.DrawEllipse(
new Pen(Color.Red, 2f),
0,0, pictureBox1.Size.Width, pictureBox1.Size.Height );
}
Handle the paint event of the picture box and do your custom drawing there.
The best way is to NOT draw a circle and line in a picturebox! It is not designed for that purpose.
From Bob Powell's GDI+ blog:
The root of this problem is that the fundamental rules of windows
programming have been broken. And as a consequence of the picture box
is blamed for something that's really not its fault. To help explain
why, the four points below outline what's gone wrong in this case.
The PictureBox control is for displaying images. It is not a handy placeholder for a graphics surface.
Windows is an event driven system in which each event must be serviced in the correct context and events destined to handle button click or mouse move events must not be used to do drawing on screen or other weird stuff.
The PictureBox refreshes itself by drawing the System.Drawing.Image based object stored in it's Image property. If there is no image, it will show the background colour.
Stealing and drawing upon the Graphics object of any control is not good practice, should be strongly discouraged and breaks the rules of handling events in the right place at the right time. Basically if you do this it will cause you pain. When you bang your head against a wall it causes you pain. that is a sign that you should stop doing it. It's the same for the PictureBox.CreateGraphics call.
The right way to do it.
Following the rules of the event driven system is easy but requires a
little forethought. So, if you want to draw some little bit of
graphics and have it remain there when a window moves in front of it
and away again or when you minimize and restore, you have to service
the Paint event of whatever object it is that you wish to paint on.
The PictureBox carries baggage around with it that is unnecessary for
this kind of application. If you just want to draw something in one
place, draw it on the form by responding to the Form.Paint event. If
you want a handy placeholder for a graphic that works within a set
bounds, use a Panel control and service it's Paint event. If you want
to duplicate a graphic over and over for your corporate image, create
a control and do the drawing in the OnPaint override.
Source: https://web.archive.org/web/20120330003635/http://bobpowell.net/picturebox.htm (the original site is defunct).
the picturebox is a control and has an image as source - so you have to draw on the image and hand the image to the control to show it
MyImage = new Bitmap(fileToDisplay);
pictureBox1.ClientSize = new Size(xSize, ySize);
pictureBox1.Image = MyImage;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Asssignment
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
Pen p = new Pen(Color.Blue);
int radius = 200;
int x =Width/2;
int y =Height/2;
int first_point1 = (int)(Math.Cos(0) * radius + x);
int first_point2 = (int)(Math.Sin(0) * radius + y);
Point p1= new Point(first_point1,first_point2);
for(int i=1;i<500; i++)
{
int dx = (int)(Math.Cos(i)*radius+x );
int dy = (int)(Math.Sin(i)*radius+y );
Point p2 = new Point(dx, dy);
g.DrawLine(p, p1, p2);
p1 = p2;
}
}
}
}
I have spent 20 minutes trying to find an example in C# of how to draw a line between two rectangles, and I can't find anything. I don't know if I just don't understand the paradigm of drawing 2D shapes in Silverlight, or if I'm just looking in the wrong place.
I have set up the rectangles so I can drag them around, and now I want to draw a line between the two shapes as I drag a rectangle across the canvas. I want to be able to do something like this as I drag the second rectangle:
void host1_MouseLeftButtonMove(object sender, MouseEventArgs e)
{
if (isDown)
{
this.host1TranslateTransform.X = e.GetPosition(canvas).X - x;
this.host1TranslateTransform.Y = e.GetPosition(canvas).Y - y;
Line l = new Line();
l.X1 = rect1.X; // does not work
l.X2 = e.GetPosition(canvas).X;
l.Y1 = rect1.Y; // does not work
l.Y2 = e.GetPosition(canvas).Y;
}
}
How do I get the coordinates of the first box? I can't figure out how to get the coordinates of shapes relative to the canvas in my application. I would appreciate any tutorials that give a beginner overview for how to draw simple 2D shapes.
Thanks!
try this
Canvas.GetTop(element);
Canvas.GetLeft(element);
position properties are attached properties ;)
Imagine I use the .NET graphic classes to draw a rectangle.
How could I then assign an event so that if the user clicks a certain point, or a certain point range, something happens (a click event handler)?
I was reading CLR via C# and the event section, and I thought of this scenario from what I had read.
A code example of this would really improve my understanding of events in C#/.NET.
Thanks
You can assign Click event handler to control whose surface will be used to draw rectangle.
Here is a small example:
When you click on form inside of rectangle it will be drawn with red border when you click outside it will be drawn with black border.
public partial class Form1 : Form
{
private Rectangle rect;
private Pen pen = Pens.Black;
public Form1()
{
InitializeComponent();
rect = new Rectangle(10, 10, Width - 30, Height - 60);
Click += Form1_Click;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(pen, rect);
}
void Form1_Click(object sender, EventArgs e)
{
Point cursorPos = this.PointToClient(Cursor.Position);
if (rect.Contains(cursorPos))
{
pen = Pens.Red;
}
else
{
pen = Pens.Black;
}
Invalidate();
}
}
PointToClient method translates cursor coordinates to control-relative coordinates. I.e. if you cursor is at (screenX, screenY) position on the screen it can be at (formX, formY) position relatively to form's top-left corner. We need to call it to bring cursor position into coordinate system used by our rectangle.
Invalidate method makes control to redraw itself. In our case it triggers OnPaint event handler to redraw rectangle with a new border color.