how to draw multiple draggable and selectable circle?

74 Views Asked by At

i wrote somecode to draw circles on a panel by mouse click, i need to add some feature like selecting and dragging this circles , help me to complete my code please, thanks

Screenshot of running code

CircleItem class:

public class CircleItem
    {
        public bool Selected { get; set; }
        public Color CircleColor { get; set; }
        public Point StartPoint { get; set; }
        public int LineWidth { get; set; }
        public String Caption { get; set; }

        public CircleItem(String caption, Color circleColor,Point startPoint,int lineWidth)
        {
            CircleColor = circleColor;
            StartPoint = startPoint;
            LineWidth = lineWidth;
            Caption = caption;
        }
        public void Draw(Graphics G)
        {
            using (var pen = new Pen(Color.DimGray, LineWidth))
            {
                G.FillEllipse(new SolidBrush(CircleColor), StartPoint.X, StartPoint.Y, 25, 25);
                G.DrawEllipse(pen, StartPoint.X, StartPoint.Y, 26, 26);
                G.DrawString(Caption, new Font("Arial", 9), Brushes.Green, StartPoint.X + 6, StartPoint.Y + 6);
            }
        }
        public bool HitTest(Point Pt)
        {

            return true;
        }

Main code:

public partial class Form1 : Form
    {
        public int circleNumber;
        private Point positionCursor;
        private List<Components.CircleItem> circleItems = new List<CircleItem>();

        public Form1()
        {
            InitializeComponent();
            typeof(Panel).InvokeMember("DoubleBuffered", BindingFlags.SetProperty
            | BindingFlags.Instance | BindingFlags.NonPublic, null,
            picCanvas, new object[] { true });
            picCanvas.Paint += PicCanvas_PaintEventHandler;
        }
        private void PicCanvas_PaintEventHandler(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            foreach (CircleItem cItem in circleItems) cItem.Draw(e.Graphics);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void picCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
        {

        }

        private void picCanvas_MouseClick(object sender, MouseEventArgs e)
        {
            positionCursor = this.PointToClient(new Point(Cursor.Position.X - 25, Cursor.Position.Y - 25));
            if (e.Button == MouseButtons.Left)
            {
                circleItems.Add(new CircleItem(circleItems.Count.ToString(), Color.White, positionCursor, 4));
            }
            else
            {
                foreach (CircleItem cirTag in circleItems)
                    cirTag.CircleColor = cirTag.HitTest(e.Location) ? Color.Red : Color.White;
            }
            picCanvas.Invalidate();
        }

        private void picCanvas_MouseDown(object sender, MouseEventArgs e)
        {

        }
}

Thanks for attention :)

I try to wrote some code to done this, i don't have any good result, circle collision routin needed

2

There are 2 best solutions below

3
Mehdi On BEST ANSWER

i solve selecting circles problem by GraphicPath(),just working on drag and drop circles now

public class CircleItem
{
    private bool Selected { get; set; }
    public Color FillColor { get; set; }
    private int LineWidth { get; set; }
    private String Caption { get; set; }
    private Point Center { get; set; }
    private int Radious { get; set; }

    private GraphicsPath path;
    public CircleItem(String caption, Color fillColor,Point startPoint,int lineWidth,int radious)
    {
        FillColor = fillColor;
        LineWidth = lineWidth;
        Caption = caption;
        Center = new Point(startPoint.X-radious,startPoint.Y - radious);
        Radious = radious;
    }
    public void Draw(Graphics G)
    {
        path = new GraphicsPath();
        using (var pen = new Pen(Color.DimGray, LineWidth))
        {
            G.FillEllipse(new SolidBrush(FillColor), Center.X, Center.Y, Radious-1, Radious-1);
            G.DrawEllipse(pen, Center.X, Center.Y, Radious,Radious);
            G.DrawString(Caption, new Font("Arial", 9), Brushes.Green, Center.X + 6, Center.Y + 6);
            path.AddEllipse(Center.X, Center.Y, Radious, Radious);
        }
    }
    public bool HitTest(Point Pt)
    {
        bool result = false;
        result = (bool)path.IsVisible(Pt);
        return result;
    }
}
1
JonasH On

There are a few things you need to do

Fix your hittest

This should be fairly simple, just compute the distance to the center point of the circle, and either check if this is lower than the radius, or the same as the radius (with some tolerance factor).

Keep track if you are moving the circle

This require a basic state machine. You need to keep track of some 'state' i.e. what circle is selected (if any), if the mouse button is pressed, and an offset from the mouse position to the circle center.

When you MouseDown on a circle you should set all of this state. You then need an event handler for the MouseMove event where you check if the button is pressed, and if so, update the position of the circle center to the mouse position + the offset. You also need a listener for the MouseUp-event to reset the state when the button is released.

You could use Mouse.LeftButton property instead of maintaining a flag for mouseDown yourself, but you would still need to keep track what circle was clicked, if any.