Introduction
This is the third tutorial of a series of tutorials on 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:
Here in this tutorial, we will start by going through how to make your C# program draw a specific svg path.
Continuing from where we stopped last time .. today, we are going to discuss how to drag and drop objects:
- Some modifications to start with
- Adding mouse move function
Background
Using the Code
1. So Let's Start With Some Modifications to Our Previous Code
As we are going to move objects, we need to remove any king of buffering so let's write this line of code in our main function
:
this.DoubleBuffered = true;
Also let's add some private data members
to tell which object was selected and to tell if we selected any element yet or not:
int selected=0;
bool is_selected = false;
Add a new key listener to listen for the move command so in Form1_KeyDown
:
else if (e.KeyData == Keys.M)
{
action = "move";
}
We would also require a member to hold the value of the left mouse click.
Another one to old a bool
identifying if it was the first time you dragged a object to make a loop and to know which object you selected... otherwise, it would loop through each time you move the mouse and create a rectangle that would be used as a selection boundary box.
private members
MouseButtons m = MouseButtons.Left;
bool first = true;
RectangleF selected_recatngle = new RectangleF();
Also, I have modified the sData
of the heart to be:
private string sData = "M97.5,181.5c-4.5-5-15.6-14.8-24.8-21.7c-27.2-20.5-30.9-23.
5-41.9-33.7c-20.4-18.7-29-37.6-29-63.1c0-12.5,0.9-17.3,4.4-24.6C12,25.9,20.8,16.7,
31.9,11c7.9-4,11.8-5.8,25-5.9c13.8-0.1,16.7,1.5,24.8,6c9.9,5.4,20.1,17,22.2,25.3l1.3,
5.1l3.2-7c18.1-39.6,75.9-39,96,1c6.4,12.7,7.1,39.8,1.4,55.1c-7.4,19.9-21.2,35.1-53.3,
58.4c-21,15.3-44.8,38.3-46.4,41.6C104.2,194.2,106,191,97.5,181.5z";
Now let's make our application more like a graphics application by simply going to the designer
and changing back color to white under properties>>back color >>white:
2. Now Let's Work on Our Drag Drop Functionality
Start by going to the designer of your form and add a mouse move action
:
Then if we go to the back code, we will see private void Form1_MouseMove(object sender, MouseEventArgs e)
created.
We need to start our dragging once the user uses this method ... so the selection technique will only occur in the first time the user uses this function... and once this technique is finished any time the user continues on selecting the object the program won't have to re calculate which object was selected.. so to sum things up ... the program will only loop through the list the first time the user uses the function .. and when he continues on moving this object, the program won't have to re calculate which object ... this will be implemented by a condition when the user first uses this function:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (first == true && e.Button == m && action.Equals("move"))
{
}
}
This will check if it was your first time to enter this function and if you entered it using the left mouse button and if the action was a move action.
Now we need to iterate through our list to know which object was selected ... so first write the foreach
loop to iterate... then, we will put the condition to see if the point of selection was inside any shape of them:
if (sh.draw_svg().Path(render).GetBounds().Contains(e.Location))
Then inside this function, let us define the rectangle that would be used to define the boundaries of the rectangle and set the selected int
to the counter to know which object was selected and set the bool
of selection to true
if this happens to break the foreach
loop else set the is_selected
to false
and of course never forget to increment your counter and if you entered in this condition, then it is your first time ... then the next time you move the object, you won't require it to enter in this condition so simply set first to false
:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (first == true && e.Button == m && action.Equals("move"))
{
int counter = 0;
foreach (shapes sh in shape_list)
{
if (sh.draw_svg().Path(render).GetBounds().Contains(e.Location))
{
selected_recatngle = sh.draw_svg().Path(render).GetBounds();
selected = counter;
is_selected = true;
break;
}
else
{
is_selected = false;
}
counter++;
}
first = false;
}
All the previous was just to define what was the selected shape ... we really need to move it... just first update the bounding rectangle ..then we simply modify the translateX
and translateY
properties inside the shape
class... and call the on paint
function ..
If the object was not selected, it will go to the next else
condition to reset first bool
to false
... so that when you select another shape next time you enter in the function handling knowing which object you selected:
if (is_selected == true && e.Button == m && action.Equals("move"))
{
selected_recatngle.Location = e.Location;
shape_list.ElementAt<shapes>(selected).translateX = e.X;
shape_list.ElementAt<shapes>(selected).translateY = e.Y;
Invalidate();
}
else
{
first = true;
}
So the final mouse move
would be like this:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (first == true && e.Button == m && 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;
break;
}
else
{
is_selected = false;
}
counter++;
}
first = false;
}
if (is_selected == true && e.Button == m && action.Equals("move"))
{
selected_recatngle.Location = e.Location;
shape_list.ElementAt<shapes>(selected).translateX = e.X;
shape_list.ElementAt<shapes>(selected).translateY = e.Y;
Invalidate();
}
else
{
first = true;
}
}
Now let's handle translateX
and translateY
in the shape members that were updated... simply add to the draw svg of both heart and star
this matrix... to just shift the return value before it returns and apply this transform
:
Matrix m = new Matrix();
m.Translate(translateX, translateY, MatrixOrder.Append);
alu.Transform(m);
so that the final draw_svg in both heart and star
would be:
public override Svg.SvgPath draw_svg()
{
Svg.SvgPath pa = new Svg.SvgPath();
Svg.Pathing.SvgPathSegmentList svgSvgPathSegmentList =
new Svg.Pathing.SvgPathSegmentList();
var converter = TypeDescriptor.GetConverter
(typeof(Svg.Pathing.SvgPathSegmentList));
pa.PathData = (Svg.Pathing.SvgPathSegmentList)converter.ConvertFrom(sData);
Svg.ISvgRenderer render = null;
GraphicsPath alu = new GraphicsPath();
alu = pa.Path(render);
Matrix m = new Matrix();
m.Translate(translateX, translateY, MatrixOrder.Append);
alu.Transform(m);
region = new Region(pa.Path(render));
return pa;
}
Now let's draw the selected rectangle... let it be a dotted reb rectangle so in the onpaint
function, add this code:
Point p_temp = new Point((int)selected_recatngle.Location.X, (int)selected_recatngle.Location.Y);
Size sssize = new System.Drawing.Size((int)selected_recatngle.Size.Width,
(int)selected_recatngle.Size.Height);
Rectangle rrrr = new Rectangle(p_temp, sssize);
Pen selection_pen = new Pen(Brushes.Red, 2);
selection_pen.DashStyle = DashStyle.Dot;
e.Graphics.DrawRectangle(selection_pen, rrrr);
The purpose of the above lines is simply to convert RectangleF
to a normal rectangle to be drawn and then to be drawn using a red dashed pen.
Now to delete a specific object, just simply modify in the Form1_KeyDown
handler:
else if (e.KeyData == Keys.Delete && is_selected==true)
{
shape_list.RemoveAt(selected);
RectangleF null_rectangle = new RectangleF();
selected_recatngle = null_rectangle;
is_selected = false;
Invalidate();
}
As we before in the mouse move identified the selected, so just remove it from the list and make the selection rectangle points to null
.
it would be very important for me to know what do you think these tutorials ... are they up to what you have expected ?? ... it would be really important for me to hear your feedback
History
- 8th March, 2016: Initial version