Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

[tut 6] Line Drag & Drop & Line Selection

0.00/5 (No votes)
20 Jun 2016 1  
we will build an outline line around the drawn lines so we would test the mouse click on this outline to know which line is selected. and we would in the coming update build a select function inside the move tool

Introduction

This is the Sixth 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:

we will build an outline line around the drawn lines (once the user draws the line arrow; finishes line drawing), so we would test the mouse click on this outline to know which line is selected.

Update

built a select function inside the move tool so that the user would select the objects and lines without the need for the user to move the objects and lines

Background

Map of the tut

 

outline line 

  1. edit the line class (to add the outline line)
  2. edit the move function to determine selected line
  3. edit the move function to move the line and te outline 
  4. edit onPaint() function to draw the line and the outline
  5. only make outline visible when choosing the move tool

 

 

add the selection function to the move tool


1-work with the Form1_MouseClick in Form1 function inside the if of move tool and copy both the technique of knowing which is selected 
2- Deselect Function 
3-Editing the delete condition  tabControl1_KeyDown in the super_form 

 

outline line 

 

 

1-edit the line class (to add the outline line)

we would edit the line class to have the outline (the outline would be a graphics path),

public GraphicsPath outline = new GraphicsPath();

and we would create this outline once the user end drawing his line (from previous tuts we have created a function that is called once the user finishes drawing, this was the draw_arrow()function),

so we would edit the draw_arrow()

the outline would be a copy of the line itself , we would use the clone function, (we would use clone function , due to the concept of C# itself) in C# every object is a pointer, so when you copy a pointer (called b) to a pointer (called a),

object b;

object a = b;

pointer a is not really an independent pointer from b, so changes in object a won't be independent from object b.   So we would use the Clone() function , but we must define the type of the object itself

object b;

object a = (object) b.Clone();

Now in our code

outline =(GraphicsPath) path_line.Clone();

Then we need to use the Widen() function , which is the function that creates a stroke around the line (to serve the concept of line selection)

and the Widen() function needs a pen object, ,which simply creates a pen with a color and a width for this pen, to be the path that is created from the Widen() function.

Pen pen = new Pen(Brushes.Red, 10);
outline.Widen(pen);

so to sum things up the line function would be

public GraphicsPath outline = new GraphicsPath();


public void draw_arrow()
     {
        .//old code

        .

         //new code
         outline =(GraphicsPath) path_line.Clone();

         Pen pen = new Pen(Brushes.Red, 10);
         outline.Widen(pen);
         //new code

         .

         .//old code

     }

2-edit the move function to determine selected line

from the previous tutorials we worked inside the move function , before moving the objects we first worked on knowing which object the user selected, we would do something similar to know which line is selected, but if the user selected an object not a line , then there wouldn't be a need to test lines , so we would use a bool variable know if an object is selected so no need to test the line 

public bool skip_checking_lines = false;

and we would use a string to tell which type is selected

public string selected_type = "";

and inside the move function, the  BOLD is the new code added to the move function

 private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

         //new
       skip_checking_lines = false; //always reset the bool to false
       //new

          if (first == true && e.Button == m && super_form.action.Equals("move"))
            {
                int counter = 0;

                foreach (shapes sh in shape_list)
                {
                    if (sh.draw_svg().Path(render).IsVisible(e.Location))
                    {
                        selected_recatngle = sh.draw_svg().Path(render).GetBounds();
                        selected = counter;
                        is_selected = true;

                        //new
                    skip_checking_lines = true;
                    selected_type = "shape"; 
                    //new

                        break;
                    }

                    else
                    {
                        is_selected = false;
                    }
                    counter++;

                }
                if (!skip_checking_lines)
                {
                   //know which line is selected
                }
             }

.

.//old code of moving objects and new code of moving lines

.

}

now we would work inside the section of knowing which line is selected.

First we would use an integer to hold the selected line

public int selected_line;

here we would write our code within if (!skip_checking_lines) to know which line is selected, just add a foreach loop to loop on all line list,

