Click here to Skip to main content
Email Password   helpLost your password?
WPF FlowChart Designer

Introduction

In this article, I have added the following commands:

Note: I will only support Visual Studio 8.0 on .NET 3.5 !

Commands

The way I use WPF commands is straight forward, as described in the WPF SDK documentation, no extra infrastructure.

Grouping

My first approach to group items was to use a DesignerItem object that should work as a group container. For this, I created a new instance of the DesignerItem class with a Canvas object as its content. On this canvas, I planned to position the designer items to be grouped. But before I could put the items on the group canvas, I had to remove them from the designer canvas because in WPF an element cannot be a child of two elements. If you try, you will get an InvalidOperationException with the following message:

"Specified element is already the logical child of another element. 
Disconnect it first."

So I removed the items from the designer canvas and put them on the group canvas. Now it is interesting to understand what WPF did behind the scenes: as soon as I removed an item from the designer canvas, its template was unloaded and when I added it to the group canvas, a new template was loaded. Now do you remember the last article where I showed you how to connect designer items? There I connected items via connectors, connectors that were part of the designer item's template, a template that is lost as soon as I remove the item from the designer canvas. You see the problem? I have connected designer items via their templates and so the designer item itself has absolutely no information about existing connections. All connection related information is isolated in the designer item's template.

Imagine a database diagram where the designer item's content is a database table. The table would never recognize any relation to other tables. One solution would be to tunnel the information from the template to the designer item to the table. A better solution is to redesign the application and divide the whole bulk into separate parts, e.g.

I will not start redesigning this code in the midst of an article, instead I will ride this 'view-only-approach' until the end of this article. The more painful this ride is, the more welcome a better solution will be. (I will cover a model backed designer in a future article.)

So let's continue. An alternative approach to group designer items uses the following interface:

 public interface IGroupable 
 { 
     Guid ID { get; } 
     Guid ParentID { get; set; } 
     bool IsGroup { get; set; } 
 }

The idea is that the DesignerItem class has to implement this interface to become part of the grouping infrastructure, which works like this:

This is simple, but the real work happens when I modify items (Select, Move, Resize, Copy, ...); with each of these operations I have to consider an item's group status. Sounds like a lot of work, but it's not as painful as it would be without LINQ. For this, I have wrapped most of the work into the SelectionService class.

Note: The Connection class does not implement the IGroupable interface and so cannot directly be part of a group, but indirectly - since a connection is always attached to an item. This gives me the flexibility to re/connect items, no matter if they are members of a group or not.

Save

To save a diagram, I have chosen to use a combination of XML and XAML. For the DesignerItem related data I use XML, and the content is serialized to XAML. Here again, please note that serializing a designer item's content to XAML only preserves the visual aspects and thus is used as a short term solution only. To create the XML file, I use LINQ. Since this is the first time I experiment with LINQ, don't expect it to be necessarily the "right" way to use it.

Here is an example of how I serialize designer items:

 XElement serializedItems = new XElement("DesignerItems",
                          from item in designerItems
                          let contentXaml = XamlWriter.Save(((DesignerItem)item).Content)
                          select new XElement("DesignerItem",
                                      new XElement("Left", Canvas.GetLeft(item)),
                                      new XElement("Top", Canvas.GetTop(item)),
                                      new XElement("Width", item.Width),
                                      new XElement("Height", item.Height),
                                      new XElement("ID", item.ID),
                                      new XElement("zIndex", Canvas.GetZIndex(item)),
                                      new XElement("IsGroup", item.IsGroup),
                                      new XElement("ParentID", item.ParentID),
                                      new XElement("Content", contentXaml)
                                      )
                           );

The let keyword allows you to store the result of a sub-expression in a variable that can be used in a subsequent expression. Here I use this feature to save the serialized content in the contentXaml variable, which I use a few lines below. Finally, I use the Save method of the XElement class to store the element's underlying XML tree:

 XElement.Save(fileName)

Open

When loading a diagram from an XML file, we have to start with the designer items because we need their connectors to create connections. We have learned that connectors are part of the item's template, so the designer item has to load its template before we can continue. Fortunately the Control class provides the ApplyTemplate() method which forces the WPF layout system to load the control template so that its parts can be referenced.

In the previous article, I provided a mechanism to customize the ConnectorDecorator template, which allows you to freely position connectors around a designer item. That solution did apply the customized template after the designer item's Loaded event was fired and that event is not fired before the item becomes visible on your screen. Now the screen cannot be redrawn before the command has ended. So the only way is to set the customized ConnectorDecorator template explicitly within the Open command, see the SetConnectorDecoratorTemplate(item) method.

Note: When defining customized connectors, you must set the x:Name property. A connection uses the name to identify its source and sink connectors.

 <s:Connector x:Name="Left" Orientation="Left" 
    VerticalAlignment="Center" HorizontalAlignment="Left"/>

Copy, Paste, Delete, Cut

The Copy and Paste commands work analogous to the Open and Save commands, except that they are applied only to the selected items and that they read and write the serialized content to the Clipboard. The Delete command simply removes all selected items from the designer canvas' Children collection, and the Cut command finally is a combination of Copy and Delete command.

Align, Distribute

Not much to say about these commands, except that the reference item for alignment is the item that was selected at first (also called primary selection). This works only when you select items with the LeftMouseButton + Ctrl, or LeftMouseButton + Shift, but not if you use rubberband selection.

Order

The Panel class (from which Canvas is derived) provides an attached property named ZIndex that defines the order on the z-plane in which the children appear, so we only have to change that property to bring an item forward or backward.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralAbout the save function
Yen Yen
4:21 19 Jan '10  
Hello,

This is the first time i am using microsoft visual C#, i am trying to build a system which has TabControl with two TabItem. The problem that i facing is the way to save both diagrams in a single xml file. Based on your explanation above, i found that you are design an xml file to store the diagram information. However, i can't get the code in your system. May i know where can i found it or where should i declare it. Is that anyone can help? Thank a lot for your help.

Thank.
GeneralZoom
el06b150
6:29 19 Nov '09  
Hallo,

Ich wollte fragen, ob es möglich ist, in Part4 die ZoomBox aus Part2 einzubauen oder irgend eine andere Zoom-Funktion??? Confused

MfG
Stefan
GeneralRe: Zoom
mikesz84
23:55 21 Dec '09  
Replace your designer canvas with this one


<s:DesignerCanvas Focusable="true" x:Name="UIcanvasDesigner"
Background="{StaticResource WindowBackgroundBrush}"
Margin="10" FocusVisualStyle="{x:Null}"
ContextMenu="{StaticResource DesignerCanvasContextMenu}">
<s:DesignerCanvas.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=Value, ElementName=zoomSlider}"
ScaleY="{Binding Path=Value, ElementName=zoomSlider}"/>
</s:DesignerCanvas.LayoutTransform>
</s:DesignerCanvas>


and add this slider somewhere above your DesignerCanvas


<Slider x:Name="zoomSlider" Minimum="0.5" Maximum="2" Value="1" Width="100"/>

GeneralDelete Key on a Connection
caldrak@gmail.com
6:02 27 Oct '09  
When running the code, hitting del on the keyboard will not delete a selected connection. I can't figure out what to change in order to make this work. Does anybody have any ideas?
GeneralRe: Delete Key on a Connection
mikesz84
0:01 22 Dec '09  
Hi, I've stucked on same problem. It looks like that Delete command does not bubble when you have selected (focused) only Connections items. I've created a button to delete all selected connections as a improvized solution but I'm not happy with it.


foreach (Connection element in UIcanvasDesigner.SelectionService.CurrentSelection.OfType<Connection>())
{
this.UIcanvasDesigner.Children.Remove(element);
}


Did anyone find better solution how to make selected Connections react on Delete command?
GeneralThumb inside a Thumb
fernubio
10:24 20 Oct '09  
Excelent article.
This help me a lot to understand Thumb and WPF templates.

I have a problem. I was trying but I´not sure if it is possible to put several Thumbs inside a Thumb.


thank you in advance
GeneralRe: Thumb inside a Thumb
mikesz84
23:52 21 Dec '09  
Hope this helps.

You have to create a DesignerItem with a DesignerCanvas inside. Put this code i.e. on button clicked event handler in Window1.xaml.cs.


DesignerItem it = new DesignerItem();

it.MinWidth = 200d;
it.MinHeight = 200d;

DesignerCanvas c = new DesignerCanvas();
c.AllowDrop = true;
c.IsHitTestVisible = false;

it.Content = c;

it.Position = new Point(X, Y);//you'll have to add this property to DesignerItem to se its position in canvas

this.UIcanvasDesigner.Children.Add(it);

