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

WPF Diagram Designer - Part 2

By , 8 Oct 2008
 
WPF Diagram Designer

Last Update

  • Zoombox (new)
  • Rubberband Adorner (updated)
WPF Zoombox

Introduction

In the first article of this series, I have shown you how to move, resize and rotate items on a canvas. This time we are going to add further features that are essential for a typical diagram designer:

  • Designer Canvas (variable size, scrollable)
  • Zoombox
  • Rubberband selection
  • Keystroke selection (LeftMouseButton + Ctrl)
  • Toolbox (drag & drop)
  • Rotate items (left, right)

Designer Canvas

In the previous article, you probably have noticed that when you move an item outside the borders of the DesignerCanvas the item is no longer accessible. Normally you would expect that the designer application provides scroll bars so that you can easily scroll to any item outside the visible canvas region. For this I thought I just have to wrap the DesignerCanvas into a ScrollViewer, but that didn't work. I soon found the reason for this behaviour; let me explain it with the following code snippet:

 <Canvas Width="200"
        Height="200"
        Background="WhiteSmoke">
    <Rectangle Fill="Blue"
               Width="100"
               Height="100"
               Canvas.Left="300"
               Canvas.Top="300" />
</Canvas>

Here I have placed a Rectangle object on a Canvas, but positioned it outside the boundaries of the Canvas. Will this change the size of the Canvas? Of course not, the Canvas will keep its size, no matter where you place an item.

For the DesignerCanvas this means that it will keep its size, even if you drag an item far beyond the borders of the canvas. Now we understand why a ScrollViewer doesn't help: the DesignerCanvas will never notify the ScrollViewer of a size change, just because there is none.

The solution is that we must force the DesignerDanvas to adjust its size everytime an item is moved or resized. Fortunately the Canvas class provides an overrideable method named MeassureOverride that allows the DesignerCanvas to calculate its desired size and return it to the WPF layout system. The calculation is quite simple as you can see here:

 protected override Size MeasureOverride(Size constraint)
{
    Size size = new Size();
    foreach (UIElement element in base.Children)
    {
        double left = Canvas.GetLeft(element);
        double top = Canvas.GetTop(element);
        left = double.IsNaN(left) ? 0 : left;
        top = double.IsNaN(top) ? 0 : top;

        //measure desired size for each child
        element.Measure(constraint);

        Size desiredSize = element.DesiredSize;
        if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
        {
            size.Width = Math.Max(size.Width, left + desiredSize.Width);
            size.Height = Math.Max(size.Height, top + desiredSize.Height);
        }
    }
    //for aesthetic reasons add extra points
    size.Width += 10;
    size.Height += 10;
    return size;
}

DesignerItem

The DesignerItem is inherited from ContentControl, so that we can reuse the ControlTemplate of our first article. The DesignerItem provides an IsSelected property to indicate if it is selected or not:

 public class DesignerItem : ContentControl
{
    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set { SetValue(IsSelectedProperty, value); }
    }
    public static readonly DependencyProperty IsSelectedProperty =
       DependencyProperty.Register("IsSelected", typeof(bool),
                                    typeof(DesignerItem),
                                    new FrameworkPropertyMetadata(false));
       ...

}

Then we have to implement an event handler for the MouseDown event to support multiple selection of items:

 protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
    base.OnPreviewMouseDown(e);
    DesignerCanvas designer = VisualTreeHelper.GetParent(this) as DesignerCanvas;

    if (designer != null)
    {
        if ((Keyboard.Modifiers & 
		(ModifierKeys.Shift | ModifierKeys.Control)) != ModifierKeys.None)
        {
            this.IsSelected = !this.IsSelected;
        }
        else
        {
            if (!this.IsSelected)
            {
                designer.DeselectAll();
                this.IsSelected = true;
            }
        }
     }

     e.Handled = false;
}

Please note that we handle the PreviewMouseDown event, which is the tunnelling version of the MouseDown event and that we mark the event as not handled. The reason is that we want the item to be selected even if the MouseDown event is targeting another Control inside the DesignerItem; e.g. take a look at a class diagram in Visual Studio, if you click on the ToggleButton of the Expander, the item becomes selected and the Expander toggles its size, both at the same time.