if (!skip_checking_lines)
                {
                    int line_move_counter = 0;
                    foreach (lines Line in lines_list)
                    {
                       
                       line_move_counter ++;
                    }

                }

then put an if condition to test if the mouse click is inside the outline

  • use the IsVisible function
  • let the selected type be "line"
  • if the test succeeded let the is_selected bool be true
  • if it fails is_selected bool be true
  • if the test succeeded stop further testing using break , but before this use the an integer selected_line  to hold the line_move_counter
if (!skip_checking_lines)
                {
                    int line_move_counter = 0;
                    foreach (lines Line in lines_list)
                    {
                        if (Line.outline.IsVisible(e.Location) ||
                            Line.arrow_path_line.IsVisible(e.Location))
                        {
                            selected_type = "line";
                            is_selected = true;
                            continous_select = true;// will discuss why it is used
                            selected_line = line_move_counter;
                            break;
                        }

                        else
                        {
                            is_selected = false;
                        }
                        line_move_counter++;
                    }

                }

There is an important concept we need to discuss 

no need to test if the mouse to see of it is inside the outline or not , once you tested  First time, I mean you only need to test once and then the user would continue moving the line , so no need to continually test if the mouse inside the outline after you tested it first.

so we would use a bool variable called continous_select and initialize it to false

public bool continous_select = false;

so now when using this concept, the code would be

if (!skip_checking_lines)
               {
                   int line_move_counter = 0;
                   Point with_offset = new Point(e.X - 10, e.Y - 10);//will discuss it
                   foreach (lines Line in lines_list)
                   {
                       if (continous_select == true)
                       {
                           selected_type = "line";
                           is_selected = true;
                       }

                       else if (continous_select != true
                           &&( Line.outline.IsVisible(with_offset) ||
                               Line.arrow_path_line.IsVisible(with_offset)))
                       {
                           selected_type = "line";
                           is_selected = true;
                           continous_select = true;
                           selected_line = line_move_counter;
                           break;
                       }

                       else
                       {
                           is_selected = false;
                       }
                       line_move_counter++;
                   }

               }

but we still need to reset the continous_select bool

the general concept of the mouse_move , we would put reset in the last else

 private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

            if (first == true && e.Button == m && super_form.action.Equals("move"))
             {
             //determining which shape or line is selected
             //which we has edited
             }

            if (is_selected == true && e.Button == m && super_form.action.Equals("move") )
            {
             //move the objects and lines
            }

            else 
            {
              //reset <code>First </code>flag
             continous_select = false;
             }

}

you could have seen that i have used an offset point for testing , we just used an offset of -10 for e.X and -10 for e.Y

Point with_offset = new Point(e.X - 10, e.Y - 10);

this is because from concept of using a custom cursor with a default center Hotspot.

so we need to change the hotspot by a simple offset

 

3-edit the move function to move the line and te outline 

now we would edit the move_function to move the lines, we would edit the section under the                            if (is_selected == true && e.Button == m && super_form.action.Equals("move") ) that works with moving objects and moving lines , but before this we would use 2 integers for moving 

public int oldX, oldY;

so our code would be , don't forget to update the oldx and oldy , we would use a Matrix to move all of

  • the line list itself
  • arrow
  • outline
private void Form1_MouseMove(object sender, MouseEventArgs e) 
{ 
        if (first == true && e.Button == m && super_form.action.Equals("move")) 
        {
        //determining which shape or line is selected
        //which we has edited 
        } 

        if (is_selected == true && e.Button == m && super_form.action.Equals("move") ) 
        {
                if (selected_type.Equals("shape"))
                {
                 //move the objects like in previous code
                }

                if (selected_type.Equals("line"))
                {
                    Matrix ma = new Matrix();
                    ma.Translate(e.X - oldX, e.Y - oldY);

                    lines_list[selected_line].path_line.Transform(ma);
                    lines_list[selected_line].arrow_path_line.Transform(ma);
                    lines_list[selected_line].outline.Transform(ma);
                }

        } 

        else 
        {
        //reset First flag 
          continous_select = false;  
        } 
            Invalidate();

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

4-edit onPaint() function to draw the line and the outline

now we need to draw the outline itself for the user to know where the user would click to select the line 

so edit the onPaint() to be 

foreach (lines l in lines_list)
           {
               //new
               Pen selection_pen_line = new Pen(Brushes.Blue, 2);
               e.Graphics.DrawPath(selection_pen_line, l.outline);
               //new

               e.Graphics.DrawPath(l.pen, l.path_line);
               e.Graphics.FillPath(Brushes.Black, l.arrow_path_line);

           }

 

5-only make outline visible when choosing the move tool 

simply edit the onPaint() to only draw the stroke of the lines only when the action tool is selected

 foreach (lines l in lines_list)
            {
                //new
                if (super_form.super_action.Equals(action.move))
                {
                    Pen selection_pen_line = new Pen(Brushes.Blue, 2);
                    g.DrawPath(selection_pen_line, l.outline);
                }
                //new

                g.DrawPath(l.pen, l.path_line);
                g.FillPath(Brushes.Black, l.arrow_path_line);

            }

 

add the selection function to the move tool

1-work with the Form1_MouseClick in Form1 function inside the if of move tool and          copy the technique of knowing which is selected 

in Form1 in Form1_MouseClick

 private void Form1_MouseClick(object sender, MouseEventArgs e)
        {

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

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

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

//new code
 else if (super_form.super_action.Equals(action.move))
            {
                skip_checking_lines = false;

                int counter = 0;

                foreach (shapes sh in shape_list)
                {
                    if (sh.draw_svg().Path(render).IsVisible(e.Location))
                    {
                        selected_recatngle = sh.draw_svg().Path(render).GetBounds();
                        selected = counter;
                        is_selected = true;

                        //new
                        skip_checking_lines = true;
                        selected_type = "shape";
                        //new

                        break;
                    }

                    else
                    {
                        is_selected = false;
                    }
                    counter++;
                }
                first = false;
            }
//new code

}

 

2- Deselect Function 

here we would solve 2 points.

  • only view selection rectangle either when moving the shape or when you select the shape

  • deselect shapes when selecting in the white background

simply by modifying the previous Form1_MouseClick in Form1 in hte else condition that resulted from not succeeding in selecting a shape

 

  private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            if (super_form.super_action.Equals(action.star))
            {   }

            else if (super_form.super_action.Equals(action.heart))
            {  }

            
            else if (super_form.super_action.Equals(action.line))
            {   }

           else if (super_form.super_action.Equals(action.move))
            {
                skip_checking_lines = false;

                int counter = 0;

                foreach (shapes sh in shape_list)
                {
                    if (sh.draw_svg().Path(render).IsVisible(e.Location))
                    {
                        selected_recatngle = sh.draw_svg().Path(render).GetBounds();
                        selected = counter;
                        is_selected = true;

                        skip_checking_lines = true;
                        selected_type = "shape";
                        break;
                    }

                    else
                    {

                         //new
                        RectangleF null_rectangle = new RectangleF();
                        selected_recatngle = null_rectangle;

                        //new

                        is_selected = false;
                    }
                    counter++;
                }
                first = false;
            }

        }

 

3-Editing the delete condition  tabControl1_KeyDown in the super_form 

edit the condition of  to delete to replace testing the selected bool with seeing selected rectangle if it is null or not

private void tabControl1_KeyDown(object sender, KeyEventArgs e)
        {

            .

            .//old code

            .

            else if (e.KeyData == Keys.Delete && selected_form.selected_recatngle!=null )
            {
                if (selected_form.selected_type == "shape")
                {
                    selected_form.shape_list.RemoveAt(selected_form.selected);

                    RectangleF null_rectangle = new RectangleF();
                    selected_form.selected_recatngle = null_rectangle;

                    selected_form.is_selected = false;
                }
                selected_form.Invalidate();
            }
        }

 

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


  1.  

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here