Click here to Skip to main content
Click here to Skip to main content

WPF Diagram Designer - Part 4

By , 26 Mar 2008
 
WPF FlowChart Designer
  • Part 1 - Drag, resize and rotate items on a canvas
  • Part 2 - Toolbox, drag & drop, rubberband selection
  • Part 3 - Connecting items

Introduction

In this article, I have added the following commands:

  • Open, Save
  • Cut, Copy, Paste, Delete
  • Print
  • Group, Ungroup
  • Align (Left, Right, Top, Bottom, Centered horizontal, Centered vertical)
  • Distribute (horizontal, vertical)
  • Order (Bring forward, Bring to top, Send backward, Send to back)

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.

  • Template (view)
  • Designer item (view model)
  • Database table (model)

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:

  • Create a new DesignerItem object with a unique ID and with its IsGroup property set to true
  • For each group member, set the ParentID to the ID of the group parent.

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

  • 25th March, 2008 -- Original version submitted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

sukram
Austria Austria
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to find id on entity setmemberroeikalvo13 Apr '13 - 23:15 
Hi,
 
I have a problem that i can't solve ... I want to find the name of the shapes when i put them on the canvas,
if I drag & drop one of the shapes from the tool box to the designer Item canvas and on mouse click I want message box to open and show me the name of the shape , how can I do that? I can't find the path to the shape name, it's very important that i will get answer very fast so if u can help me i will much appreciated Smile | :)
QuestionFive stars!!!memberKurt Richardson29 Mar '13 - 9:03 
Many thanks indeed - saved me a LOT of time!
QuestionZoom In/Out capabilitymemberyuancn26 Mar '13 - 8:46 
Hi sukram,
 
These serial articles are very helpful to my current project and I really appreciate your contribution.
 
One more functionality I need is zooming in/out.
 
Can you please show me how to add it to this project?
 

Thanks again!
 

yuancn
QuestionHow to draw line on the designer CanvasmemberSujit Bhujbal7 Mar '13 - 20:09 
Hi sukram
 
Very nice code and features.
 
Could you please let me know how to draw line on the designer canvas??
 
Thanks
Sujeet
Thanks
Sujeet
 
Blog: www.sujitbhujbal.blogspot.com
Personal Website :- http://sujitbhujbal.wordpress.com/
Facebook :- www.facebook.com/sujit.bhujbal
CodeProject:-http://www.codeproject.com/Members/Sujit-Bhujbal
www.sujitbhujbal.com

GeneralMy vote of 5memberKunal_Daud26 Feb '13 - 1:13 
We have used this code in our project
QuestionAbout table in ContentControlmemberDimkoc17 Feb '13 - 1:57 
I try to put <ContentControl><Table></Table></ContentControl>
 But it doesnt display the table it just displays string of the parent tree I believe is Widows.Documents.Table.
Do you know what is the issue? Thx
 P.S.
 Sale thing with <ListViewItem><Table></Table></ListViewItem

GeneralMy vote of 5memberEric Ouellet28 Jan '13 - 10:15 
Great, Thanks !
GeneralMy vote of 5memberKruglyj Max22 Nov '12 - 2:15 
You are save my time!!!!
QuestionPathmemberjavorbg17 Nov '12 - 10:27 
how to make this
[^] with Path?
Questionhow to define these shapestencilsmemberjie83041310 Nov '12 - 3:25 
I mean how to draw these shapestencils in this project?
QuestionGreat series of articles!! Have a small issue implementing logic EDIT - Fixed [modified]memberWinstonSmith36 Nov '12 - 18:46 
If you add another form to this project, set it as startup and then use it to open the original form everything works great. But once you close the designer form and attempt to open it again you get a "Specified element is already the logical child of another element. Disconnect it first." error. Try as I might I can't open the main window from this article twice without crashing. Anything I should look at? Cheers for a great resource in teaching me about WPF!!
 
EDIT - found how to fix. I added x:Shared="false" to the
<s:Toolbox and <ToolBar xaml
file calls.
 
so
<ToolBar x:Shared="false" x:Key="MyToolbar" Height="120">
and
 <s:Toolbox x:Shared="false" x:Key="ShapeStencils" ItemSize="60,60">
 
Hope that can help another dev
Soylent Green tastes like chicken


modified 7 Nov '12 - 1:09.

QuestionImages drug and drop [modified]memberzzfima10 Oct '12 - 22:05 
In Part3 was images, that could be drag and dropped. In this example (part4) i added images, but here drag&drop problem - i can drag them only by clicking on border. Can you please, show me what i need to change?
Thanks

modified 11 Oct '12 - 5:23.

