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
- edit the
super_form[desgin]
to add an icon for the new tool, we would call it point_mover_tool
. - 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
lines.cs
class to create a technique to be able to see the points - 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
- continue with the
Form.cs
with the function of the move Form1_MouseMove
to actually move the points. - 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 it point_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;
}
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)
{
.
.
.
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;
}
.
.
.
}
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()
{
.
.
.
create_circles_around_points();
.
.
.
}
now lets create this function create_circles_around_points()
public void create_circles_around_points()
{
circle_points_list.Clear();
foreach (Point p in point_line)
{
GraphicsPath circle = new GraphicsPath();
circle.AddEllipse(p.X-10, p.Y-10, 20, 20);
circle_points_list.Add(circle);
}
}
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)
{
.
.
foreach (lines l in lines_list)
{
.
.
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);
}
}
.
.
}
.
.
}
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)
{
.
.
else if (super_form.super_action.Equals(action.point))
{
if (e.Button == m)
{
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 (circle.IsVisible(with_offset))
{
line.selected_point = count;
selected_point_in_line = counter_for_lines;
Invalidate();
break;
}
count++;
}
counter_for_lines++;
}
}
}
.
.
}
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)
{
.
.
.
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++;
}
.
.
.
}
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))
{
.
.
.
}
else if (super_form.super_action.Equals(action.point))
{
}
}
first we must differ between 2 uses of the point_mover_tool
,
- the first time the user clicks the point to move----> look which point the user selects
- 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 uses move_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))
{
.
.
.
}
else if (super_form.super_action.Equals(action.point))
{
if(e.Button == m)
{
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)
{
}
}
else
{
continous_select = false;
}
Invalidate();
}
}
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))
{
.
.
.
}
else if (super_form.super_action.Equals(action.point))
{
if(e.Button == m)
{
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)
{
lines selected_line_for_point = lines_list[selected_line];
int selected_point = selected_line_for_point.selected_point;
selected_line_for_point.point_line[selected_point] = with_offset;
selected_line_for_point.path_line = new GraphicsPath();
selected_line_for_point.path_line.AddLines(selected_line_for_point.point_line.ToArray());
GraphicsPath circle_g = new GraphicsPath();
circle_g.AddEllipse(with_offset.X - 10, with_offset.Y - 10, 20, 20);
selected_line_for_point.circle_points_list[selected_point] = circle_g;
selected_line_for_point.draw_arrow();
}
}
else
{
continous_select = false;
}
Invalidate();
}
}
- 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)
{
.
.
.
}
if (is_selected && e.Button == m)
{
selected_recatngle.Location = e.Location;
if (selected_type.Equals("shape"))
{
.
.
.
}
if (selected_type.Equals("line"))
{
.
.
.
selected_line_from_list.point_lineF = selected_line_from_list.path_line.PathPoints;
selected_line_from_list.update_circles_around_points();
}
}
else
{
first = true;
continous_select = false;
}
Invalidate();
oldX = e.X;
oldY = e.Y;
}
else if (super_form.super_action.Equals(action.point))
{
.
.
.
}
}
(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();
foreach (PointF p in point_lineF)
{
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++)
{
point_line[i] = new Point((int)point_lineF[i].X, (int)point_lineF[i].Y);
}
}
If you liked the tut Vote it up