Finally we have to update the template for the DesignerItem such that the resize decorator is only visible when the IsSelected property is true, which can be handled with a simple DataTrigger:

  <Style TargetType="{x:Type s:DesignerItem}">
    <Setter Property="MinHeight" Value="50"/>
    <Setter Property="MinWidth" Value="50"/>
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type s:DesignerItem}">
          <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, 
			  Path=.}">
            <s:MoveThumb
                x:Name="PART_MoveThumb"
                Cursor="SizeAll" 
                Template="{StaticResource MoveThumbTemplate}" />
            <ContentPresenter
                x:Name="PART_ContentPresenter"                
                Content="{TemplateBinding ContentControl.Content}"
                Margin="{TemplateBinding Padding}"/>
            <s:ResizeDecorator x:Name="PART_DesignerItemDecorator"/>
          </Grid>
          <ControlTemplate.Triggers>
            <Trigger Property="IsSelected" Value="True">
              <Setter TargetName="PART_DesignerItemDecorator" 
			Property="ShowDecorator" Value="True"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

Toolbox

The Toolbox is an ItemsControl that uses the ToolboxItem class as default container to display its items. For this we have to override the GetContainerForItemOverride method and the IsItemItsOwnContainerOverride method:

public class Toolbox : ItemsControl
{
    private Size defaultItemSize = new Size(65, 65);
    public Size DefaultItemSize
    {
        get { return this.defaultItemSize; }
        set { this.defaultItemSize = value; }
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ToolboxItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return (item is ToolboxItem);
    }
}

Additionally we want the Toolbox to use a WrapPanel to layout its items:

<Setter Property="ItemsPanel">
   <Setter.Value>
     <ItemsPanelTemplate>
       <WrapPanel Margin="0,5,0,5"
                  ItemHeight="{Binding Path=DefaultItemSize.Height,
                        RelativeSource={RelativeSource AncestorType=s:Toolbox}}"
                  ItemWidth="{Binding Path=DefaultItemSize.Width,
                        RelativeSource={RelativeSource AncestorType=s:Toolbox}}"/>
     </ItemsPanelTemplate>
   </Setter.Value>
</Setter>

Note that the ItemHeight and ItemWidth properties of the WrapPanel are bound to the DefaultItemSize property of the Toolbox.

ToolboxItem

The ToolboxItem is the place where drag operations are actually started if you want to drag an item from the toolbox and drop it on the canvas. There is nothing mysterious about drag and drop itself, but still you have to take care how to copy an item from the drag source (Toolbox) to the drop target (DesignerCanvas). In our case we use the XamlWriter.Save method to serialize the content of the ToolboxItem into XAML, although that kind of serialization has some notable limitations in exactly what is serialized. In a later article, we will switch to binary serialization.

 public class ToolboxItem : ContentControl
 {
     private Point? dragStartPoint = null;

     static ToolboxItem()
     {
         FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(ToolboxItem),
                new FrameworkPropertyMetadata(typeof(ToolboxItem)));
     }

     protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
     {
         base.OnPreviewMouseDown(e);
         this.dragStartPoint = new Point?(e.GetPosition(this));
     }

     protected override void OnMouseMove(MouseEventArgs e)
     {
         base.OnMouseMove(e);
         if (e.LeftButton != MouseButtonState.Pressed)
         {
             this.dragStartPoint = null;
         }
         if (this.dragStartPoint.HasValue)
         {
             Point position = e.GetPosition(this);
             if ((SystemParameters.MinimumHorizontalDragDistance <=
                  Math.Abs((double)(position.X - this.dragStartPoint.Value.X))) ||
                  (SystemParameters.MinimumVerticalDragDistance <=
                  Math.Abs((double)(position.Y - this.dragStartPoint.Value.Y))))
             {
                 string xamlString = XamlWriter.Save(this.Content);
                 DataObject dataObject = new DataObject("DESIGNER_ITEM", xamlString);

                 if (dataObject != null)
                 {
                     DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Copy);
                 }
             }
             e.Handled = true;
         }
     }
 }

Rubberband Selection

When the user initiates a drag operation directly on the DesignerCanvas, a new instance of a RubberbandAdorner is created:

public class DesignerCanvas : Canvas
{
    ...

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);

        if (e.LeftButton != MouseButtonState.Pressed)
            this.dragStartPoint = null;

        if (this.dragStartPoint.HasValue)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
            if (adornerLayer != null)
            {
                RubberbandAdorner adorner = new RubberbandAdorner(this, dragStartPoint);
                if (adorner != null)
                {
                    adornerLayer.Add(adorner);
                }
            }
            
            e.Handled = true;
        }        
    }
    
    ...
}

As soon as the RubberbandAdorner is created, it takes control over the drag operation and updates the drawing of the rubber band and the current selection of items. These updates happen inside the UpdateRubberband() and UpdateSelection() methods:

public class RubberbandAdorner : Adorner
{    
    ....
    
    private Point? startPoint, endPoint;

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            if (!this.IsMouseCaptured)
            {
                this.CaptureMouse();
            }

            this.endPoint = e.GetPosition(this);
            this.UpdateRubberband();
            this.UpdateSelection();
            e.Handled = true;
        }
    }
    
    ...
}

Since the actual rubber band is an instance of a Rectangle class, the UpdateRubberband() method just needs to update the size and the position of that Rectangle:

private void UpdateRubberband()
{
    double left = Math.Min(this.startPoint.Value.X, this.endPoint.Value.X);
    double top = Math.Min(this.startPoint.Value.Y, this.endPoint.Value.Y);

    double width = Math.Abs(this.startPoint.Value.X - this.endPoint.Value.X);
    double height = Math.Abs(this.startPoint.Value.Y - this.endPoint.Value.Y);

    this.rubberband.Width = width;
    this.rubberband.Height = height;
    Canvas.SetLeft(this.rubberband, left);
    Canvas.SetTop(this.rubberband, top);
}

A little more work needs to be done in the UpdateSelection() method. Here we check for each DesignerItem if it is contained in the current rubber band. For this, the VisualTreeHelper.GetDescendantBounds(item) method provides us the bounding rectangle for each item. We transform the coordinates of this rectangle to the DesignerCanvas and call the rubberband.Contains(itemBounds) method to decide whether the item is selected or not!

private void UpdateSelection()
{
    Rect rubberBand = new Rect(this.startPoint.Value, this.endPoint.Value);
    foreach (DesignerItem item in this.designerCanvas.Children)
    {
        Rect itemRect = VisualTreeHelper.GetDescendantBounds(item);
        Rect itemBounds = item.TransformToAncestor
			(designerCanvas).TransformBounds(itemRect);

        if (rubberBand.Contains(itemBounds))
        {
            item.IsSelected = true;
        }
        else
        {
            item.IsSelected = false;
        }
    }
}

Please note that these update methods are called whenever the MouseMove event is fired during a drag operation, and that is quite frequently! Instead you may consider to update the selection only once at the end of the drag operation, when the MouseUp event is fired.

Customize the DragThumb

The default style of the DragThumb class is a transparent Rectangle, but if you want to adjust that style you can do this with the help of an attached property named DesignerItem.DragThumbTemplate. Let me explain the usage with an example. Let's say the content of a DesignerItem is a star shape like this one:

<Path Stroke="Red" StrokeThickness="5" Stretch="Fill" IsHitTestVisible="false"
      Data="M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z"/> 

To illustrate the result, I have colorized the default DragThumb template:

Now try the following:

 <Path Stroke="Red" StrokeThickness="5" Stretch="Fill" IsHitTestVisible="false"
      Data="M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z">
    <s:DesignerItem.DragThumbTemplate>
         <ControlTemplate>
            <Path Data="M 9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7 Z"
                    Fill="Transparent" Stretch="Fill"/>
         </ControlTemplate>
    </s:DesignerItem.DragThumbTemplate>
</Path>

The result is a DragThumb that fits much better than the default one:

 

History

  • 28th January, 2008 -- Original version submitted
  • 11th February, 2008 -- Rubberband selection added
  • 7th October, 2008 -- Zoombox added, RubberbandAdorner updated

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   
GeneralMy vote of 5memberAdy Shimony10 Dec '12 - 5:59 
5 is not enough. Great article. thanks
GeneralMy vote of 5memberChona117125 Sep '12 - 23:31 
Very Nice helped me a whole lot
QuestionDo you have a demo with Silverlight?membervietanh15418 Sep '12 - 21:35 
Hi Sukram,
 
Thanks for your sharing!
I'm building a simple Silverlight Form Designer, do you have a demo with Silverlight?
 