Questiongeneralize an algorithm from an activity diagrammemberMember 943920120 Sep '12 - 19:15 
I followed the article (WPF Diagram Designer) my project is to generalize an algorithm
from an activity diagram.
thanks to the article, my project allows me to draw the activity diagram
My problem is how to generate the algorithm, ie how to assign each part of the algorithm at each compsante my activity diagram
and get my algorithm in a text file

thank you for helping me
QuestionHow can I add wpfPropertyGrid in this project?memberaghapour.ahad6 Sep '12 - 1:44 
these 4 projects are notable, but I don't know how can I add wpfPropertyGrid to this project and how I can show some customized property and saved and open customized property for each Item in DesignerCanvas.
 
best regard
SuggestionRead this if you want to add Items and Connections from codememberNotter20 Aug '12 - 2:15 
I needed to create a diagram from code, without saving/reading from XML or whatever.
Unfortunently the Graph doesn't support this very well right now.
So i got a guide for you that i gathered from different comments around here:
 
Add 4 properties to the DesginerItem class, for: Top, Bottom, Left and Right
public Connector ConnectorRight
        {
            get
            {
                var connectorDecorator = this.Template.FindName("PART_ConnectorDecorator", this) as Control;
                connectorDecorator.ApplyTemplate();
                return connectorDecorator.Template.FindName("Right", connectorDecorator) as Connector;
            }
        }
 
Now, i've add a couple of methods you can use in your Window1.xaml.cs:
 
In the DesignerCanvas Class, make SetConnectorDecoratorTemplate a public method.
 
this method creates items, right now just ellipses as content.
(you can remove the brush parameter from this method)
DesignerItem CreateAndAddDesignerItem(Point point, Brush brush)
        {
            var ellipse = new Ellipse { Fill = brush, IsHitTestVisible = false };
            var item = new DesignerItem { Content = ellipse, Width = 80, Height = 80 };
 
            Canvas.SetLeft(item, point.X);
            Canvas.SetTop(item, point.Y);
 
            MyDesigner.Children.Add(item);
            MyDesigner.SetConnectorDecoratorTemplate(item);
 
            return item;
        }
 
next add this little method to easily create connections:
 
void CreateAndAddConnection(Connector source, Connector sink)
        {
            var connection = new Connection(source, sink);
            MyDesigner.Children.Add(connection);
        }
 
and now, to acttuly use them, make a button or something, and it should do this:
 
var mainItem = CreateAndAddDesignerItem(new Point { X = 100, Y = 100 }, Brushes.Blue);
var subItem1 = CreateAndAddDesignerItem(new Point { X = 100, Y = 200 }, Brushes.Red);
 
and finally to connect between them, we'll give the source, and the target, and specify which parts connect:
 
CreateAndAddConnection(mainItem.ConnectorBottom, subItem1.ConnectorTop);

QuestionCompile to library and use in Winform ElementHostmemberczWolfHunter24 Jul '12 - 6:13 
Hello, first of all I would like to thank you for the wonderful series of articles.
 
I would like to ask you, whether anyone of you solve the following situation: Compile the project into *.dll (class library) and then implement it into WinForm control (or WinForm UserControl) over ElementHost (the problem is lost Resources ie. got lost graphics).
 
Best regards,
Petr
QuestionRe: Compile to library and use in Winform ElementHostmemberJ.A.I.L.31 Mar '13 - 22:36 
I am having the same problem: needing a .dll with a WinForm Control with the functionality of this (great) application. But it gives a not found resource error. Any help on how it could be solved is really appreciated.
 
B.T.W: Thank you sukram for posting this great article (+5).
Error!
Signature can't exceed 10Gb of size!

QuestionRotate and Drag group (2 or more selected object) of controlsmembernit6827 Jun '12 - 23:56 
Hi there, Any updates on Rotate and Drag, 2 or more selected controls together.
I am stuck with this.
QuestionCan you convert this to silverlight?memberMehdy Khoshrou15 Jun '12 - 8:23 
Hi
It would be very helpful if you convert this to a silverlight version
Questionhard coded in xamlmemberakohan13 Jun '12 - 21:50 
I see you have hard-coded resources e.g. Process, Decision and etc in xaml files so toolbox.cs and toolboxitem.cs are reading data off the ResourceDictionary but my question (which I am not sure you won't answer but if you do then I would appreciate it) is that how can be done in code behind using C#? in fact adding toolboxitem on toolbox.
 
Nice coding but I don't see any response on this project's page.
QuestionApplicationToolbar Resourcemembersadjad.k28 May '12 - 21:16 
First of all Thanks for the great application which saved me a lot of work. I actually learned a lot of your Article. but there's just this problem I got here, I couldn't make head or tail after hours of searching and reading. in your Window1.xaml you have used a resource for your windows toolbar named ApplicationToolbar.xaml . in that resource you bind some commands to controls. and the command target is the control which has defined in Window1 ( MyDesigner ) . I have done something similar and the problem is in my resource file is not able to see the control to bind target of my commands to it.
Its probably not a big deal but I'm Haven't been able to make it work to this point.
AnswerRe: ApplicationToolbar Resourcememberakohan12 Jun '12 - 11:28 
You have to declare it and its path in your project in App.xaml
 
I hope that would help.
QuestionHow to implement some rules? [modified]memberrdeschain15 May '12 - 5:25 
hi,first of all I wanna say that your diagram saved my life Smile | :) I made some changes like adding a zoombox and adding other shapes for uml diagrams.I'm about to finish my thesis but had a difficulty.I have to implement some rules:
1-for instance an A shape have to be derived from an A and B from B.
2-how can I use diamond or arrow shape by my own will,I mean I may want composition relation or a normal arrow shaped relation.But the rule is I have to have the control.I saw you added a diamond in xaml and I used it but ı cant do the both if I want.
 
I'm in a rush nowadays,please help me Sigh | :sigh:
thanks a lot!

modified 15 May '12 - 13:05.

QuestionVery finememberCIDev25 Apr '12 - 11:27 
Another great WPF article; wish you would write more. Smile | :)
Just because the code works, it doesn't mean that it is good code.

GeneralJust perfect !memberMazen el Senih7 Apr '12 - 1:58 
Oh yeah keep it up !
voted 5 ! Thumbs Up | :thumbsup:
There is always hope ..!

GeneralMy vote of 5memberShahin Khorshidnia2 Apr '12 - 8:22 
Very useful
GeneralMy vote of 5memberCaio Proiete29 Mar '12 - 6:49 
Great article! Keep up the good work.
QuestionThank you lettermemberMokhtarBouslimi29 Mar '12 - 4:13 
Hello , this is an incredible article it helped me alot in my projects
so I wanted to thank you so much , keep forward,
Cordially,
Mokhtar BOUSLIMI
GeneralMy vote of 5memberRAJI @Codeproject22 Mar '12 - 18:56 
realy helpfulllllll
GeneralMy vote of 5memberReza Naghavi5 Mar '12 - 3:21 
This article is very useful for my project
Thanks a lot for best site and projects in its
GeneralMy vote of 5memberSyed Javed29 Feb '12 - 7:41 
Excellent
GeneralMy vote of 5memberarezamoghaddam19 Feb '12 - 21:52 
Proffesional Source code
QuestionColor of connector & marginmemberMember 810845112 Feb '12 - 9:28 
Thanks for the best program can you tell me how can change color of connector and reduce margin between source connector position and the beginning of the path geometry
thanks

QuestionRegarding mouse events on droped itemsmemberMember 84703347 Feb '12 - 2:50 
Hello sir,me vishwa here,
i am not able to get the mouse events on drag and dropped items on canvas,in wpf,so how to get the mouse events on droped items on canvas.means click on droped item that should show new window.please help me to solve this problem.thanks in advance,,,,,,,,
QuestionDragged Item IdmemberMember 853613418 Jan '12 - 3:51 
How do I know which block has been dragged into canvas? because I need to add some functions to the selected block.
Is that dragged block will be a copy of item in toolbox? or is it a new object. I did not get what the code is in DesignerItem.xaml.
GeneralMy vote of 5memberShrishailHalijol17 Jan '12 - 1:09 
Fantastic implementation using WPF.
QuestionSourceID and SinkID from Connectionmembermaksim092 Jan '12 - 11:43 
Hi . Somebody know how to get SoruceID and SinkID ? i try to make some Interface
public interface IConector
{
    Guid SourceID { get; set; }
    Guid SinkID { get; set; }
 

}
and
public Guid SinkID
       {
           get { return (Guid)GetValue(sinkIDProperty); }
           set { SetValue(sinkIDProperty, value); }
 
       }
       public static readonly DependencyProperty sinkIDProperty = DependencyProperty.Register("SinkID", typeof(Guid), typeof(DesignerItem));
       public Guid SourceID
       {
           get { return (Guid)GetValue(sourceIDProperty); }
           set { SetValue(sourceIDProperty, value); }
 
       }
       public static readonly DependencyProperty sourceIDProperty = DependencyProperty.Register("SourceID", typeof(Guid), typeof(DesignerItem));
But it still return 0.
QuestionAdding an item from the toolbox programmatically.memberSeanFitzgerald18 Nov '11 - 12:59 
I want to be able to add an item from the toolbox via code, but I haven't had much luck.
 
