Pages

Tuesday 5 June 2012

Silverlight FlowChart Designer - Part 1 of 3

Hi ,
    Long time guys . I have been working on that FlowChart designer and Social Networking site in my free time , and things are going quite good.  So here's the thing.  I will take you step by step through creation of a flowchart designer ( Which can also be used as a workflow designer with little modifications ) .  We will do it in a three part series .

1) The first part will contain how to build building basic controls and UI  and implement the dragging functionality.
2) The second part will build upon the first part, and will be covering linking two flow chart items and related functionality.
3) The final part will deal with saving the stuff in different formats , so that it can be used somewhere else.

So lets dive in . 

Step 1:   Create a new Silverlight Navigation application.

  a)  You will find a default set of Views created in the Views folder . 
  b)  Add a new Silverlight Page , and name it FlowChart.xaml.This will contain flowchart designer.
  c)  In MainPage.Xaml , you see that there are two menu items ...Home and About.
  d) Add a third one, by copying and pasting the rectangle and Hyperlink. Name it FlowChart.




Step 2:  Creating the FlowChart designer UI.

 a)  In the FlowChart.xaml ,  we will split the screen into two regions.
      The right region will contain the designer canvas,
      The left region will contain the options to add new items to the designer.
 b)  So the Left region will be a StackPanel  , the right region and the LayoutRoot will be a canvas.
 c)  Add three buttons to the StackPanel, and name them  "Add Start","Add Process", "Add decision".
 d) So your FlowChart.xaml should look like this ... 

<Canvas x:Name="LayoutRoot" Height="340">
 <Canvas x:Name="DesignCanvas" Width="507" Height="317" Canvas.Left="130" Canvas.Top="16"></Canvas>
<StackPanel Canvas.Left="11" Canvas.Top="16" Height="316" Name="FlowItemsList" Width="113">
<Button Content="Add Start" Height="26" Name="btnAddStart" Width="78" Click="btnAdd_Click" />
<Button Content="Add Process" Height="31" Name="btnAddFlowProcess" Width="81" Click="btnAddFlowProcess_Click" />
<Button Content="Add Decision" Height="31" Name="btnAddFlowDecision" Width="81" Click="btnAddFlowDecision_Click" />
</StackPanel>
</Canvas>



 e) Write appropriate event handlers for Click event for the buttons , and leave them empty for now.

Step 3:  Creating the Flow Chart controls .

a)  Create a new Folder in your Project.  Name it FlowItems. Add three usercontrols  to it , named  FlowStart, FlowProcess and FlowDecision . Your project structure should look like this.



b) The FlowStart will represent the start nodeand the the FlowProcess will represent the process node of the flowchart. The FlowDesicion will be diamond shaped,  used to represent the desicion box.
c)  So lets design each of the controls and their properties further.

d) Open  FlowStart.xaml.  
    This should be a rounded rectangle shaped object according to convention.
     i)  Let the size of the control be  d:DesignHeight="39" d:DesignWidth="134"
     ii) Add a rectangle to the grid, and set its RadiusX="30" RadiusY="30"    
     iii)Add a textblock , and set its isHitTestVisible to false  .
   
Your code should look like this
<Grid>
 <Rectangle Height="39" HorizontalAlignment="Left" Name="inputRect" StrokeThickness="1" VerticalAlignment="Top" Width="134" RadiusX="30" RadiusY="30" Fill="#FF41C136" />
<TextBlock IsHitTestVisible="False" Height="23" HorizontalAlignment="Left" Margin="33,10,0,0" Name="txtInput" Text="Start" VerticalAlignment="Top" />
<Grid.Background>
<SolidColorBrush />
</Grid.Background>
</Grid>


e) Open  FlowProcess.xaml
   i) This should be a regualr rectangle with a similar structure
   ii) Set its d:DesignHeight="53"  and d:DesignWidth="96" .
   Just add the following code to that page.
<Grid x:Name="LayoutRoot" Background="Transparent" Width="94">
<Rectangle Height="53" HorizontalAlignment="Left" Name="inputRect" Stroke="Black" VerticalAlignment="Top" Width="97" Fill="LightBlue" Margin="-3,0,0,0" />
<TextBlock Height="29" IsHitTestVisible="False" HorizontalAlignment="Left" Name="textBlock1" Text="Process" VerticalAlignment="Top" Width="82" Margin="2,13,0,0" TextWrapping="Wrap" />
</Grid>
f) Open the FlowDecision.Xaml.
   i) This needs a Rotate transform for the rectangle to be shown like a diamond shape.
   ii) Set its d:DesignHeight="81" and d:DesignWidth="81" .
   iii) Paste the following code

 <Grid x:Name="LayoutRoot" Height="81" Width="95" Margin="0" Background="Transparent">