Thanks & Best Regards,
AnhDV.
Programming is my hobby!
Vietanh154, vietanh154@yahoo.com or vietanh154gmail.com
Manager for Concat Group: http://nhómConcatGroup.vn/
Project Manager & Technical Leader for FPT Software JSC
Technical Leader & Solution Architect for VietSoftware International

QuestionPlz guide, What is exact use of Rubberband selectionmemberrahul.uttarkar@igatepatni.com5 Sep '12 - 20:24 
Hi,
 
Great article, it really helps me a lot..
 
One thing i am not able to undersdand is what is exact use of rubberband selection.. as it works without it also.. thanks in advance
 
Regards
GeneralMy vote of 5memberaghapour.ahad29 Jul '12 - 18:35 
Excellent
QuestionHow to Convert in Powerbuilder ScriptmemberJosant Wighuno7 Jun '12 - 23:28 
Hello ..
We try to develope this source in WPF PowerBuilder 12.5 .NET
but we have question How convert this script to Powerbuilder Script?
 
 public IEnumerable<DesignerItem> SelectedItems
        {
            get
            {
                var selectedItems = from item in this.Children.OfType<DesignerItem>()
                                    where item.IsSelected == true
                                    select item;
 
                return selectedItems;
            }
        }
 
Thanks
Questiongood articlememberCIDev25 Apr '12 - 11:22 
A fine continuation of your WPF series.
Just because the code works, it doesn't mean that it is good code.

GeneralMy vote of 5membersalim4187 Jul '11 - 1:39 
i love ur work...
GeneralCausing scrollbars to appear by acknowledging that the canvas needs to be redrawnmemberPat Kujawa17 Dec '10 - 7:11 
I added functionality to the Designer (starting with the Part 4 code) to move an item to specified coordinates, but the scrollbars weren't appearing when it was outside the canvas bounds. The trick was to finish by calling InvalidateMeasure on the DesignerCanvas instance.
GeneralMy vote of 5membersuryawang24 Oct '10 - 17:23 
very good article for make designer application
GeneralImage Tilingmemberbreath2k6 Sep '10 - 5:32 
Instead of the image being stretched can it be tiled when resized instead?
blah

GeneralCanvas Background Imagememberbreath2k6 Sep '10 - 4:44 
Can you let me know how to set the designer canvas background to a tiled image instead
blah

GeneralSavingmemberbreath2k6 Sep '10 - 4:20 
How would you suggest saving the items on the canvas into a database. I am trying to create a simple garden drawing application and I think this kind of thing would be perfect.
blah

GeneralSet of iconsmemberRobson Félix17 Mar '10 - 5:07 
Where did you obtain the set of icons (symbols) included in this app?
 
I would love to have the complete set.
 
Thanks,
QuestionPaint ApplicationmemberVisionator5 Feb '10 - 20:45 
Hey, nice work!!
 
is it possible to use this methohds to write a paint application? i think all i need is a linepath to draw on the canvas? did you have a example for something like a paint application with this resize and rotate methods?
 
thx!
 
very good work!!!
GeneralBravo! [EOM]memberTheArchitectmc30 Dec '09 - 6:49 
5/5
Thumbs Up | :thumbsup:
GeneralSoftiiiiiiiiiiiimemberms_soft8920 Oct '09 - 23:10 
Goood k , go ahead ya maw
 
Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves

GeneralGreat article!memberSELVAM PARAMASIVAN27 Apr '09 - 15:06 
This is one of the great article and I was thinking to design an pictorial representation of cutom workflow (entity in my current assignment) using flow chart. I beleive this is nice work and great reference for me to build my own.
 
Regards
SP
GeneralUnable to rotate objectmembersosillysally20 Feb '09 - 13:55 
I am unable to rotate the object. I am try to port your app to Silverlight, and I want a solution without adorners. Should I be pressing some other key sequence to enable rotation. It works fine in Part I w/o adorner solution.
 
Thanks in advance
GeneralPlace of the ZoomboxmemberQuentschi9 Jan '09 - 1:44 
Hey!
 
Firstly thank you for that really great tutorial. That
retrieved my diploma thesis Wink | ;)
 
I have one question. I want to place the Zoombox at the
bottom of the toolbox instead onto the Canvas.
The rebuilding of the graphic was no problem, but the
functions doesn´t work.
What is the problem?
 
Greetz
Andreas
Generalnice!memberviciouskinid30 Nov '08 - 22:52 
great article. Just wondering.. you have some nice icons in this application, can i ask where did you get them from? Can I use them in commercial software?
QuestionHow to detect drop of one ToolBoxItem on anothermemberMember 273853524 Nov '08 - 16:05 
Pls help on how to detect when a user drags one toolboxitem on another. I need to get hold of those two toolboxitems and then draw a line between them.
 
Thanks in advancce.
AnswerPrinting works thanks to the MeasureOverride method (printVisual)membernitropit26 Aug '08 - 20:55 
Hi,
 
Thanks a lot for yours very very nice articles.
 
I just wanted to point out something which cost me hours to find.
 
When you print your canvas, you print the whole canvas thanks to the MeasureOverride method.
If you remove it, only the user view is printed (objects outside the view area are not printed !).
 
Thanks again, hopes it will help others beginners like me Blush | :O )
 
Nd
.

QuestionHOW TO DELETE THE ADDED THUMB OR CONNECTOR LINEmembermuneeb1931 Aug '08 - 21:30 
IF I SELECT THE CONNECTOR LINE OR THUMB FROM THE DESIGNER CONVAS AND PRESS "DELETE" BUTTON TO REMOVE IT THEN HOW IS THIS POSSIBLE.
ALSO I WANT TO ADD OPEN SAVE OF THE DRAWN DIAGRAM, HOW TO DO IT
AnswerRe: HOW TO DELETE THE ADDED THUMB OR CONNECTOR LINEmembersukram27 Aug '08 - 0:02 
Please see Part 4 of this series; its only a ugly prototype, but it should give you all answers to your questions.
QuestionHOW TO COUNT THE NUMBER OF THUMBS ADDED TO THE DESIGNER CONVASmembermuneeb1931 Aug '08 - 21:21 
HOW TO ADD THE NUMBER OF THUMBS ADDED TO THE DESIGNERCONVAS AND GET THE LOCATION OF EACH THUM, ALSO HOW TO GET THE NUMBER AND COORDINTES OF CONNECTION LINE
AnswerRe: HOW TO COUNT THE NUMBER OF THUMBS ADDED TO THE DESIGNER CONVASmembersukram27 Aug '08 - 0:02 
Please see Part 4 of this series; its only a ugly prototype, but it should give you all answers to your questions.
GeneralSilverlight Appmemberfiannolo27 Jul '08 - 12:30 
Hi markus, i'm writing from caracas, venezuela. I'm a tecnology information student and i need make an application like this, but web as a final work of my carreer.
 
Can you help me with guides, tutorials or examples to make my app?
 
sorry for my poor english but i don't speak this language. i wait you can understand me.
 
Congrats for your exelent work, your diagram designer is great.
GeneralRe: Silverlight Appmembersukram27 Aug '08 - 0:21 
I have no plans to provide a Silverlight version, but maybe this link will help you:
 
http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!293.entry
GeneralRe: Silverlight Appmembermtonsager25 Oct '09 - 5:17 
Hello,
 
It is probably too late, but I converted this app to Silverlight.
 
Thanks,
Matt
GeneralRe: Silverlight Appmemberanilmomin8713 Jul '10 - 1:37 
Hi i m a new to silverlight and WPF, I would also like to convert this application to silverlight, if you have any available
resource please share here. Looking forward for a quick response
 
Regards Anil Momin
GeneralRubberbandAdorner preformance issuememberJakub Klímek12 May '08 - 22:12 
Hi! Nice article, very useful. However, the RubberbandAdorner used for selecting items has some performance issue. When you place several objects into the canvas, the selecting becomes slow and consumes a lot of CPU time. I've used it in my project with 100+ items and it consumes 2 cores of my C2Q CPU, but the CPU load is noticable even in this example. Is there any way around it? I suppose the problem is in whole canvas redrawing all the time...
GeneralRe: RubberbandAdorner preformance issuemembersukram14 May '08 - 23:53 
This performance issue has nothing to do with the way WPF renders items. It's my fault.
 