I've tried this code, but it's pretty much a hack:
 
                DesignerItem item = new DesignerItem(Guid.NewGuid());                      
                item.Content = FindResource("GridResource"); //Resource Copied from FlowChartStencils.xaml
                Point position = new Point(100, 100);
                DesignerCanvas.SetLeft(item, Math.Max(0, position.X));
                DesignerCanvas.SetTop(item, Math.Max(0, position.Y));
                Canvas.SetZIndex(item, this.Children.Count);
                this.Children.Add(item);
                // item.RenderSize = new Size(...); // ...doesn't work
                SetConnectorDecoratorTemplate(item);
 
When I do it this way, I can't seem to change the size of the DesignerItem . Also, whenever I add a second item, I get a "Specified element is already the logical child of another element. Disconnect it first" error.
 
Has anyone been able to add a toolbox item from code? Is it possible?
 
Thanks for any help!
Questionhow can i drag a usercontrol with this projectmemberebrahim rajabloo26 Sep '11 - 23:50 
hey
im new to wpf
i try this project with own usercontrol,but i have a problem
if the usercontrol's item had name,it give bug,but if i remove usercontrol's item's name it work properly!!!
can you help me why???
Questionhow to get workflow thumbs by namememberpsymon2526 Sep '11 - 1:26 
Hi i am new to wpf and really like this project.
 
I am trying to get a control (templated item) by name
 
i can see you have x:key items in the stencils which have name identifiers such as
x:Key="Process"
 
I would like to be able to capture this on drag / drop do you know how this can be done?
 
many thanks
AnswerRe: how to get workflow thumbs by namememberpsymon2526 Sep '11 - 3:25 
Ok i have it now, i added a tag property to the xaml file for the workflow items then added the following to the save method which works Smile | :)
 
new XElement("ItemTag",((System.Windows.FrameworkElement)(((DesignerItem)item).Content)).Tag),

SuggestionImpressive! Zoom and 'autoarggange' features should be great!memberpl.225 Jul '11 - 22:31 
One of the best WPF App ever
QuestionMovable Line Diagram Designer Code Project [modified]memberRavinderaG20 Jul '11 - 22:39 
Hi Dear,
 
How i can make Connector(line) to be moveable can u help on the same.
 
For reference see feature of Diagram Designer by MindScape Lines
 
Big Grin | :-D
 
Regards
Rv

modified on Friday, July 22, 2011 5:11 AM

Questionplease answer me?memberMember 286001716 Jul '11 - 5:18 
helloConfused | :confused:
but i have a question
i try to add another type element to your program but i gets error
for example i add an button control to id and it works but 2 parts of your program doesn't work :
1) you can not drag drop it
2) when you create a WPF DLL and try to add as a control the program stops.
 

please answer me.
thanks alot
navid
QuestionAre there any license related constraints while using this code?memberBharat Mane11 Jul '11 - 21:11 
Dear Sukram,
Your all four articles on this are fantastic, and have given enough knowledge on building a diagramming tool.
 
Presently I am working on one such tool, when i am referring your all four articles as well as code. The application that I am building is commercial application. I am bit confused in the terms and conditions of COPL.
 
Can you please let me know whether I can use this article and the code of this for my application.
 
Best Regards
Bharat
AnswerRe: Are there any license related constraints while using this code?memberBharat Mane12 Jul '11 - 22:53 
Dear Sukram,
Sorry to bother you again. But since I can start my project work depending on the terms and conditions of this license, so again i am disturbing you. I apologize for the same.
 
As I said earlier, I will be using you article's explanations as well as sample source code that is available in your all four articles for one of our commercial project. I would like to know is there any constraint in the usage of the same.
 
Best Regards
Bharat Mane
QuestionAdding text inside the diagrammembersazzadur23 Jun '11 - 2:45 
Sukram,
 
This is excellent article.
I have a question.
How can I add text inside a shape?
QuestionGetting Namespace error while debuggingmembersazzadur23 Jun '11 - 2:03 
Hi Sukram,
 
I opened this in Visual Studio 2010. When I tried to run this program, it shows the following error for Window1.xaml file:
 
Error 1: Undefined CLR namespace. The 'clr-namespace' URI refers to a namespace 'DiagramDesigner' that is not included in the assembly. Line 4
 
Error 2: Undefined CLR namespace. The 'clr-namespace' URI refers to a namespace 'DiagramDesigner.Controls' that is not included in the assembly. Line 5
 
Error 3: The type 's:DesignerCanvas' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built. Line 46

 
Could you please suggest me how to resolve these problems?
GeneralWould you take on an assignment of porting this diagram for silverlightmemberwmmihaa9 Jun '11 - 4:36 
Hi Sukram,
My company would need a similar framework for silverlight? If interested, please contact me so we could negotiate the terms.
 
Regards
Mikael
 
mikael (dot) hakansson (at) enfo (dot) se
GeneralMy vote of 5memberhichem14723 May '11 - 8:06 
Wonderfull work
thanks to share

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 26 Mar 2008
Article Copyright 2008 by sukram
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid