behnam
behnam

Reputation: 1143

Draw circle in new position without removing previous circle?

In fact , I want to draw circle in new position each time double-click and without remove before circle ,It should be noted that, I used PictureBox.

public Point postionCursor { get; set; }
List<Point> points = new List<Point>();
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{

    postionCursor = this.PointToClient(new Point(Cursor.Position.X - 25, Cursor.Position.Y - 25));
    points.Add(postionCursor);
    pictureBox1.Invalidate();

    pictureBox1.Paint += new PaintEventHandler(pic_Paint);
}

private void pic_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
       g.SmoothingMode = SmoothingMode.AntiAlias;

    foreach (Point pt in points)
    {

        Pen p = new Pen(Color.Tomato, 2);
        SolidBrush myb = new SolidBrush(Color.White);
        g.DrawEllipse(p, postionCursor.X, postionCursor.Y, 20, 20);
        g.FillEllipse(myb, postionCursor.X, postionCursor.Y, 20, 20);
        p.Dispose();
    }

}

enter image description here

Upvotes: 2

Views: 710

Answers (2)

Stuart Meeks
Stuart Meeks

Reputation: 173

So I got to thinking, and then Visual Studio-ing, that perhaps we don't even need the foreach loop. I still maintain a List so we know where the user has clicked, but there's no need to loop through it and redraw everything every time.

I realize this doesn't handle the case where the underlying list is modified, but nor does the original sample. Here's my entire Form1 class:

public partial class Form1 : Form
{
    private const int CircleDiameter = 20;
    private const int PenWidth = 2;

    private readonly List<Point> _points = new List<Point>();

    public Form1()
    {
        InitializeComponent();

        pictureBox1.Paint += (sender, args) =>
        {
            _points.ForEach(p => DrawPoint(p, args.Graphics));
        };
    }

    private void pictureBox1_DoubleClick(object sender, EventArgs e)
    {
        var cursorLocation = pictureBox1.PointToClient(Cursor.Position);
        _points.Add(cursorLocation);

        var circleArea = new Rectangle(
            cursorLocation.X - CircleDiameter/2 - PenWidth,
            cursorLocation.Y - CircleDiameter/2 - PenWidth,
            CircleDiameter + PenWidth*2,
            CircleDiameter + PenWidth*2);

        pictureBox1.Invalidate(circleArea);
    }

    private static void DrawPoint(Point point, Graphics graphics)
    {
        point.X -= CircleDiameter / 2;
        point.Y -= CircleDiameter / 2;

        using (var pen = new Pen(Color.Tomato, PenWidth))
        using (var brush = new SolidBrush(Color.White))
        {
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.DrawEllipse(pen, point.X, point.Y, CircleDiameter, CircleDiameter);
            graphics.FillEllipse(brush, point.X, point.Y, CircleDiameter, CircleDiameter);
        }
    }
}

Update 1: So I updated the code to use the Paint event which has the foreach loop. However, I don't Invalidate (and Paint) every time a circle is added - there's no need for that. Just adding a circle by drawing means the control only invalidates and re-paints the region where the new circle was added.

Try setting a breakpoint on the DrawAllPoints method. You'll see it only happens during full invalidation operations such as minimizing and restoring.

Update 2: After further chat, I agree the Invalidate method is superior. Code updated to use Invalidate with a rectangle to invalidate.

And now it's looking very much like the OP :)

Upvotes: 0

keyboardP
keyboardP

Reputation: 69372

You're not using the pt variable in the foreach loop.

foreach (Point pt in points)
{
    using(Pen p = new Pen(Color.Tomato, 2))
    using(SolidBrush myb = new SolidBrush(Color.White))
    {
        g.FillEllipse(myb, pt.X, pt.Y, 20, 20);
        g.DrawEllipse(p, pt.X, pt.Y, 20, 20);
    }
}

In your code, you were just overwriting the circle in the same location for every Point in the points list.

Also, as Reza mentioned in the comments, you don't need to attach the PaintEventHandler event hanlder every time the PictureBox is clicked, you just need to do it once.

Upvotes: 4

Related Questions