this.UIcanvasDesigner.SetConnectorDecoratorTemplate(it);
this.UIcanvasDesigner.SelectionService.SelectItem(it);



Now you can add any other block into this one.
GeneralWPF VB.NET code - Need help
Member 4113793
4:56 27 Jul '09  
I had the code entirely converted from C# to VB.NET but there remains a few ertrors and I am hoping anyone out there who had a successful conversion can help me out. The problem begins at runtime that when I drag two items onto the canvas and try to connect them the breakpoint halts at this line of code in ConnectorAdorner.vb.

code
  
Private m_hitConnector As Connector
Private Property HitConnector() As Connector
Get
Return m_hitConnector
End Get
Set(ByVal value As Connector)
If Not m_hitConnector.Equals(value) Then
m_hitConnector = value
End If
End Set
End Property



Can anybody tell me what is wrong with this piece of code. Is there a problem with the If Not operator (the equivalent of sukram C# code is If hitconnector != value) I have been tearing out at this problem for a couple of hours but still no solution. I understand that overloading of this operator <> can be done in VB.NET but I dont see anything wrong here. Anybody out there please help!

Thanks

Ricky
GeneralSilverlight flowchart
Madhuribala
2:17 20 Jul '09  
Hi...
Could someone help me to do the same thing in silverlight.I want to open the saved xml file in silverlight only for viewing purposes. When i try to use this project in silverlight , many of the features of wpf arent in silverlight and hence i am unable to work on it.
Kindly give some tips on the same.

Thank you.
GeneralConnector title
Morales_01
6:19 13 Jul '09  
Hi and congratulations for your work.
I have modified the FlowChartStencils for accept a TextBox and add a title to them, then I could get this text from each DesignerItem by reading the content attribute of each one. Now, I want to do the same thing for the connectors but I just got to write them a name inside, but I could not get this text from code because they do not have an attribute "Content".
How do you think I can do this?

Thanks and good bye!
GeneralRe: Connector title
niveditha 2009
3:56 2 Nov '09  
Hi Morales,
can u tell me how u went about adding textbox and reading content attribute.Please explain in detail as I am totally new to wpf.

Thnx n bye,
Niveditha
GeneralWindow.XAML won't load in VS2008 SP1 Designer (FIX)
TheArchitectualizer
8:00 2 Jul '09  
Had some problems loading the Window.xaml in the designer in VS2008 SP1 Team. There were no path bindings for DesignerItem.xaml & Conection.xaml. If you set the Path=. it load fine in the designer.

DesignerItem.xaml Line 135:27
-----------------------------
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent},Path=.}"
ContextMenu="{StaticResource DesignerItemContextMenu}">

-----------------------------
Conection.xaml Line 85:27
-----------------------------

<Canvas DataContext="{Binding RelativeSource={RelativeSource TemplatedParent},Path=.}"
ContextMenu="{StaticResource ConnectionContextMenu}">

-----------------------------
Optionally, a period (.) path can be used to bind to the current source. For example, Text=”{Binding}” is equivalent to Text=”{Binding Path=.}”.
Binding.Path[^]

Cool App!
Thumbs Up
GeneralPart 5
CyberDev
0:07 1 Jul '09  
Is there progress on a Part 5 of this series?

Thanks.
GeneralConnect two node by code
ale.capu80
7:10 10 Jun '09  
Hi,
I want create a diagram with code.

I've created two node like this:

DesignerItem node1 = new DesignerItem();
node1.Content = content1;
MyDesignerCanvas.Children.Add(node1);

DesignerItem node2 = new DesignerItem();
node2.Content = content1;
MyDesignerCanvas.Children.Add(node2);

Now... how can I create a link from node1 and node2 via code ???

Tnks
QuestionProperties for each item
Paralias
23:24 11 May '09  
Hallo there and Congratulations for this great article.

I would like to ask you how can we have properties for each item (node,line,etc...)

I mean: when you click on a start node for example, at the right side of the page having a PropertyGrid (like in Win Form Controls - or something similar) so we can fill some properties, like NodeName,NextNode or anything else...

Can we do that?
GeneralGet type of designer item and automatically display in canvas
Member 4113793
22:41 3 May '09  
Hi

Is it possible to get say for example in your series Part 4 the ellipse and display the ellipses on both sides of a draggable item that I drag say for example a rectangle? If so how do we get the ellipses and join them to the rectangle by means of lines(i.e) connecting all the three?

I have been searching through the forums but to no avail. Any sample code or conceptual view would be helpful. I am also in the process of learning WPF quite a bit and I wanted to extend your example to make it like a Visio type diagram where dynamic connectors can be drawn like when we drag a shape over a connector the connector resizes itself to connect to that shape that was just dragged into the canvas. Is such a thing possible with WPF?

Thanks

Ricky
GeneralHow to dynamically edit content ?
cieszak
22:47 27 Apr '09  
Hi

I have a polygon which the user can edit (move the vertices). In the easiest way to do it based on your example?

Regards,
Cieszak
GeneralWPF Drag and Drop item over arrow splits automatically
Member 4113793
19:13 17 Apr '09  
Hi

In regard to this article I couldn't find a way that would works as follows

When we drag an item over already two joining items connected by an arrow it would split the arrows and that item would be joined automatically between the two with the arrows repositioned.... Is there such a way in DiagramDesigner???

a small sample code or some tutorials might help me out...I have been trying to figure this out

Thanks

Ricky
GeneralListView - Connection
leo uri
4:06 16 Apr '09  
hi all,

This article is GREAT it was sehr helpful, and now i want to make some modifications.

i want to add a Listview with observablecollection for all connections. but i got a argumentexception.

I add one String-Property Name in DesignerItem
the collection is in the Class designercanvas and i modify the OnMouseUP event of the class connector adorner.

(...)

this.designercanvas.children.Add(newConnection);
this.designercanvas.ConnectioncollectionListview.add(newConnection);

(..)

i got this exception

Argumentexception

the specified child element must be of current
parent visual object to be separated before it
a new parent Visual object added.


I did the same with the DesignerItem and all works great but with the connections doesn't work.

thanks

regards

Leonardo





GeneralNeed help in VB.NET
Member 4113793
21:05 15 Apr '09  
Hi Sukram

The article was indeed helpful and when I translated this into VB.NET I keep getting the following errors

1) operator = or <> is not defined for types Object and DiagramDesigner.DesignerCanvas - Source of error happens in DesignerCanvas.vb The same case happens in the rest of other programs where operator are being used....Is there any way to go about this I did used the VB.NET conversion tool to translate the entire source code of your..

I have been fighting this error for the past 2 days but cant seem to get a solution. I believe this has to do something with Operator Overloading but if u could help me in this that will be great

Thanks

Ricky
GeneralRe: Need help in VB.NET
machmuel
6:18 18 Jun '09  
Were you successful?

I need to do exactly the same, since I am not skilled in C#.

I don´t even know how to convert to vb.net.


Could you give me your code?

I would appreciate that very much.
GeneralAdding an another flowchart item to toolbar in the left
asdsdasda
12:53 18 Mar '09  
I want to convert this software to a ER diagram modeler instead of creating a flow chart. So I have to add ER diagram shapes to the toolbar like strong entity weak entity vs. Also I have to remove the arrow in the connector. Do you have solution for this problem?
GeneralRe: Adding an another flowchart item to toolbar in the left
vijayakumarkj
7:30 22 Apr '09  
Hi,

you can change this particular file DiagramDesigner\Resources\Stencils\ShapeStencils.xaml which has list of shapes defined in XAML path element.

You can Use Microsoft Blend to create ER diagram shapes and convert that into path element and paste it in above file. now you can see ER Diagram shapes in left hand box!

and secondly, the connector is created during runtime (check connector.cs class file). however, you might need to explore the class in detail. but, I am pretty sure, you can achieve that.

HTH

KJ
GeneralRegarding Overlap of DesignerItems
Sandeep Srinivas Kulkarni
17:22 15 Mar '09  
Hi Sukram,

Great article, hats off.

I had one small doubt. Could we by any means raise an event when one designer item gets placed on another designer item in the canvas. I mean not on drag and drop. But if we are auto drawing without location co-ordinates.

Is there any way to achieve this.

Can you please help

Regards
Sandeep
GeneralSimulo -- a codeplex project that uses that DiagramDesigner [modified]
king_rollo
8:07 23 Feb '09  
Hello,

Simulo is a project made by me, which can simulate digital circuits. It uses the wonderful DiagramDesigner code files. I would like to thank the author sukram of that article series.
Link to the project: www.codeplex.com/Simulo

Benjamin Gentner

modified on Monday, February 23, 2009 1:20 PM


Last Updated 26 Mar 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010