<Rectangle HorizontalAlignment="Left" Margin="40,1,0,0" Name="inputRect" StrokeThickness="1" VerticalAlignment="Top" Width="55" Height="53" Fill="#FFFFFFAD">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>

These are just the basic shapes that will be used to implement our logic.  In the next part of the series, we will add more controls to these shapes and make them functional.


Step 4:  Enabling Dragging for the Flow Controls .

a) The elements should be draggable over the surface of the designer.  For that to happen , we will need to use the Top and Left properties of the canvas. (And that is also the reason why Canvas is the best control for these sort of drag operations)

b) So we will attach event handlers to the rectangle inside each of the Flow controls. The events that will be monitored are  MouseLeftButtonDown , MouseLeftbuttonUp , MouseMove  and MouseRightButtonDown.

MouseLeftButtonDown will be used to get the initial location of the  control .
MouseLeftButtonUp will indicate that the dragging is completed.
MouseMove will be used to update the location of the control.
MouseRightButtonDown will be used to show a Context menu , to delete the flow item.

This code will be common for all the controls  for now.  As we go further in the Next lesson , we will add more functionality specific to the individual controls.

c) Every flow control should have the following properties ,
   
bool isMouseCaptured;
Popup p;
public Canvas parent;
double mouseVerticalPosition, mouseHorizontalPosition;

d) The double variables will store the initial location of the Flow control , and the popup control will show the delete menu item (which allows you to remove the flow control from the designer).
e) Open the FlowStart.Xaml.cs , and add the above properties to it.
f) In the constructor,  add the code

public FlowStart(Canvas par)
{
InitializeComponent();
inputRect.MouseRightButtonDown +=new MouseButtonEventHandler(inputRect_MouseRightButtonDown);
inputRect.MouseLeftButtonDown += new MouseButtonEventHandler(inputRect_MouseLeftButtonDown);
inputRect.MouseLeftButtonUp += new MouseButtonEventHandler(inputRect_MouseLeftButtonUp);
inputRect.MouseMove += new MouseEventHandler(inputRect_MouseMove);
parent = par;

p = new Popup();
p.Width = 50;
p.Height = 150;
List<Button> clicks = new List<Button>();
Button b1 = new Button();
b1.Width = 50;"Delete";new RoutedEventHandler(b1_Click);StackPanel sp = new StackPanel();foreach(Button b in clicks)    
b1.Height = 30;
b1.Content =
b1.Click +=
clicks.Add(b1);

sp.Width = 50;
sp.Height = 150;

{
sp.Children.Add(b);
}
p.Child = sp;

}

g) If you notice above, i am adding the buttons to a stackpanel , and that becomes the content for the popup menu.  this way, you can add as many buttons as you want to your popup.
h)  Also note that this is common code for all the flow controls. Each one has the exact same code inside them , except their different shapes for now. Copy the event handlers .


The MouseLeftButtonDown will get the inital location of the control and put it in the mouseVerticalPosition  and the mouseHorizontalPosition variables.

void inputRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs args)
{
Rectangle item = sender as Rectangle;
mouseVerticalPosition = args.GetPosition(parent).Y;
mouseHorizontalPosition = args.GetPosition(parent).X;
isMouseCaptured=true;
item.CaptureMouse(); }



The MouseRightButtonDown  will open the Popup menu

void inputRect_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{ e.Handled = true;
p.VerticalOffset = e.GetPosition(null).Y;
p.HorizontalOffset = e.GetPosition(null).X
p.IsOpen = true;
}

The MouseLeftButtonUp  event releases the mouse. 

void inputRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e){
Rectangle item = sender as Rectangle;
isMouseCaptured =false;
item.ReleaseMouseCapture();
mouseHorizontalPosition = -1;
mouseVerticalPosition = -1;}


void inputRect_MouseMove(object sender, MouseEventArgs args)
{
if (isMouseCaptured)
{
double deltaV = args.GetPosition(parent).Y;
double deltaH = args.GetPosition(parent).X;
double newTop = deltaV ;
double newLeft = deltaH;
this.SetValue(Canvas.TopProperty, newTop);
this.SetValue(Canvas.LeftProperty, newLeft);
mouseVerticalPosition = args.GetPosition(parent).Y;
mouseHorizontalPosition = args.GetPosition(parent).X;
}
}
  
The MouseMove will change the location of the control.

This will allow your Flow controls to be draggable over the canvas.  The next step is to add functionality to link elements .  This will be dealt in the next Part of the Series.  

Keep watching for the next post ! :)

2 comments:

  1. Sir you forgot to explain what to add in the "Add Start" "Add Process" "Add Decision" btn code wise.

    ReplyDelete
    Replies
    1. I think you will add something like this example for Add process:

      stDesignCanvas.Children.Add(new FlowProcess(stDesignCanvas));

      Delete