I want to click inside a square and then an "X" should appear, but I'm not sure what to put inside the Form1_MouseDown, Form1_Paint and Form1_MouseUp events. How can I implement this is C#?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace VTest
{
public partial class Form1 : Form
{
Rectangle rect; // single rect
int sqsize, n;
int margin;
public Form1()
{
n = 3;
margin = 25;
sqsize = 50;
rect = new Rectangle(10, 10, 150, 150);
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
// what goes here?
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// what goes here?
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
// what goes here?
}
// ...
In your MouseDown event, determining whether the click has occurred within your rectangle is easy:
if (rect.Contains(e.Location))
{
// the user has clicked inside your rectangle
}
Drawing the "X" on the form is also easy:
Graphics g = this.CreateGraphics();
g.DrawString("X", this.Font, SystemBrushes.WindowText,
(float)e.X, (float)e.Y);
However, the "X" in this case will not be persistent, meaning that if you drag another form over your form and then move it away, the "X" will not be there anymore. To draw a persistent "X", create a form-level Point variable like this:
private Point? _Xlocation = null;
Use your MouseDown event to set this variable if the user clicks in your Rectangle:
if (rect.Contains(e.Location))
{
_Xlocation = e.Location;
this.Invalidate(); // this will fire the Paint event
}
Then, in your form's Paint event, draw the "X":
if (_Xlocation != null)
{
e.Graphics.DrawString("X", this.Font, SystemBrushes.WindowText,
(float)e.X, (float)e.Y);
}
else
{
e.Graphics.Clear(this.BackColor);
}
If you want the "X" to then disappear when the user lets go of the mouse button, just put this code in the MouseUp event:
_Xlocation = null;
this.Invalidate();
You can make this as much more complicated as you like. With this code, the "X" will be drawn just below and to the right of wherever you click on the form. If you want the "X" to be centered on the click location, you can use the Graphics object's MeasureString method to determine how high and how wide the "X" will be, and offset the DrawString location accordingly.
You don't need both a mousedown and mouseup event handler.
Pick one to react to, I tend to react to the MouseDown event instead.
But, when you will want to look at the MouseEventArgs properties and you should be able to determine if you are inside the square.
You will probably want to call:
System.Diagnostics.Debug.WriteLine(...)
using the x and y properties in MouseEventArgs, so you can see where the mouse clicks are, and determine when you are in the square.
Once you are there, then you can draw the X.
You may want to write a function to draw an X and test it by having it draw an X at 300,300 so that you can ensure it looks as you want, while you are experimenting with MouseDown.
Update: I like the Rectangle.contains(location) method demonstrated by MusiGenesis.
public partial class formDemo : Form
{
Rectangle rec;
public formDemo() => InitializeComponent();
private void formDemo_Load(object sender, EventArgs e) =>
rec = new Rectangle(150,100,100,100);
private void frmDemo_Paint(object sender, PaintEventArgs e)
{
var p = new Pen(Color.Blue);
var g = e.Graphics;
g.DrawRectangle(p, rec);
}
private void formDemo_MouseMove(object sender, MouseEventArgs e) =>
Cursor = rec.Contains(e.Location) ? Cursors.Cross : Cursors.Default;
private void formDemo_MouseDown(object sender, MouseEventArgs e)
{
if (rec.Contains(e.Location))
{
// Mouse position adjust for window postion and border size.
// You may have to adjust the borders depending your
// Windows theme
int x = MousePosition.X - this.Left - 4;
int y = MousePosition.Y - this.Top - 29;
var g = this.CreateGraphics();
var p = new Pen(Color.Black);
var p1 = new Point(x - 10, y - 10);
var p2 = new Point(x + 10, y + 10);
var p3 = new Point(x - 10, y + 10);
var p4 = new Point(x + 10, y - 10);
g.DrawLines(p, new Point[] { p1, p2 });
g.DrawLines(p, new Point[] { p3, p4 });
}
}
}
Related
I am creating a Graphics Form where objects with coordinates x,y are being drawn into the Graphics. It works properly for small x and y, but when I want to draw them in different place (f.e. x = 500, y = 300) they disappear.
public WindowHandler()
{
dc = this.CreateGraphics();
this.Size = new Size(sizeX, sizeY); // 800x600
startSimulation = new Button
{
// button properties
};
this.Controls.Add(startSimulation);
startSimulation.Click += new EventHandler(StartSimulationClick);
}
private void CreationsMethods()
{
creations.PaintAllAnimals(dc);
}
public void PaintAllAnimals(Graphics g)
{
foreach (var animal in ecoStructure.world.animals)
{
animal.PaintAnimal(g);
}
}
public void PaintAnimal(Graphics graphics)
{
Rectangle rectangle = new Rectangle(x, y, 3, 3);
Pen pen = new Pen(colour);
graphics.DrawRectangle(pen, rectangle);
graphics.FillRectangle(colour, rectangle);
}
I want to put all the objects onto the window. Is there any way to make the Graphics "bigger"? Do I need to make another one? Or should I use different tool to draw rectangles?
Thanks to #Chris Dunaway for posting an answer in comment.
So I deleted the CreateCraphics, and instead of that i am now using an OnPaint method. It works slowly, but works. So I will try to make it as fast as i can. For now, I just created this. NextStepClick is how I use the OnPaint to paint the rectangles.
private void CreationsMethods(object sender, PaintEventArgs e)
{
dc = e.Graphics;
base.OnPaint(e);
creations.PaintAllAnimals(dc);
}
private void NextStepClick(object sender, EventArgs e)
{
this.Refresh();
picBox.Paint += new System.Windows.Forms.PaintEventHandler(CreationsMethods);
}
It's getting to the F_Paint event but not to the F_MouseDown event.
What I want to be able to draw rectangle on the F form.
Maybe since the F form is transparent it can't draw on it ? But it's never get to the F_MouseDown event I use a break point inside the F_MouseDown event.
Not sure why it's not getting to the MouseDown event.
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 Tester
{
public partial class Form1 : Form
{
Ffmpeg ffmpeg = new Ffmpeg();
private bool _canDraw;
private int _startX, _startY;
private Rectangle _rect;
public Form1()
{
InitializeComponent();
BackColor = Color.Blue;
TransparencyKey = BackColor;
Opacity = 1;
var f = new HelperForm { Opacity = 0, ShowInTaskbar = false, FormBorderStyle = FormBorderStyle.None };
f.MouseDown += F_MouseDown;
f.MouseMove += F_MouseMove;
f.MouseUp += F_MouseUp;
f.Paint += F_Paint;
f.Show();
Visible = false;
Owner = f;
Visible = true;
Move += (o, a) => f.Bounds = Bounds;
Resize += (o, a) => f.Bounds = Bounds;
f.Bounds = Bounds;
ffmpeg.Start(#"d:\ffmpegx86\test.mp4", 24);
}
private void F_Paint(object sender, PaintEventArgs e)
{
//Create a new 'pen' to draw our rectangle with, give it the color red and a width of 2
using (Pen pen = new Pen(Color.Red, 2))
{
//Draw the rectangle on our form with the pen
e.Graphics.DrawRectangle(pen, _rect);
}
}
private void F_MouseUp(object sender, MouseEventArgs e)
{
//The system is no longer allowed to draw rectangles
_canDraw = false;
}
private void F_MouseMove(object sender, MouseEventArgs e)
{
//If we are not allowed to draw, simply return and disregard the rest of the code
if (!_canDraw) return;
//The x-value of our rectangle should be the minimum between the start x-value and the current x-position
int x = Math.Min(_startX, e.X);
//The y-value of our rectangle should also be the minimum between the start y-value and current y-value
int y = Math.Min(_startY, e.Y);
//The width of our rectangle should be the maximum between the start x-position and current x-position minus
//the minimum of start x-position and current x-position
int width = Math.Max(_startX, e.X) - Math.Min(_startX, e.X);
//For the hight value, it's basically the same thing as above, but now with the y-values:
int height = Math.Max(_startY, e.Y) - Math.Min(_startY, e.Y);
_rect = new Rectangle(x, y, width, height);
//Refresh the form and draw the rectangle
Refresh();
}
private void F_MouseDown(object sender, MouseEventArgs e)
{
//The system is now allowed to draw rectangles
_canDraw = true;
//Initialize and keep track of the start position
_startX = e.X;
_startY = e.Y;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ffmpeg.Close();
}
}
class HelperForm : Form
{
protected override CreateParams CreateParams
{
get
{
const int WS_EX_TOOLWINDOW = 0x80;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TOOLWINDOW;
return cp;
}
}
}
}
There're several reasons preventing the rectangle from being drawn and firing mouse events. First, you're setting the Opacity to 0; this means whatever you try to draw could never be visible. Instead, you should set the TransparencyKey to the color in BackColor:
f.TransparencyKey = f.BackColor;
Then, you're trying to draw a rectangle using the object _rect which was never initialized; therefore the rectangle you're trying to draw will be drawn with the size of 0 width and 0 height which means it won't be drawn, so, during your initialization, you should give a default value for _rect e.g:
_rect = new Rectangle(Point.Empty, this.Size);
The drawn rectangle should be visible by now; however, the events won't fire because you're reversing form ownership, so, instead of
Owner = f;
Use:
f.Owner = this;
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);
}
}
}
I try to make a graphics.
When I click on my label, I want to draw a line. It works, it draw my line but at the last point there is another line going at the left top corner.. I don't know why.
(It's useless, but it's for another project, I try to understand how works the drawing)
Here's my code :
public partial class Form1 : Form
{
Pen myPen = new Pen(Color.Blue);
Graphics g = null;
int start_x = 0, start_y;
Point[] points = new Point[1000];
int test = 0;
public Form1()
{
InitializeComponent();
start_y = canvas.Height / 2;
points[0] = new Point (start_x,start_y);
myPen.Width = 1;
}
private void drawLine()
{
g.DrawLines(myPen, points);
}
private void incrementation_Click(object sender, EventArgs e)
{
test = test + 1;
incrementation.Text = test.ToString();
if(test == 1)
{
points[1] = new Point(100, start_y);
}
if (test == 2)
{
points[test] = new Point(200, 90),new Point(220, 10);
}
if (test == 3)
{
points[test] = new Point(220, 10);
drawLine();
}
}
private void canvas_Paint(object sender, PaintEventArgs e)
{
g = canvas.CreateGraphics();
}
}
A couple of issues.
You don't assign any values to points after points[3].
Point is a structure and will have a value of [0,0] at all further elements
so your lines go there.. (all 996 of them ;-)
There is more you should change:
Do the drawing in the Paint event or trigger it from there.
Do not store the Paint e.Grahpics object. You can pass it out to use it, but don't try to hold on to it.
After adding or changing the points, write canvas.Invalidate() to trigger the Paint event. This will make your drawing persistent.
To learn about persistent drawing minimize & restore the form!
Also you should use a List<Point> instead of an array. This lets you add Points without having to decide on the number of Points you want to support..
To create a new Point you write something like this:
points.Add(new Point(100, start_y) );
To draw you then use this format in the Paint event::
e.Graphics.DrawLines(myPen, points.toArray());
In the constructor you're filling first point as
points[0] = new Point (start_x,start_y);
At this moment, start_x = 0 (since you're not assigned anything else to it after declaration int start_x = 0).
Then in incrementation_Click you're assigning points[1], points[2] and points[3], but you don't changing anywhere in your code points[0].
So when you calling g.DrawLines - first point will always be (0, canvas.Height / 2)
Aside from this:
You don't need to create graphics explicitly in _Paint event handler since it can accessed as e.Graphics.
It's better to move all paintings into canvas_Paint like:
private void canvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLines(myPen, points);
}
and in your _Click handler instead of calling drawLine you should only call canvas.Refresh()
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