Home » Blog » [tut 7] Drag & Drop points in lines

[tut 7] Drag & Drop points in lines

[tut 7] Drag & Drop points in lines [more…]

Introduction

This is the Seventh tutorial of a series of tutorials of how to build a graphics program using C# in Windows form that exports the artwork in a vector format.

…. also, you would understand how to move, delete, ctrl z your vector art and save it in a special format to be read by your program again.

Also, we will learn how to save XML files… how to export in verilog format… how to use a star algorithm… how to use hand tool… how to manually create the ctrl z technique.

What you will be capable of building:

in this tut we will create a technique to enable the user to control points within a drawn line, by drawing circles around points of the line , when the user clicks a circle he would control the corresponding point.

 

Background

 

Map of the tut

  1. edit the super_form[desgin] to add an icon for the new tool, we would call it point_mover_tool.
  2. edit action enum then, edit the super_form.cs to work with the click function of the icon and the keyword of that tool , the keyword for this tool would be 'P'.
  3. edit the lines.cs class to create a technique to be able to see the points
  4. now we would work with the Form.cs edit the OnPaint inside the Form.cs to actually view the points, this would occur when clicking the point_mover_tool, inside Form1_MouseClick 
  5. continue with the Form.cs with the function of the move  Form1_MouseMove to actually move the points.
  6. update the points when all the line is moved through                                                                                      (a) editing lines.cs class                                                                                                                                      (b) work with the Form.cs with the Form1_MouseMove with the section of moving all the line

 

 

1-edit the super_form[desgin] to add an icon for the new tool , we would call itpoint_mover_tool.

work with the super_form[desgin] , add a button.

set the icon of the button to this image

just change the name to point_tool_button

so the result would be

2-edit action enum then, edit the super_form.cs to work with the click function of the icon and the keyword of that tool , the keyword for this tool would be 'P'.

edit the action enum to have a new action for the new tool, we would call it point action

public enum action
{
    star, heart, line, move,point, none
}

then create a click function for the newly added button

private void point_tool_button_Click(object sender, EventArgs e)
        {
            super_action = action.point;
            toolStrip2.Visible = false;//make the tool of the line proprieties
                                      // invisible when clicking this button
        }

we would add a custom cursor for this tool , use this cursor

point__cursor.zip

and add like discussed in this tut

and edit the click function to be

private void point_tool_button_Click(object sender, EventArgs e)
        {
          super_action = action.point;
          cursor_super = new Cursor(drag_and_drop_and_delete.Properties.Resources.point__cursor.Handle);
          this.Cursor = cursor_super;
          toolStrip2.Visible = false;
        }

also don’t forget to edit the tabControl1_KeyDown to add a new keyword for this new tool , it would simply contain the same code like in the function for the button of the point tool.

private void tabControl1_KeyDown(object sender, KeyEventArgs e)
        {

             .//old code

             .

             .  

       //new code
       else if (e.KeyData == Keys.P)
       {
         super_action = action.point;
         cursor_super = new Cursor(drag_and_drop_and_delete.Properties.Resources.point__cursor.Handle);
         this.Cursor = cursor_super;
         toolStrip2.Visible = false;
       }
       //new code

           .

           .

          .//old code

          }

 

3-edit the lines.cs class to create a technique to be able to see the points

the concept of moving the points would require a shape visible to the user on the required point to move ,which would enable the user to move this shape in order to move the point.

so lets use circles as the shape around th points, lets put the circles inside a list

public List<GraphicsPath> circle_points_list = new List<GraphicsPath>();

lets use an int to tell which point is selected, and lets initialize it with -1

public int selected_point = -1;

so we require to draw this shape around all points, when the line is done drawing , so we would work with the function of  draw_arrow() which was called when the line is finished drawing

then call inside the draw_arrow() function a new function that would create circles around all points

public void draw_arrow()
        {
           . 
           .//old code

           . 

           

            //new code
            create_circles_around_points();
            //new code

           . 
           .  //old code

           . 

        }

now lets create this function create_circles_around_points()

public void create_circles_around_points()
        {
            circle_points_list.Clear();//begin with clearing all the circle list

            foreach (Point p in point_line)//iterate through all points in the lines class 
            {
                GraphicsPath circle = new GraphicsPath();

                circle.AddEllipse(p.X-10, p.Y-10, 20, 20);//create circle around each point
                circle_points_list.Add(circle);//and add it to circle list
            }

        }

4-now we would work with the Form.cs edit the OnPaint inside the Form.cs to actually view the points, this would occur when clicking the point_mover_tool, inside Form1_MouseClick 

now lets return back to working with Form.cs, we would start with viewing the points after the user finishes drawing the line and when he clicks the point_mover_tool.

so we would start working with OnPaint function, to just view the circles around the points , to tell the user where to click to control the required point .

protected override void OnPaint(PaintEventArgs e)
        {

         .//old code

         .

            foreach (lines l in lines_list)
             {
             .//old code
             .

                //new code 

                if (super_form.super_action.Equals(action.point))
                {
                    foreach (GraphicsPath circle in l.circle_points_list)
                    {
                            Pen selection_pen_line = new Pen(Brushes.Chocolate, 2);
                            g.DrawPath(selection_pen_line, circle);
                    }
                }
               
               //new code

            .
            .//old code
            }
        .

        .//old code
}

then we would work with Form1_MouseClick , to enable the user to click the required  points that he needs to move.

to avoid selecting 2 points from 2 lines we would use an integer to tell which line is used to control its points.

public int selected_point_in_line=0;
 private void Form1_MouseClick(object sender, MouseEventArgs e)
        {

       . //old code

       .

            //new code
            else if (super_form.super_action.Equals(action.point))
            {
                if (e.Button == m)//enable the if condition when the user clicks on the left 
                                  //button of the mouse 
                {
                    //know which point is selected
                    Point with_offset = new Point(e.X - 10, e.Y - 10);//offset because of using
                                                                      //a custom cursor
                    int counter_for_lines = 0;

                    foreach (lines line in lines_list)
                    {
                        int count = 0;

                        foreach (GraphicsPath circle in line.circle_points_list)
                        {
                            if (circle.IsVisible(with_offset))
                            {
                                line.selected_point = count;//set the selected_point variable 
                                                            //inside the line object to the count 
                                                            //of the selected point

                                selected_point_in_line = counter_for_lines;
                                Invalidate();

                                break;//once found you don't need to continue looping through all points
                            }
                            count++;
                        }

                    counter_for_lines++;
                    }

                }
            }
            //new code

       . 

       .//old cod

}

now after setting the selected point variable inside the line object , we need to view the selected point in a different way , so lets make the selected point having a solid color

so lets edit the OnPaint function again , to view the selected point in a different way of having a solid background

protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            int counter_line = 0;

            foreach (lines l in lines_list)
            {
               .
               . //old code
               .    

                //new point
                if (super_form.super_action.Equals(action.point))
                {
                    foreach (GraphicsPath circle in l.circle_points_list)
                    {

                    int count = 0;

                        if (count == l.selected_point && counter_line == selected_point_in_line)
                        {
                            g.FillPath(Brushes.Chocolate,circle);
                        }
                        else
                        {
                            Pen selection_pen_line = new Pen(Brushes.Chocolate, 2);
                            g.DrawPath(selection_pen_line, circle);
                        }
                        count++;
                    }
                }
                counter_line++;

                //new point

            }

               .
               . //old code
               .    

}

 

 

5-continue with the Form.cs with the function of the move  Form1_MouseMove to  actually move the points.

now lets work on the function that would really move the points, so we would work on the Form1_MouseMove ,we would creae a new codition for the new action

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    if (super_form.super_action.Equals(action.move))
    {
     .

     .//old code

     .
    }

    //new code
    else if (super_form.super_action.Equals(action.point))
    {

    }
    //new code
}

first we must differ between 2 uses of the point_mover_tool ,

  1. the first time the user clicks the point to move—-> look which point the user selects
  2. once the user selected the point to move , no need to loop to find which point the user needs to move , as he is still holding the point —-> move the point itself

so we would use a bool called continous_select (was used before in the move_tool) but the user either usesmove_tool or the point_mover_tool , so we would use only one bool for both of the two tools

we would work the first time the user selects the required point , ( condition with continous_select=false ) here we would use the same code that was used before in Form1_MouseClick to know which point is selected

private void Form1_MouseMove(object sender, MouseEventArgs e) 
{ 
    if (super_form.super_action.Equals(action.move)) 
    { 
      . 
      .//old code 
      . 
    } 

    //new code 
    else if (super_form.super_action.Equals(action.point)) 
    {
                if(e.Button == m) //only work when left_mouse_click is selected
                {
                    //know which point is selected
                    Point with_offset = new Point(e.X - 10, e.Y - 10);
                    int counter_for_lines = 0;

                    foreach (lines line in lines_list)
                    {
                        int count = 0;
                        
                        foreach (GraphicsPath circle in line.circle_points_list)
                        {
                            if (!continous_select)//first condition when the user clicks the
                                                  // point for the first point
                            {
                                if (circle.IsVisible(with_offset))
                                {
                                    line.selected_point = count;
                                    selected_line = counter_for_lines;
                                    continous_select = true;//here make the user hold the point 
                                    selected_point_in_line = counter_for_lines;
                                    break;// no need to continue looping 
                                }
                            }
                           
                            count++;
                        }
                        counter_for_lines++;
                    }
                    
                    if (continous_select)
                    {
                         //movement itself
                    }
                }

                else//when the user doesn't click the left_mouse_click
                {
                    continous_select = false;//reset the holding bool
                }

                Invalidate();//redraw the form

    }
    //new code 

}

now we would work on the second condition , that of when the user holds the point so we work on moving the points themselves

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    if (super_form.super_action.Equals(action.move))
    {
      .
      .//old code
      .
    }

    //new code
    else if (super_form.super_action.Equals(action.point))
    {

               if(e.Button == m)
                {
                    //know which point is selected
                    Point with_offset = new Point(e.X - 10, e.Y - 10);
                    int counter_for_lines = 0;
                    foreach (lines line in lines_list)
                    {
                        int count = 0;
                        
                        foreach (GraphicsPath circle in line.circle_points_list)
                        {
                            if (!continous_select)
                            {
                                if (circle.IsVisible(with_offset))
                                {
                                    line.selected_point = count;
                                    selected_line = counter_for_lines;
                                    continous_select = true;
                                    selected_point_in_line = counter_for_lines;
                                    break;
                                }
                            }
                           
                            count++;
                        }
                        counter_for_lines++;
                    }
                    
                    if (continous_select)
                    {

                        //actually move point
                        lines selected_line_for_point = lines_list[selected_line];
                                                        //get the selected line from the list
                        int selected_point = selected_line_for_point.selected_point;
                                             //get index of the selected point

                        selected_line_for_point.point_line[selected_point] = with_offset;
                        //use the index of the selected point and update that point with the position
                        //of the mouse 
 

            selected_line_for_point.path_line = new GraphicsPath(); //reset the whole line
            selected_line_for_point.path_line.AddLines(selected_line_for_point.point_line.ToArray());
            //create the line with the updated points

                        //move the circles
                        GraphicsPath circle_g = new GraphicsPath();
                        circle_g.AddEllipse(with_offset.X - 10, with_offset.Y - 10, 20, 20);
                        //create a new circle with a new position

                        selected_line_for_point.circle_points_list[selected_point] = circle_g;
                        //update the circle list with the new circle , using the index of the 
                        //selected point

                        //update outline and arrow
                        selected_line_for_point.draw_arrow();


          }
                }

                else
                {
                    continous_select = false;
                }

                Invalidate();

            }
            //new point
        }

 

  1. update the points when all the line is moved through                                                                                      (a) editing lines.cs class                                                                                                                                      (b) work with the Form.cs with the Form1_MouseMove with the section of moving all the line

6-update the points when all the line is moved

don’t forget that we haven’t yet solved the condition of updating the points when the line is moved, in the previous tuts we were only concerned with viewing the line when moving without really moving the line’s points

so we would work on two points

(a) work with the Form.cs with the Form1_MouseMove with the section of moving all the line

(b) editing lines.cs class

 

(a) Form1_MouseMove

we would edit the section of the move action inside the Form1_MouseMove , we would use

PointF[]=GraphicsPath.PathPoints

but this would return array of PointF , so we would create in Lines.cs class a poinf array

in lines.cs

        public PointF[] point_lineF;

 

in Form1_MouseMove

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (super_form.super_action.Equals(action.move))
            {
                skip_checking_lines = false;
                if (first && e.Button == m)
                {
                    if (!skip_checking_lines)
                    {
                       .
                       .//old code
                       .
                    }

                if (is_selected && e.Button == m)
                {
                    selected_recatngle.Location = e.Location;
                    if (selected_type.Equals("shape"))
                    {
                          .
                          .//old code
                          .
                    }

                    if (selected_type.Equals("line"))
                    { 
                         .
                         .//old code
                         .

         //new code

         selected_line_from_list.point_lineF = selected_line_from_list.path_line.PathPoints;
         selected_line_from_list.update_circles_around_points();
         //we would create this function in the lines.cs 
 
         // this function would update the circle list and the point list 
         // (of normal points not the pointf)
         //from the pointf_array that we have just created in the lines.cs

         //new code


                    }
                }
                else
                {
                    first = true;
                    continous_select = false;
                }

                Invalidate();

                oldX = e.X;
                oldY = e.Y;
            }

            else if (super_form.super_action.Equals(action.point))
            {
                    .
                    .//old code
                    .
            }

        }

 

(b) create the update_circles_around_points in the lines.cs class

this function would update the circle list and the point list (of normal points not the pointf) from the pointf_array that we have just created in the lines.cs

 public void update_circles_around_points()
        {
            circle_points_list.Clear();//clear the circles point list

            foreach (PointF p in point_lineF)//update the circles point list
            {
                GraphicsPath circle = new GraphicsPath();
                circle.AddEllipse(p.X - 10, p.Y - 10, 20, 20);
                circle_points_list.Add(circle);
            }

            for (int i = 0; i < point_lineF.Count(); i++)//update the point list
            {
                point_line[i] = new Point((int)point_lineF[i].X, (int)point_lineF[i].Y);
            }
        }

If you liked the tut Vote it up Smile | :)