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:

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.

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.

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.

Could you please help me, how to resize the multiple controls (two or more ) without grouping, so that it will maintain the scale and relative positioning? I found that only grouping the controls and then resize, allowing to maintain the scale and positioning.

I need to maintain the scale and relative positioning without grouping.

Hi ,
you did a nice work !
But what can i modify the background color of a designeritem ?
In the project there is a gradient color but i would like to change the color with a desire one at runtime.
what can i do ?
please help me

Has anyone developed a way for Connections to avoid collisions with DesignerItems (but allow crossing other Connections)?

I am programmatically adding DesignerItems and Connections to the same Canvas.SetZIndex of 0. I have tried passing all DesignerItems as Rect[] to OptimizeLinePoints() and IsPointVisible(), which in turn should pass to RectangleIntersectsLine(). I thought this would feed back through and force lines to be drawn avoiding the DesignerItems, but this hasn't worked.

because otherwise DesiredSize is set to (NaN; NaN), and later, in DesignerCanvas.cs, dragObject.DesiredSize.HasValue will be evaluated to true. Consequently, a new item will be placed at the origin of the diagram canvas, irrespective of a drop location.

Alternatively, one can set DesiredSize based on the proper designeritem size (but not to NaN), like that:

i add an image box to the canvas and i add the image dynamically by using a browser button but when i try to save and open it again the image is not displayed , so what can i do to save the image like the designer items?

I know it's been asked a couple of times before but if anybody has managed to attach a propertygrid to the objects please share with me. Under a time crunch and I have to get this done. Would really appreciate it.

There's a simpler way to copy and paste collections to and from the Windoze clipboard, and clone, open, save them or whatever. You don't need all the Linq stuff or dozens of lines of XElement code like this guy uses.

The ArrayList class is serializable. All one needs to do is create an ArrayList and add the objects in question to it. You can then `xaml-serialize/deserialize the whole ArrayList and copy, paste, save it or whatever. On deserialization, you'll get an ArrayList which needs to have its members cast to the proper type in the usual way.

Here's a C++/CLI example of how to wrap/unwrap a Canvas' Children collection for serialization/deserialization:

It's pretty simple to implement. You can basically use the same code from Part 1 or 2, or whatever. The main change is that you need an array/list/collection of `initialAngle' guys, one for each selected item. Then in Dragdelta() you'll need to update each `initialAngle' by the rotation angle and set each selected item's RotateTransform to that new value. To get a list of `selected' items, see how its done in the ResizeHandle or MoveHandle.

OK this is 5 years later just to tell you that your article series still rocks big time.

I just implemented your RubberBandAdorner, ResizeAdorner, and the Align, Same Size, and Distribute functions in a more MVVM compliant way than it was presented here. So this should be interesting for anyone wanting to use your work in an MVVM design...

Check this out:https://edi.codeplex.com/wikipage?title=MiniUML%20Version%202&referringTitle=Pre-Alpha%20features

...and make sure you test drive the application as it is a lot of fun to draw class diagrams now

In this article another method of creating a connection was mentioned, specifically the providing of connectors in a toolbox which the user can drop on the designer canvas and then drag the endpoints to the source and sink items. How we can achieve it . Can anyone help me out.And one more thing , i want to draw straight line instead of polyline for connecting two designer items. Can any one help me in this

First of all I need to thank for this article it was very really useful.

I'm creating customized dialog editor basing on this project. With text boxes, labels and so on.

The problem is that I need to display created layout as regular form. I created separate user control with only DesignerCanvas which loads items from xml file. The problem is I need to turn off all designer functionality (moving, resizing etc).

What would be the best way to do that? I'm just starting my adventure with Wpf so I don't know where to start.