I want to draw a rectangle. Thing what I want is that show the user to rectangle on the mouse event.
Like in the image. This is for C# .net Forms application.
Help me to achieve this. Any help is appreciated.
Thank You
Yohan
You can do that in three steps:
First check if mouse is pressed down
If it is then on mouse move event keep initializing the rectangle with new positions while mouse is being dragged
Then on paint event draw the rectangle. (It will be raised for almost every mouse event, depends mouse refresh rate and dpi)
You can do somthing like this (in your Form):
public class Form1
{
Rectangle mRect;
public Form1()
{
InitializeComponents();
//Improves prformance and reduces flickering
this.DoubleBuffered = true;
}
//Initiate rectangle with mouse down event
protected override void OnMouseDown(MouseEventArgs e)
{
mRect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
//check if mouse is down and being draged, then draw rectangle
protected override void OnMouseMove(MouseEventArgs e)
{
if( e.Button == MouseButtons.Left)
{
mRect = new Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top);
this.Invalidate();
}
}
//draw the rectangle on paint event
protected override void OnPaint(PaintEventArgs e)
{
//Draw a rectangle with 2pixel wide line
using(Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, mRect);
}
}
}
later if you want to check if Buttons (shown in diagram) are in rectangle or not , you can do that by checking the Button's region and check if they lie in your drawn rectangle.
The solution by Shekhar_Pro draws a rectangle just in one direction (top to bottom, left to right) if you want to draw a rectangle regardless of the mouse position and the direction of the movement the solution is:
Point selPoint;
Rectangle mRect;
void OnMouseDown(object sender, MouseEventArgs e)
{
selPoint = e.Location;
// add it to AutoScrollPosition if your control is scrollable
}
void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point p = e.Location;
int x = Math.Min(selPoint.X, p.X)
int y = Math.Min(selPoint.Y, p.Y)
int w = Math.Abs(p.X - selPoint.X);
int h = Math.Abs(p.Y - selPoint.Y);
mRect = new Rectangle(x, y, w, h);
this.Invalidate();
}
}
void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Blue, mRect);
}
Those blue rectangles look a lot like controls. Drawing a line on top of a control is hard to do in Winforms. You have to create a transparent window that overlays the design surface and draw the rectangle on that window. This is also the way the Winforms designer works. Sample code is here.
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);
}
}
}
This question already has answers here:
Draw a rectangle on mouse click
(4 answers)
Closed 7 years ago.
So I've been trying lately to make a paint application to practice C#.
My problem for the past 2 days is in the creation of rectangles.
I made a panel so all the drawing are going there. The user selects the shape he wants to draw using a menu and he can start drawing with the mouse.
I encounter 2 problems which are the following:
1) Even though my starting point was inside the panel, I moved the mouse and went outside the panel and the rectangle was drawn outside the panel as shown in the picture below.
2) After I create this rectangle and I try to draw another, the previous one is deleted. So in a way I can't draw 2 rectangles at once.
Here's a part of my source code.
Graphics mygraphics;
Pen lPen = new Pen(Color.Black); //Left Pen
Pen rPen = new Pen(Color.White); //Right pen
Point sp = new Point(0, 0);
private bool isRectangle;
private bool isLeft, isRight; //isLeft -- Left Click, isRight -- Right Click
private void drawPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isRectangle == true)
{
if (e.Button == MouseButtons.Left)
{
isLeft = true;
Point p = e.Location;
int x = Math.Min(sp.X, p.X);
int y = Math.Min(sp.Y, p.Y);
int w = Math.Abs(p.X - sp.X);
int h = Math.Abs(p.Y - sp.Y);
mRect = new Rectangle(x, y, w, h);
this.Invalidate();
}
}
}
private void drawPanel_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
}
private void drawPanel_MouseUp(object sender, MouseEventArgs e)
{
isLeft = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(lPen, mRect);
}
What I want to accomplish is my rectangles not getting deleted after I try to draw another one and to draw them inside the panel.
What do you guys suggest?
Here are the minimal changes I suggest:
List<Rectangle> rectangles = new List<Rectangle>();
Rectangle mRect = Rectangle.Empty;
private void drawPanel_MouseUp(object sender, MouseEventArgs e)
{
isLeft = false;
rectangles.Add(mRect);
mRect = Rectangle.Empty;
drawPanel.Invalidate();
}
private void drawPanel_Paint(object sender, PaintEventArgs e)
{
foreach (Rectangle rect in rectangles) e.Graphics.DrawRectangle(lPen, rect );
e.Graphics.DrawRectangle(Pens.Orange, mRect); // or whatever..
}
Note that the Paint event now is the one of the Panel. Do make sure you hook it up with the Panel!!
Also note how I draw the current rectangle mRect in a different color than the list of the other rectangles; this is of course optional..
I have an application which uses the mouse to free-draw a rectangle on a picbox image. However the rectangle only shows up behind the picbox, rather than on top of it. Is there a property i can set which can fix this? (show rect on top of picbox image rather than behind it). Here is the code:
System.Drawing.Graphics picboxGraphics;
bool mDown = false;
int mouseX;
int mouseY;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = true;
mouseX = e.X;
mouseY = e.Y;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mDown == true)
{
this.Refresh();
Pen drawPen = new Pen(Color.Red, 5);
int width = e.X - mouseX, height = e.Y - mouseY;
Rectangle rect = new Rectangle(mouseX, mouseY, width * Math.Sign(width), height * Math.Sign(height));
picboxGraphics = this.CreateGraphics();
picboxGraphics.DrawRectangle(drawPen, rect);
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
mDown = false;
}
You creating graphics from form which is behind the picbox
you can create graphics from picbox's image and draw someting. But if you want layer system you can draw your thins on an transparent image and combine them. with this vay you can make an undo or delete layer system.
There are several problems in your code: dispose, global variable and wrong control to create graphics from. Make it this way:
using(var graphics = (sender as Control).CreateGraphics())
graphics.DrawRectangle(drawPen, rect);
But honestly, you have to organize it differently (assuming you are going to mimik Paint):
create new object (when mouse is down)
in Paint event draw object (if any)
during mouse move event, update edited object properties and call Invalidate
when you minimize/restore your form, the object will be still there (while in your example, it will get lost).
You can support List of objects to have storage for many rectangles and add history support (as TC Alper Tokcan answer suggesting) to at least Undo last object.
I'm very new in C#
I want a rectangle to appear wherever there's a mouseclick on a panel
Here's my code:
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
int x = e.Location.X;
int y = e.Location.Y;
if (radioButton1.Checked == false)
{
((Panel)sender).Invalidate(new Rectangle(x * 40, y * 40, 40, 40));
}
else if (radioButton2.Checked == true)
{
return;
}
}
I wonder how to change the color of the rectangle?
Please advise me if my code is wrong.
Thanks.
Your drawing should be performed in the panel's Paint event handler. When you click the panel, create the rectangle (in the MouseUp event of the panel) and store it in a collection of rectangles (such as a dictionary). Then refresh the panel. In the panel's Paint event, draw all the rectangles. Here is a simple example:
Dictionary<Color, List<Rectangle>> rectangles = new Dictionary<Color, List<Rectangle>>();
private void panel1_Paint(object sender, PaintEventArgs e)
{
//The key value for the dictionary is the color to use to paint with, so loop through all the keys (colors)
foreach (var rectKey in rectangles.Keys)
{
using (var pen = new Pen(rectKey)) //Create the pen used to draw the rectangle (using statement makes sure the pen is disposed)
{
//Draws all rectangles for the current color
//Note that we're using the Graphics object that is passed into the event handler.
e.Graphics.DrawRectangles(pen, rectangles[rectKey].ToArray());
}
}
}
//This method just adds the rectangle to the collection.
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Color c = getSelectedColor(); //Gets a color for which to draw the rectangle
//Adds the rectangle using the color as the key for the dictionary
if (!rectangles.ContainsKey(c))
{
rectangles.Add(c, new List<Rectangle>());
}
rectangles[c].Add(new Rectangle(e.Location.X - 12, e.Location.Y - 12, 25, 25)); //Adds the rectangle to the collection
}
//Make the panel repaint itself.
panel1.Refresh();
}
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = panel1.CreateGraphics();
g.DrawRectangle(new Pen(Brushes.Black),
new Rectangle(new Point(e.X, e.Y), new
Size(100, 100)));
}
you can change the color in Brushes.Black part of code, change it as you desire
I have created a small application in C#, in which I draw a rectangle when the mouse is moved.
However, when the form is minimized or maximized, the drawing is erased. Also, when I draw a second time, the drawing of the first rectangle is erased.
How can I solve this problem? Here is the code that I currently have:
int X, Y;
Graphics G;
Rectangle Rec;
int UpX, UpY, DwX, DwY;
public Form1()
{
InitializeComponent();
//G = panel1.CreateGraphics();
//Rec = new Rectangle(X, Y, panel1.Width, panel1.Height);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
UpX = e.X;
UpY = e.Y;
//Rec = new Rectangle(e.X, e.Y, 0, 0);
//this.Invalidate();
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
DwX = e.X;
DwY = e.Y;
Rec = new Rectangle(UpX, UpY, DwX - UpX, DwY - UpY);
Graphics G = pictureBox1.CreateGraphics();
using (Pen pen = new Pen(Color.Red, 2))
{
G.DrawRectangle(pen, Rec);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// Draws the rectangle as the mouse moves
Rec = new Rectangle(UpX, UpY, e.X - UpX, e.Y - UpY);
Graphics G = pictureBox1.CreateGraphics();
using (Pen pen = new Pen(Color.Red, 2))
{
G.DrawRectangle(pen, Rec);
}
G.Save();
pictureBox1.SuspendLayout();
pictureBox1.Invalidate();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
pictureBox1.Update();
}
The reason why your drawings are getting erased is because you're drawing into a Graphics object you obtained by calling the CreateGraphics method. In particular, this line of your code is incorrect:
Graphics G = pictureBox1.CreateGraphics();
As you've discovered, whenever the form is repainted (which happens when it is maximized, minimized, covered by another object on the screen, or in a number of other possible situations), everything that you've drawn into that temporary Graphics object is lost. The form completely repaints itself with its internal painting logic; it's completely forgotten about what you temporarily drew on top of it.
The correct way to draw persistent images in WinForms is to override the OnPaint method of the control that you want to draw onto (or, you could also handle the Paint event). So, if you wanted to paint onto your form, you would place your drawing code into the following method:
protected override void OnPaint(PaintEventArgs e)
{
// Call the base class first
base.OnPaint(e);
// Then insert your custom drawing code
Rec = new Rectangle(UpX, UpY, DwX - UpX, DwY - UpY);
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, Rec);
}
}
And to trigger a re-paint, you just call the Invalidate method in any of the mouse events (MouseDown, MouseMove, and MouseUp):
this.Invalidate();
Note, however, that there's absolutely no reason to call the Update method in the Paint event handler. All that calling the Update method does is force the control to repaint itself. But that is already happening when the Paint event gets raised!