Click here to Skip to main content
12,549,492 members (43,877 online)
Click here to Skip to main content
Add your own
alternative version


620 bookmarked

WPF Diagram Designer: Part 1

, 23 Aug 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Drag, resize and rotate elements on a Canvas


In this article, I will show you how to move, resize and rotate objects of any type on a canvas. For this, I will provide two different solutions - the first one without and then one with WPF Adorners.

About the Code

The attached Visual Studio Express 2008 solution consists of three projects:

MoveResize: This version shows you how to move and resize objects without WPF Adorners.

MoveResizeRotate: In addition, this project provides rotation of objects, still without WPF Adorners. Rotation has some minor side effects that need to be considered when moving or resizing objects. You can easily track these side effects when you compare this project with the previous one.

MoveResizeRotateWithAdorners: The third project finally shows you how to move, resize and rotate items with the help of WPF Adorners. It also gives you an example of how Adorners can be used to provide visual feedback to indicate the actual size of an object during a resize operation.


We start with a simple diagram:

  <Ellipse Fill="Blue"

You might not be impressed by this diagram, nevertheless it is a good starting point. It is easy to understand and it has all a basic diagram needs to have: a drawing canvas with a shape. But you are right, this diagram isn't really useful - it's just too static.

So let's start with some preparations by wrapping the ellipse into a ContentControl:

   <ContentControl Width="100"
      <Ellipse Fill="Blue"/>

Not much better you may say, we still can't move the ellipse, so what is it good for? Well, the ContentControl serves as a container for the object that we want to place on the canvas and it is actually this ContentControl that we are going to move, resize and rotate! And because the content of a ContentControl can be of any type, we will be able to move, resize and rotate objects of any type on our canvas!

Note: Because of its key role, this ContentControl is also referred to as DesignerItem.

We conclude our preparations by assigning a ControlTemplate to the DesignerItem. This introduces a further level of abstraction, so that from now on we will just expand this template and leave the DesignerItem and its content completely untouched.

      <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
         <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
   <ContentControl Name="DesignerItem"
                   Template="{StaticResource DesignerItemTemplate}">
      <Ellipse Fill="Blue"/>

Now that we have finished our preparations, we are ready to bring some activity on the canvas.


There is a control in WPF about which the MSDN documentation says: " ...represents a control that lets the user drag and resize controls." That seems to be a perfect candidate for our job. It is the Thumb control and here is how we are going to use it:

public class MoveThumb : Thumb
    public MoveThumb()
        DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);

    private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
        Control item = this.DataContext as Control;

        if (item != null)
            double left = Canvas.GetLeft(item);
            double top = Canvas.GetTop(item);

            Canvas.SetLeft(item, left + e.HorizontalChange);
            Canvas.SetTop(item, top + e.VerticalChange);

The MoveThumb is inherited from Thumb and it provides just an implementation of the DragDelta event handler. Within the event handler, first the DataContext is cast to a ContentControl and then its position is updated according to the horizontal and vertical drag change. You may have already guessed that the control retrieved from the DataContext is our DesignerItem, but where does it come from? You can find the answer if you look at the updated DesignerItem's template:

 <ControlTemplate x:Key="DesignerItemControlTemplate" TargetType="ContentControl">
    <s:DragThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>

Here you see that the MoveThumb's DataContext property is bound to the templated parent, which is of course our DesignerItem. Note that we have added a Grid as the layout panel for the template, which allows both the ContentPresenter and the MoveThumb to take in the complete DesignerItem's real estate. Now we can compile and run the code.

Default visual representation of a Thumb control in WPF

As a result, we get a blue ellipse on top of a gray MoveThumb. If you play around with it, you will notice that you can actually grab and drag the object, but only where the gray MoveThumb is visible. That's because the ellipse hinders the mouse events to make its way through to the MoveThumb. We can easily change this behaviour by setting the IsHitTest property of the ellipse to false.

<Ellipse Fill="Blue" IsHitTestVisible="False"/>

The MoveThumb has inherited its style from the base Thumb class, which is not really appealing in our case. For this, we create a new template consisting of a transparent rectangle only. A more general solution would be to create a default style for the MoveThumb class, but for the moment a customized template will do.

Now the DesignerItem's control template looks like this:

 <ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type s:MoveThumb}">
  <Rectangle Fill="Transparent"/>

<ControlTemplate x:Key="DesignerItemTemplate" TargetType="Control">
     <s:MoveThumb Template="{StaticResource MoveThumbTemplate}"
        DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
     <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>

That's all we need to move items on a canvas, now I will show you how to resize objects.


You remember that the MSDN documentation promised that the Thumb control would let the user drag and resize controls? So, we stick with the Thumb control and build a control template, named ResizeDecoratorTeamplate:

<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="Control">
   <Thumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0"
          VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
   <Thumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0"
          VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
   <Thumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0"
          VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
   <Thumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4"
          VerticalAlignment="Bottom"  HorizontalAlignment="Stretch"/>
   <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0"
          VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0"
          VerticalAlignment="Top" HorizontalAlignment="Right"/>
   <Thumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6"
          VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
   <Thumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6"
          VerticalAlignment="Bottom" HorizontalAlignment="Right"/>

Here you see a control template that consists of a grid filled up with a bunch of 8 Thumb controls, which should work as resize handles. By setting the Thumb properties like we did above, we achieved a layout that results in something that looks like a real resize decorator:

A resize decorator build with 8 Thumbs

Amazing, isn't it. But so far it is only a fake, because there is no event handler that would handle the DragDelta events of the Thumbs. For this, we replace the Thumb objects by ResizeThumbs:

public class ResizeThumb : Thumb
    public ResizeThumb()
        DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta);

    private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
        Control item = this.DataContext as Control;

        if (item != null)
            double deltaVertical, deltaHorizontal;

            switch (VerticalAlignment)
                case VerticalAlignment.Bottom:
                    deltaVertical = Math.Min(-e.VerticalChange, 
                        item.ActualHeight - item.MinHeight);
                    item.Height -= deltaVertical;
                case VerticalAlignment.Top:
                    deltaVertical = Math.Min(e.VerticalChange, 
                        item.ActualHeight - item.MinHeight);
                    Canvas.SetTop(item, Canvas.GetTop(item) + deltaVertical);
                    item.Height -= deltaVertical;

            switch (HorizontalAlignment)
                case HorizontalAlignment.Left:
                    deltaHorizontal = Math.Min(e.HorizontalChange, 
                        item.ActualWidth - item.MinWidth);
                    Canvas.SetLeft(item, Canvas.GetLeft(item) + deltaHorizontal);
                    item.Width -= deltaHorizontal;
                case HorizontalAlignment.Right:
                    deltaHorizontal = Math.Min(-e.HorizontalChange, 
                        item.ActualWidth - item.MinWidth);
                    item.Width -= deltaHorizontal;

        e.Handled = true;

The ResizeThumbs just update the DesignerItem's width, height and/or position, depending on the ResizeThumb's vertical and horizontal alignment. Now let's integrate the resize decorator into the DesignerItem's control template by adding a Control object with the ResizeDecoratorTemplate.

<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
  <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
    <s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/>
    <Control Template="{StaticResource ResizeDecoratorTemplate}"/>
    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>

Perfect, now we can move and resize objects. Next comes rotation of objects.


To provide rotation of objects, we follow the same solution path as in the chapter before, but this time we create a RotateThumb and arrange four instances of it in a control template named RotateDecoratorTemplate. Together with the resize decorator, it looks like this:

A rotate decorator build with 4 Thumbs

The structure of the code for RotateThumb and the RotateDecoratorTemplate is very similar to what we have seen in the chapter before, so I will not list the code here.

Note: My first approach to drag, resize and rotate items was to use WPF's TranslateTransform, ScaleTransform and RotateTransform. But that turned out to be the wrong way, because Transforms in WPF do not really change an object's properties like width or height, WPF Transforms are just a rendering issue. So I didn't use TranslateTransform and ScaleTransform to drag and resize items, but I had to use RotateTransform because of no alternative.

DesignerItem Style

For convenience, we wrap the DesignerItem's control template into a style, where we also set various properties like MinWidth, MinHeight and RenderTransformOrigin. A trigger allows us to make the resize and rotate decorator visible only when the item is selected, which is indicated by the attached property Selector.IsSelected.

Note: WPF comes with a class named Selector, which is a control that allows you to select items from among its child elements. I do not make use of this control in this article, but I use the attached Selector.IsSelected property to mimic selection.

 <Style x:Key="DesignerItemStyle" TargetType="ContentControl">
  <Setter Property="MinHeight" Value="50"/>
  <Setter Property="MinWidth" Value="50"/>
  <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
  <Setter Property="Template">
      <ControlTemplate TargetType="ContentControl">
        <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
          <Control x:Name="RotateDecorator" 
                   Template="{StaticResource RotateDecoratorTemplate}" 
          <s:MoveThumb Template="{StaticResource MoveThumbTemplate}" 
          <Control x:Name="ResizeDecorator" 
                   Template="{StaticResource ResizeDecoratorTemplate}" 
          <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
          <Trigger Property="Selector.IsSelected" Value="True">
            <Setter TargetName="ResizeDecorator" 
                Property="Visibility" Value="Visible"/>
            <Setter TargetName="RotateDecorator" 
                Property="Visibility" Value="Visible"/>

That's it. Now we can move, resize and rotate objects. One has to realize that a few lines of XAML code together with three classes provide all we need to do that! Best of all, we don't need to touch the objects themselves: all the behaviour is completely wrapped into a ControlTemplate.

Adorner Based Solution

In this chapter, I present a solution that raises the resize and rotate decorators to the AdornerLayer so that they are rendered on top of all other items.

The adorner based solution is best explained by showing you the resulting DesignerItem's control template:

 <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
  <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
    <s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/>
    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
    <s:DesignerItemDecorator x:Name="decorator" ShowDecorator="true"/>
    <Trigger Property="Selector.IsSelected" Value="True">
      <Setter TargetName="decorator" Property="ShowDecorator" Value="true"/>

This template is similar to what we had in the previous chapter, except that the resize and rotate decorators are replaced by an instance of a new class named DesignerItemDecorator. This class is derived from Control and has no own default style, instead the class provides an adorner that becomes visible when the boolean ShowAdorner property gets true.

 public class DesignerItemDecorator : Control
    private Adorner adorner;

    public bool ShowDecorator
        get { return (bool)GetValue(ShowDecoratorProperty); }
        set { SetValue(ShowDecoratorProperty, value); }

    public static readonly DependencyProperty ShowDecoratorProperty =
            ("ShowDecorator", typeof(bool), typeof(DesignerItemDecorator),
        new FrameworkPropertyMetadata
            (false, new PropertyChangedCallback(ShowDecoratorProperty_Changed)));

    private void HideAdorner()

    private void ShowAdorner()

    private static void ShowDecoratorProperty_Changed
        (DependencyObject d, DependencyPropertyChangedEventArgs e)
        DesignerItemDecorator decorator = (DesignerItemDecorator)d;
        bool showDecorator = (bool)e.NewValue;

        if (showDecorator)

The adorner that becomes visible when the DesignerItem is selected is of type DesignerItemAdorner and is derived from Adorner:

 public class DesignerItemAdorner : Adorner
    private VisualCollection visuals;
    private DesignerItemAdornerChrome chrome;

    protected override int VisualChildrenCount
            return this.visuals.Count;

    public DesignerItemAdorner(ContentControl designerItem)
         : base(designerItem)
    { = new DesignerItemAdornerChrome(); = designerItem;
        this.visuals = new VisualCollection(this);         

    protected override Size ArrangeOverride(Size arrangeBounds)
    { Rect(arrangeBounds));
        return arrangeBounds;

    protected override Visual GetVisualChild(int index)
        return this.visuals[index];

You see that this adorner has a single visual child of type DesignerItemAdornerChrome, which is actually the control that provides the drag handles to resize and rotate items. This chrome control has a default style which arranges ResizeThumbs and RotateThumbs objects in a way that is similar to what we have seen in the previous chapter, so I will not repeat that code here.

Custom Adorners

You can, of course, add your own customized adorners to a DesignerItem. As an example, I have added an adorner that displays width and height while resizing an object. For more details, please see the attached code. If you have questions, feel free to ask.


  • 10th January, 2008 -- Original version
  • 18th January, 2008 -- Update: Introduced ContentControl as designer item
  • 5th February, 2008 -- Update: Added rotation of items
  • 22nd August, 2008 -- Update: Added adorner based solution


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


About the Author

Austria Austria
No Biography provided

You may also be interested in...


Comments and Discussions

QuestionAdorner Layer doesn't work with .NET Framework 4 Pin
syz3r23-Oct-13 0:55
membersyz3r23-Oct-13 0:55 
AnswerRe: Adorner Layer doesn't work with .NET Framework 4 Pin
wuzhenda16-Nov-13 0:33
memberwuzhenda16-Nov-13 0:33 
QuestionLock objects Pin
MarkusWolff3-Aug-13 9:42
memberMarkusWolff3-Aug-13 9:42 
QuestionProblem Pin
Member 1016023226-Jul-13 8:32
memberMember 1016023226-Jul-13 8:32 
Questionhow can i stop overlapping of elemnts ? Pin
ajit_machhe14-Jul-13 17:53
memberajit_machhe14-Jul-13 17:53 
AnswerRe: how can i stop overlapping of elemnts ? Pin
Vamsee Krishna Reddy23-Dec-13 23:39
memberVamsee Krishna Reddy23-Dec-13 23:39 
GeneralRe: how can i stop overlapping of elemnts ? Pin
ajit_machhe30-Mar-14 2:44
memberajit_machhe30-Mar-14 2:44 
GeneralMy vote of 5 Pin
kmf11-Jul-13 9:18
memberkmf11-Jul-13 9:18 
QuestionCode Correction? Pin
Sagger AlSaadi5-Jul-13 0:27
memberSagger AlSaadi5-Jul-13 0:27 
GeneralMy vote of 5 Pin
Yan Qiang2-Jul-13 8:43
memberYan Qiang2-Jul-13 8:43 
QuestionPrevent drag outside of canvas Pin
ncik_w25-Jun-13 11:41
memberncik_w25-Jun-13 11:41 
AnswerRe: Prevent drag outside of canvas Pin
karthikin30-May-14 0:06
memberkarthikin30-May-14 0:06 
QuestionResize Content with no default height or width Pin
ncik_w25-Jun-13 11:37
memberncik_w25-Jun-13 11:37 
GeneralMy vote of 5 Pin
Pankaj Nikam3-Jun-13 17:19
memberPankaj Nikam3-Jun-13 17:19 
Questionhow to make movable inner connected objects, help me, please! Pin
Duška Miloradović21-May-13 1:59
memberDuška Miloradović21-May-13 1:59 
QuestionPerfect! Pin
evrenG26-Apr-13 10:02
memberevrenG26-Apr-13 10:02 
GeneralMy vote of 4 Pin
schmiddd20-Apr-13 6:14
memberschmiddd20-Apr-13 6:14 
GeneralMy vote of 3 Pin
Francisco T. Chavez15-Mar-13 10:43
memberFrancisco T. Chavez15-Mar-13 10:43 
Generalmy vote of 5 Pin
Member 822540725-Nov-12 3:55
memberMember 822540725-Nov-12 3:55 
GeneralMy vote of 5 Pin
Member 822540725-Nov-12 3:54
memberMember 822540725-Nov-12 3:54 
QuestionSelect and delete shapes Pin
shabnamsarup10-Oct-12 8:07
membershabnamsarup10-Oct-12 8:07 
AnswerRe: Select and delete shapes Pin
schmiddd20-Apr-13 6:53
memberschmiddd20-Apr-13 6:53 
GeneralMy vote of 5 Pin
zzfima9-Oct-12 22:13
memberzzfima9-Oct-12 22:13 
GeneralMy vote of 5 Pin
PeterJerz1-Oct-12 1:48
memberPeterJerz1-Oct-12 1:48 
QuestionXAML Resources Lookup Pin
Edelmeier17-Sep-12 23:18
memberEdelmeier17-Sep-12 23:18 
GeneralMy vote of 5 Pin
Member 778437717-Sep-12 23:08
memberMember 778437717-Sep-12 23:08 
Questionhow to select one item alone Pin
Member 91736163-Jul-12 1:36
memberMember 91736163-Jul-12 1:36 
GeneralMy vote of 5 Pin
Madhan Mohan Reddy28-Jun-12 21:03
memberMadhan Mohan Reddy28-Jun-12 21:03 
Questionmy vote of 5 Pin
Greg Cadmes28-Jun-12 11:10
memberGreg Cadmes28-Jun-12 11:10 
Questionadd a constraint to resize thumb Pin
thefox8530-May-12 22:18
memberthefox8530-May-12 22:18 
AnswerRe: add a constraint to resize thumb Pin
schmiddd20-Apr-13 6:35
memberschmiddd20-Apr-13 6:35 
QuestionDynamically implementation. Pin
ronziv17-May-12 18:20
memberronziv17-May-12 18:20 
QuestionHow would this work when databinding the ContentControl? Pin
David_Keaveny7-May-12 15:21
memberDavid_Keaveny7-May-12 15:21 
GeneralMy vote of 5 Pin
Member 88888652-May-12 4:12
memberMember 88888652-May-12 4:12 
Questionnice Pin
CIDev25-Apr-12 11:17
memberCIDev25-Apr-12 11:17 4.0 Pin
fubarrr16-Apr-12 3:04
memberfubarrr16-Apr-12 3:04 
AnswerRe: .net 4.0 Pin
fubarrr16-Apr-12 3:07
memberfubarrr16-Apr-12 3:07 
GeneralRe: .net 4.0 Pin
Member 867745820-May-12 10:54
memberMember 867745820-May-12 10:54 
GeneralRe: .net 4.0 Pin
PLMorek16-Sep-12 0:50
memberPLMorek16-Sep-12 0:50 
Questionstop overlapping... Pin
ajit_mac5-Mar-12 3:50
memberajit_mac5-Mar-12 3:50 
Questionplease help ............dynamically adding control over canvas Pin
ajit_mac20-Feb-12 7:22
memberajit_mac20-Feb-12 7:22 
private void button1_Click(object sender, RoutedEventArgs e)
//System.Windows.Controls.Label lb = new System.Windows.Controls.Label();
//lb.Content = "this is test lable";
//lb.IsHitTestVisible = false;

System.Windows.Shapes.Ellipse lb = new System.Windows.Shapes.Ellipse();
lb.IsHitTestVisible = false;
lb.Name = "dynecp";
lb.Fill = System.Windows.Media.Brushes.DeepSkyBlue;

System.Windows.Controls.ContentControl contcrl = new System.Windows.Controls.ContentControl();
contcrl.MinHeight = 35;
contcrl.MinWidth = 90;
contcrl.Height = 40;
contcrl.Width = 120;
contcrl.Content = lb;
contcrl.Name = "iamdyn";

contcrl.Template = this.Resources["DesignerItemTemplate"] as ControlTemplate;
//contcrl.Margin = new System.Windows.Thickness(10, 10, 0, 0);



I have added control successfully.... and i can even resize it..

but please help me to move the control round the canvas.

thanks a lot

modified 21-Feb-12 13:01pm.

AnswerRe: please help ............dynamically adding control over canvas Pin
ramsayra28-Feb-12 6:47
memberramsayra28-Feb-12 6:47 
GeneralRe: please help ............dynamically adding control over canvas Pin
ajit_mac29-Feb-12 9:07
memberajit_mac29-Feb-12 9:07 
Questionhow can i stop overlapping ?? Pin
ajit_mac10-Feb-12 1:40
memberajit_mac10-Feb-12 1:40 
QuestionAdd scale transform in addition to Height / Width modification? Pin
Simon Mansfield26-Jan-12 4:16
memberSimon Mansfield26-Jan-12 4:16 
GeneralMy vote of 5 Pin
Florian.Witteler18-Jan-12 12:23
memberFlorian.Witteler18-Jan-12 12:23 
GeneralMy Vote of 5 Pin
RaviRanjankr16-Oct-11 10:56
memberRaviRanjankr16-Oct-11 10:56 
Questionremoving selector Pin
Member 81683008-Sep-11 5:00
memberMember 81683008-Sep-11 5:00 
QuestionIs there any who update this project to restrict move and resize ? Pin
Dorian PIERRE20-Jul-11 6:13
memberDorian PIERRE20-Jul-11 6:13 
Questionexplain Pin
bocheli9-Jul-11 10:23
memberbocheli9-Jul-11 10:23 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161021.1 | Last Updated 24 Aug 2008
Article Copyright 2008 by sukram
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid