问题
I have a WinForms
program where the user clicks on a PictureBox
control.
I want a small red dot (a few pixels across) every time the user clicks.
I also don't want any of the previous dots to go away.
I know I will need a generic list of ellipses and rectangles, but am not sure how to execute this. How would I go about doing this?
In my program a pictureBox1_Click
method handles mouse click events and returns the position of the clicks.
A pictureBox1_Paint
method handles the graphics to be drawn on these points.
回答1:
You have to create a container that can reference the Points collection and add one point to the collection each time you click on a paint-able Control.
Maybe, you want to also create different kinds of drawing points, based on some conditions or requirements.
Thus, you need to store also these extra properties, not just a point coordinate.
If so, you need a specialized object that can expose these properties when needed.
So, here's a custom Class
object with some simple properties, that lets you define a Point's Color and Size. For each of its Points collection.
It also implements the IDisposable
interface, because we need to create a Pen
object for each Point we draw. And a Pen
object needs to be disposed (implements IDisposable
).
To perform the drawing, you just need to call Control.Invalidate()
(pictureBox1.Invalidate()
in the example). This causes the repainting of the invalidated parts of the control, raising the OnPaint()
event.
Each point (that needs to be repainted) is drawn using e.Graphics.DrawEllipse()
You can test it this way:
With predefined properties, using just a Mouse pointer coordinate:
myPoints.Add(new MyPoints.DrawingPoint(e.Location));
With specific properties when something different is needed:
With a size of 8x8 pixelsnewPoint.Dot = new Rectangle(e.Location, new Size(8, 8)));
With an orange Pen of 2 pixels in sizenewPoint.DrawingPen = new Pen(Color.Orange, 2);
Add this new Point to the collectionmyPoints.DrawingPoints.Add(newPoint);
EDIT:
Adden a Clear()
method to the Main object, used to Dispose()
the current list of Points and create a new, empty, List:MyPoints.Clear();
Sample implementation:
MyPoints myPoints = new MyPoints();
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//Use default property values
//myPoints.Add(new MyPoints.DrawingPoint(e.Location));
MyPoints.DrawingPoint newPoint = new MyPoints.DrawingPoint();
newPoint.Dot = new Rectangle(e.Location, new Size(4, 4));
newPoint.DrawingPen = new Pen(Color.Red, 2);
myPoints.DrawingPoints.Add(newPoint);
((Control)sender).Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (MyPoints.DrawingPoint mypoint in myPoints.DrawingPoints) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(mypoint.DrawingPen, mypoint.Dot);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myPoints.Dispose();
}
The Point objects collection Class container:
internal class MyPoints : IDisposable
{
bool IsDisposed = false;
public MyPoints() => DrawingPoints = new List<DrawingPoint>();
public List<DrawingPoint> DrawingPoints { get; set; }
public void Add(DrawingPoint NewPoint)
{
if (NewPoint.Dot.Size.Width > 1 && NewPoint.Dot.Size.Height > 1)
DrawingPoints.Add(NewPoint);
}
public void Clear()
{
this.Dispose();
this.DrawingPoints.Clear();
this.DrawingPoints = new List<DrawingPoint>();
}
public void Remove(Point point)
{
Remove(this.DrawingPoints.Select((p, i) => { if (p.Dot.Contains(point)) return i; return -1; }).First());
}
public void Remove(int Index)
{
if (Index > -1)
{
this.DrawingPoints[Index].Delete();
this.DrawingPoints.RemoveAt(Index);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool IsSafeDisposing)
{
if (IsSafeDisposing && (!this.IsDisposed) && (this.DrawingPoints.Count > 0))
{
foreach (DrawingPoint dp in this.DrawingPoints)
if (dp != null) dp.Delete();
}
}
public class DrawingPoint
{
Pen m_Pen = null;
Rectangle m_Dot = Rectangle.Empty;
public DrawingPoint() : this(Point.Empty) { }
public DrawingPoint(Point newPoint)
{
this.m_Pen = new Pen(Color.Red, 1);
this.m_Dot = new Rectangle(newPoint, new Size(2, 2));
}
public Pen DrawingPen { get => this.m_Pen; set => this.m_Pen = value; }
public Rectangle Dot { get => this.m_Dot; set => this.m_Dot = value; }
public void Delete()
{
if (this.m_Pen != null) this.m_Pen.Dispose();
}
}
}
This is how it can work, changing its properties when required:
回答2:
Basically you must use GDI+
Check the code bellow:
private void pictureBox1_Click(object sender, EventArgs e)
{
Graphics g = Graphics.FromImage(pictureBox1.Image);
Pen p = new Pen(Color.Red, 3);
var cursorPosition = pictureBox1.PointToClient(Cursor.Position);
g.DrawEllipse(p, cursorPosition.X, cursorPosition.Y, 15, 15);
MyCircles.Add(cursorPosition);
pictureBox1.Refresh();
}
List<Point> MyCircles = new List<Point>();
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
foreach (var item in MyCircles)
{
MessageBox.Show($"One circle was created: X:{item.X}, Y:{item.Y}");
}
}
The MyCircles List is only if you want to store the circles to reshow the image with the circles later, and the doubleclick is a sample of using it (i mean persistency purposes, because the circles will not leave the image until you close your app). Check other g methods to draw lines, rectangles or whatever. (Uses System.Drawing namespace)
回答3:
I came up with a simple solution for my problem I made a list of rectangles:
List<Rectangle> rects = new List<Rectangle>();
And added rectangles with ever click:
private void pictureBox1_Click(object sender, MouseEventArgs e)
{
rects.Add(new Rectangle(e.Location, new Size(4,4)));
}
And painted the points:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (var rect in rects)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(magenta,rect);
}
}
And added a method for clearing drawings with rects.Clear()
来源:https://stackoverflow.com/questions/52599453/how-to-draw-small-dot-on-every-click-of-the-mouse-in-winforms-picturebox