The reason can be found in the UpdateSelection() method of the RubberbandAdorner class; currently I iterate over all items whenever I drag the mouse to check if an item is within the rubber band. Not really efficient code, yet.
GeneralRe: RubberbandAdorner preformance issue [modified]memberJakub Klímek28 May '08 - 20:15 
Hi! I thought so also. But, when I removed the UpdateSelection from onMouseMove and put it in onMouseUp (with the obvious effect that it selects items only on mouseup), the speedup was only few % of CPU load... still consuming whole 2 cores.... So I think this is not the issue...
 
Next thing I tried was removing the Dash style from RubberbandPen. That also helped a little. But still... it is quite slow.
 
And even if I move the rubberband in the area of canvas, where there is no designerItem, it consumes the CPU. So it has to be something with WPF redrawing whole canvas while selecting, don't you think?
 
modified on Thursday, May 29, 2008 2:27 AM

GeneralRubberbandAdorner preformance issue fixed [modified]membersukram27 Aug '08 - 0:04 
Hi Jakub,
 
you were right, the whole canvas was redrawn with every drag delta event of the Rubberband Adorner. In the new version I have updated the Adorner and fixed that problem, please see the code for more details.
 
modified on Wednesday, October 8, 2008 2:32 PM

QuestionRotating [modified]memberKBou27 Mar '08 - 3:28 
Hey markus,
I was just playing around with your project and I was trying to implement the rotate function from your previous part. But when I'm draging a rotated item it freaks out. The item starts moving in mysterious ways.
Is this why you've left it out?
 
Greets.
 
modified on Thursday, March 27, 2008 9:52 AM

GeneralRe: Rotatingmembersukram27 Mar '08 - 8:20 
> Is this why you've left it out?
 
Yes, sort of. Initially rotation wasn't planned.
GeneralRe: RotatingmemberKBou27 Mar '08 - 22:16 
To bad. I'm trying to make something simular but users should be able to drag customized buttons and labels onto a canvas. And they should be able to drag, resize and rotate them.
 
Whell I'm gonna try it again. Sigh | :sigh:
Thanks for this, it's very nice.
GeneralVery Nicememberjherington19 Feb '08 - 7:03 
This is an excellent way to present the new features in WPF while building a very useful application. Thank You! Your skill is very much appreciated!
GeneralThis might give you additional ideasmemberVuyka10 Feb '08 - 21:51 
http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!220.entry[^]
 
Vuyka

GeneralRe: This might give you additional ideasmemberlyntonhu11 Feb '08 - 14:28 
Thanks Vuyka Laugh | :laugh:
GeneralNice work!! I can't wait to see part -3 ! Please post it asap.memberlyntonhu10 Feb '08 - 21:00 
Smile | :)
GeneralNice startmvpSacha Barber8 Feb '08 - 1:57 
This is cool, and the 1st part also.
 
Keep it up
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same view, and never argue
 
My Blog : sachabarber.net

GeneralAmazing stuffmemberjulius-dias7 Feb '08 - 4:03 
Good job. My 5 too!
What features planned for the next release ? Is it possible to add Linking nodes, Rubberband selection.
GeneralRe: Amazing stuffmembersukram7 Feb '08 - 7:30 
Linking of nodes is planned for the coming article (~next week), maybe also rubberband selection - if I have time.
 
Markus
GeneralRe: Amazing stuffmemberAjithIsKool7 Feb '08 - 22:04 
Fantastic job dude.
Can we have multiple points coming out of a Node, to which we can connect other nodes?
I am working on similar stuff. Your article is going to help me a lot.
 
Thanks a lot!!
 
When I was born, I was so surprised I didn't talk for a year and a half.

AnswerRe: Amazing stuffmembersukram7 Feb '08 - 22:35 
> Can we have multiple points coming out of a Node?
 
Yes, by default you get so called connectors at the top,left,right and bottom side. They can be used for linking in and out.
 
Markus
GeneralGood stuffmemberFatGeek29 Jan '08 - 2:42 
Got my 5! Keep up the good work...
GeneralnicememberAbhijit Jana28 Jan '08 - 17:52 
nice one !!!
5 form me Smile | :)
 
Best Regards
-----------------
Abhijit Jana
View My Latest Article :- SpyNet : Your Network Spy

"Success is Journey it's not a destination"

GeneralGreat article.memberRajib Ahmed28 Jan '08 - 8:48 
Nice article. Can you post this article at www.progtalk.com?
 
Rajib Ahmed

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 8 Oct 2008
Article Copyright 2008 by sukram
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid