How to instantiate images upon user click in windows forms - c#

I have small Image for a circle and I want to make the following:
Whenever some place on my form is clicked, I want to add a new instance of that circle in that place if there is no other circle there already.
I was thinking about a list of Circles and when that click happens i check the list to see if none of its circles is overlapping before adding the new one but I don't have any experience with forms so i don't know what would be the best approach for that.

You can build up a GraphicsPath and check if the clicked point is inside any of its parts with the IsVisible method.
This code also builds up a list of the points and to draws the image to each of it in the Paint event. If you let the GraphicsPath do the drawing you uncomment the DrawPath line and delete these //** list related lines.
GraphicsPath GP = new GraphicsPath();
List<Point> PL = new List<Point>(); //**
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
int diameter = 22; // put in the size of your circle
Size s = new Size(diameter, diameter);
if (!GP.IsVisible(e.Location))
{
Point middle = new Point(e.X - diameter / 2, e.Y - diameter / 2);
GP.AddEllipse(new Rectangle(middle, s));
PL.Add(middle); //**
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// e.Graphics.DrawPath(Pens.Firebrick, GP);
Image img = new Bitmap("D:\\circle22.png"); //**
foreach(Point pt in PL) e.Graphics.DrawImage(img, pt); //**
img.Dispose(); //**
}

Related

C# WindowForm How can I make line cursor in PictureBox?

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:

Drawing a new circle bitmap at the click location while preserving previously drawn circles

I am trying to draw circles using Bitmap.
Each time I click the mouse, the circle I previously drew is moved to the new position.
What I want to happen is: Each time I click the mouse, a new circle is created/drawn at the position I clicked and all previously drawn circles remain without moving.
I am working with the following code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace multirectangle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
Bitmap background;
Graphics scG;
Rectangle rectangleObj;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
rectangleObj = new Rectangle(10, 10, 30, 30);
scG = Graphics.FromImage(background);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
rectangleObj.X = clickPrev.X - rectangleObj.Height / 2;// +radius;
rectangleObj.Y = clickPrev.Y - rectangleObj.Width / 2;
Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
scG.Clear(SystemColors.Control);
scG.DrawEllipse(myPen, rectangleObj);
return background;
}
}
}
Your English was a little confusing. If I'm understanding your problem correctly, right now the only thing that's being drawn is the new circle where the click was, and you want all the old ones to persist as well? In which case, there are two options:
Don't clear the bitmap before you draw. scG.Clear(SystemColors.Control); will clear the bitmap you just drew. If you remove that line and don't clear the bitmap, then the next time you click, it will then draw the new ellipse right on top of the last bitmap.
If you want a fresh drawing/bitmap everytime, you would need a list of your rectangleObj . Each time you click, you add that point to your rectangleObj collection. Then in your draw method, you would iterate through the collection and draw all of them.
I notice a few things. First, in Form1_MouseDown(), you have this:
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
You are overwriting the old position (clickPrev) before you even save it. If you want to keep both positions, you should put them in a simple structure, like a List. When you get a new point, just Add() it to the list. Then, in your Draw() routine, loop over all the elements in the list and draw them all.
If you just want two positions--and only two--just swap your statements like this:
clickPrev = clickCurrent;
clickCurrent = PointToClient(Cursor.Position);
And you'll have to allocate another rectangle object for the drawing, although it would make more sense to take care of this in the Draw() routine.
Swap the position of the following statements
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
I think you are assigning the clickCurrent to clickPrevious after you initialize clickCurrent. It needs to be the other way.
Please try this
Rectangle rectangleObj;
Bitmap background;
Graphics scG;
Pen myPen;
private void Form1_Load(object sender, EventArgs e)
{
rectangleObj = new Rectangle(10, 10, 30, 30);
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
myPen = new Pen(Color.Red, 3);
BackgroundImage = background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
var point = PointToClient(Cursor.Position);
rectangleObj.X = point.X - rectangleObj.Height / 2;
rectangleObj.Y = point.Y - rectangleObj.Width / 2;
scG.DrawEllipse(myPen, rectangleObj);
Refresh();
}
OnPaint and Draw methods removed. As well as clickCurrent and clickPrev fields.
When you change the form size (for example, maximize it), Bitmap and Graphics remain the same, so you get this effect. To avoid this, you need to add the event handler
private void Form1_SizeChanged(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
BackgroundImage = background;
}
Note that each time you resize the form, all previously drawn is erased. If this is undesirable, a different approach is needed for drawing. Let me know, I will give another example.

How to keep two colors circles together

The goal of this little program I am working on is to draw a red circle while radiobutton1 is checked, and draw a black circle while radiobutton2 is checked.
Below is my code, the problem with this code is when radiobutton1 is checked, it does draw red circles, but then if I click radiobutton2, then all the red circles will turn black. Then if check radiobutton1 again, the all the dots will turn red again.
How do I keep both color circles on the panel?
List<Point> points = new List<Point>();
Graphics g;
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
points.Add(e.Location);
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
int count = 0;
if (radioButton1.Checked)
{
g = e.Graphics;
while (count < points.Count())
{
g.FillEllipse(Brushes.Red, points[count].X, points[count].Y, 10, 10);
count++;
}
}
else if (radioButton2.Checked)
{
g = e.Graphics;
while (count < points.Count())
{
g.FillEllipse(Brushes.Black, points[count].X, points[count].Y, 10, 10);
count++;
}
}
}
The way graphics works in Windows Forms is, the Paint method redraws the entire panel.
You are already drawing every point every time the paint method executes: this is correct.
But you have no way of remembering which point is supposed to be which color, so all you have to go on when you do the painting is the current values of the radiobuttons. You need some way of recording the current color when you add a circle.
One way to do this would be to define a Circle class which stores the location and color of a circle:
class Circle
{
public Point Location { get; set; }
public Brush Fill { get; set; }
}
Then instead of points being a List<Point>, it can be a List<Circle>, and when you see a mouse click, you can add a new Circle instead of a Point:
var circle = new Circle()
{
Location = e.Location,
Fill = radioButton1.Checked ? Brushes.Red : Brushes.Black
};
points.Add(circle);
And when you do the painting, you can check each circle's color as you draw them - all you have to do is this:
foreach (var circle in points)
{
e.Graphics.FillEllipse(circle.Fill, circle.Location.X, circle.Location.Y, 10, 10);
}
Note that you do not need a member level Graphics g - and it's a bad idea to keep a Graphics object after the Paint method has finished. It will not necessarily still be valid later. Always just use e.Graphics.
I also replaced your while loop with a simpler foreach.
If I understand your intent correctly, you need to keep two lists of points, one for each color. Then, when you click somewhere, put the clicked point in the appropriate list (red or black). Then, in your Paint event handler, replace the conditional code with two loops, one through each list of points (drawing the points from the red list in red and the points from the black list in black).
Code:
List<Point> redPoints = new List<Point>();
List<Point> blackPoints = new List<Point>();
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
if (radioButton1.Checked)
redPoints.Add(e.Location);
else
blackPoints.Add(e.Location);
panel1.Invalidate();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
int count = 0;
Graphics g = e.Graphics;
foreach (Point p in redPoints)
{
g.FillEllipse(Brushes.Red, p.X, p.Y, 10, 10);
}
foreach (Point p in blackPoints)
{
g.FillEllipse(Brushes.Black, p.X, p.Y, 10, 10);
}
}
Note: if your circles overlap one another and you care about maintaining the layering order (first-clicked circles drawing first), then #Blorgbeard's solution is better because it keeps all the circles in the same list, thus maintaining the original layering. Feel free to switch the accepted answer.

How do i reset/clear all the drawings i did on pictureBox1?

I have a pictureBox1 with image inside and when i click on it its drawing points.
Now i added a reset button i called it when i click on it its should clear all the drawings i did on the pictureBox and leavethe image inside without the drawings on it.
I did:
private void button4_Click(object sender, EventArgs e)
{
Graphics graphics;
graphics = pictureBox1.CreateGraphics();
graphics.DrawImage(pictureBox1.Image, 0, 0);
}
So i draw a lot of points on pictureBox1 then click the button and all points are gone but then once i click on the picturebox1 again i see also the new points but also the old points i did before the clearing.
How can i clear the old drawings so it wont show up on the next clicks ?
This is the paint event: Moved the paint event to a new class:
public static void Paint(List<PointF> pb1points, GraphicsPath pb1gp, Point movingPoint, PictureBox pictureBox1, Graphics e)
{
e.Clear(Color.White);
e.DrawImage(pictureBox1.Image, movingPoint);
Pen p;
p = new Pen(Brushes.Green);
foreach (PointF pt in pb1points)
{
e.FillEllipse(Brushes.Red, pt.X, pt.Y, 3f, 3f);
}
using (Pen pp = new Pen(Color.Green, 2f))
{
pp.StartCap = pp.EndCap = LineCap.Round;
pp.LineJoin = LineJoin.Round;
e.DrawPath(pp, pb1gp);
}
}
You can try using Graphics.Clear().
Reference: http://msdn.microsoft.com/en-us/library/system.drawing.graphics.clear(v=vs.110).aspx
setting the Image property to null should work.
picBox.Image = null;
Ii it's not worked ,you might be used the InitialImage property to display your image.
pictBox.InitialImage = null;
Please refer the link:
Clear image on picturebox
This is working:
private void button4_Click(object sender, EventArgs e)
{
Graphics graphics;
graphics = pictureBox1.CreateGraphics();
graphics.DrawImage(pictureBox1.Image, 0, 0);
pb1points = new List<PointF>();
}

Drawing a path with multiple vertices on a panel

I have a task of drawing a path on a panel using mouse click. The path should be some thing like left
click and release at (x1, y1), move the mouse and then left click and release at (x2, y2). Then a line should be drawn from (x1, y1) to (x2, y2). Now this time when I move my mouse to other location say (x3,y3) and then left click & release, a line should be drawn from (x2, y2) to (x3,y3).
In this manner I want to draw a path with multiple vertices say up to (Xn, Yn).
Currently I could able to draw only from (x1, y1) to (x2, y2).
using (Pen draw_pen = new Pen(Color.Blue, PEN_WIDTH))
{
g.DrawLine(draw_pen, _StartPt.X, _StartPt.Y, _EndPt.X, _EndPt.Y);
}
Does anyone can let me know whether this is achievable ? If yes can you please provide sample code snippet. Thanks in advance.
Use a list or a collectible of your choice, then whenever a click/letgo happens add the current point to your list, after adding draw over your complete list, always from one element to the next until the end.
You can use a GraphicsPath to represent it. Here's a simple example:
public partial class Form1 : Form
{
private int PEN_WIDTH = 5;
private Point lastPoint = new Point(-1, -1);
private System.Drawing.Drawing2D.GraphicsPath GP = new System.Drawing.Drawing2D.GraphicsPath();
public Form1()
{
InitializeComponent();
this.Paint += Form1_Paint;
this.MouseDown += Form1_MouseDown;
}
void Form1_MouseDown(object sender, MouseEventArgs e)
{
Point pt = new Point(e.X, e.Y);
if (lastPoint.X == -1 && lastPoint.Y == -1)
{
lastPoint = pt;
}
else
{
GP.AddLine(lastPoint, pt);
this.Refresh();
}
lastPoint = pt;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
using (Pen draw_pen = new Pen(Color.Blue, PEN_WIDTH))
{
Graphics g = e.Graphics;
g.DrawPath(draw_pen, GP);
}
}
}
Obviously you could track the number of clicks if you wanted to stop after a certain number of vertices. You could use a List<GraphicsPath> to represent more than one set of lines.

Categories

Resources