Links go to top of node instead of port

I am using GoXam for Silverlight v2.0.2.5.


My data template for nodes has a “From” port on the outermost stackpanel and a “To” port that is inside the stackpanel on a border control.

    <DataTemplate x:Key="nodeTemplate1">
        <StackPanel Orientation="Vertical"
                        go:Part.SelectionAdorned="True"
                        go:Part.SelectionElementName="VisualNode"
                        go:Part.LayoutId="Gate"
                        go:Node.PortId="From"
                        go:Node.FromSpot="BottomCenter"
                        MouseLeftButtonDown="Node_MouseLeftButtonDown"
                        >
            <Rectangle Height="28" Stroke="BlanchedAlmond"></Rectangle>
            <Border Height="40" Width="40" BorderBrush="Black" Background="{Binding Path=Data.Color}" Opacity="50"  x:Name="VisualNode" go:Node.PortId="To" go:Node.ToSpot="TopCenter">
                <TextBlock Text="{Binding Path=Data.Key}" HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </StackPanel>
    </DataTemplate>

Most of the time the links correctly go to the “To” port. But sometimes the links go to the top of the node.

I was able to reproduce this by modifying the TreeLayout sample. One change I made is that if you double click on a node it will insert a child. In this case I rapidly clicked on Node16 many, many times and produced the following that shows the problem. Note the links in the lower right going into Node72 - Node76.

Here’s the code:


MainPage.xaml


<UserControl.Resources>
    <local:SimpleComparer x:Key="theSimpleComparer" />

    <!--<DataTemplate x:Key="nodeTemplate1">
        <StackPanel go:Part.SelectionAdorned="True" go:Part.SelectionElementName="ellipse">
            <Ellipse x:Name="ellipse" Fill="{Binding Path=Data.Color}" Stroke="Black" 
             Width="40" Height="40" go:Node.PortId="" />
            <TextBlock Text="{Binding Path=Data.Key}" HorizontalAlignment="Center" />
        </StackPanel>
    </DataTemplate>-->

    <DataTemplate x:Key="nodeTemplate1">
        <StackPanel Orientation="Vertical"
                        go:Part.SelectionAdorned="True"
                        go:Part.SelectionElementName="VisualNode"
                        go:Part.LayoutId="Gate"
                        go:Node.PortId="From"
                        go:Node.FromSpot="BottomCenter"
                        MouseLeftButtonDown="Node_MouseLeftButtonDown"
                        >
            <Rectangle Height="28" Stroke="BlanchedAlmond"></Rectangle>
            <Border Height="40" Width="40" BorderBrush="Black" Background="{Binding Path=Data.Color}" Opacity="50"  x:Name="VisualNode" go:Node.PortId="To" go:Node.ToSpot="TopCenter">
                <TextBlock Text="{Binding Path=Data.Key}" HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="linkTemplate1">
        <go:LinkPanel>
            <go:Link.Route>
                <go:Route Routing="Orthogonal" Curve="None" Corner="5" />
            </go:Link.Route>
            <go:LinkShape Stroke="Black" StrokeThickness="1" />
            <Path Fill="Black" go:LinkPanel.ToArrow="Standard" />
        </go:LinkPanel>
    </DataTemplate>
</UserControl.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="4*" />
    </Grid.RowDefinitions>

    <Border Grid.Row="0" Background="LightGray" Padding="2">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Border Grid.Column="0" VerticalAlignment="Stretch"
            Background="AliceBlue" Margin="1" Padding="1"
            BorderBrush="Gray" BorderThickness="2" CornerRadius="2">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
                        <StackPanel>
                            <TextBlock Text="TreeStyle:" FontWeight="Bold" />
                            <Grid x:Name="TreeStyleRadioButtonGroup" Tag="LastParents">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>
                                <RadioButton Grid.Row="0"
                Content="Layered" Checked="RadioButton_Checked" />
                                <RadioButton Grid.Row="1"
                Content="LastParents" Checked="RadioButton_Checked" IsChecked="True" />
                                <RadioButton Grid.Row="2"
                Content="Alternating" Checked="RadioButton_Checked" />
                                <RadioButton Grid.Row="3"
                Content="RootOnly" Checked="RadioButton_Checked" />
                            </Grid>
                            <TextBlock Text="New Tree:" FontWeight="Bold" Margin="0 5 0 0" />
                            <StackPanel Orientation="Horizontal">
                                <TextBlock VerticalAlignment="Center" Text="MinNodes: " />
                                <TextBox Text="20" x:Name="txtMinNodes" MaxLength="3" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock VerticalAlignment="Center" Text="MaxNodes: " />
                                <TextBox Text="50" x:Name="txtMaxNodes" MaxLength="3" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock VerticalAlignment="Center" Text="MinChildren: " />
                                <TextBox Text="3" x:Name="txtMinLinks" MaxLength="2" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock VerticalAlignment="Center" Text="MaxChildren: " />
                                <TextBox Text="5" x:Name="txtMaxLinks" MaxLength="2" />
                            </StackPanel>
                            <Button Content="Generate Tree" Click="GenerateTree_Click" />
                        </StackPanel>
                    </ScrollViewer>
                </Grid>
            </Border>

            <Border Grid.Column="1" VerticalAlignment="Stretch"
            Background="AliceBlue" Margin="1" Padding="1"
            BorderBrush="Gray" BorderThickness="2" CornerRadius="2">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0"
                     Text="Tree Layout Properties" FontWeight="Bold" />
                    <ScrollViewer Grid.Row="1">
                        <StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Angle: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AngleTextBox" Text="90" />
                                <TextBlock Text=" (0: right, 90: down, 180: left, 270: up)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Alignment: "
                           VerticalAlignment="Center" />
                                <Grid x:Name="AlignmentRadioButtonGroup" Tag="CenterChildren">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <RadioButton Grid.Row="0" Grid.Column="0"
                    Content="CenterChildren" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Grid.Row="0" Grid.Column="1" 
                    Content="CenterSubtrees" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="0"
                    Content="Start" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="1"
                    Content="End" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="2" Grid.Column="0"
                    Content="Bus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="2" Grid.Column="1"
                    Content="TopLeftBus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="3" Grid.Column="0"
                    Content="BottomRightBus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="3" Grid.Column="1"
                    Content="BusBranching" Checked="RadioButton_Checked" />
                                </Grid>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="NodeSpacingTextBox" Text="20" />
                                <TextBlock Text=" (negative causes overlaps)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeIndent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="NodeIndentTextBox" Text="0" />
                                <TextBlock Text=" (when Start or End; >= zero)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeIndentPastParent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="NodeIndentPastParentTextBox" Text="0" />
                                <TextBlock Text=" (fraction; 0-1)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="LayerSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="LayerSpacingTextBox" Text="50" />
                                <TextBlock Text=" (negative causes overlaps)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="LayerSpacingParentOverlap: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="LayerSpacingParentOverlapTextBox" Text="0" />
                                <TextBlock Text=" (fraction; 0-1)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Sorting: "
                           VerticalAlignment="Center" />
                                <Grid x:Name="SortingRadioButtonGroup" Tag="Ascending">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <RadioButton Grid.Row="0" Grid.Column="0"
                    Content="Forward" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="0" Grid.Column="1"
                    Content="Reverse" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="0"
                    Content="Ascending" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Grid.Row="1" Grid.Column="1"
                    Content="Descending" Checked="RadioButton_Checked" />
                                </Grid>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Compaction: "
                         VerticalAlignment="Center" />
                                <StackPanel x:Name="CompactionRadioButtonGroup" Tag="Block"
                          Orientation="Horizontal">
                                    <RadioButton Content="Block" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Content="None" Checked="RadioButton_Checked" />
                                </StackPanel>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="BreadthLimit: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="BreadthLimitTextBox" Text="0" />
                                <TextBlock Text=" (zero means no limit)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="RowSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="RowSpacingTextBox" Text="25" />
                                <TextBlock Text=" (negative causes overlaps)"
                         VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="RowIndent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="RowIndentTextBox" Text="10" />
                                <TextBlock Text=" (>= zero)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                        </StackPanel>
                    </ScrollViewer>
                </Grid>
            </Border>

            <Border Grid.Column="2" VerticalAlignment="Stretch"
            Background="AliceBlue" Margin="1" Padding="1"
            BorderBrush="Gray" BorderThickness="2" CornerRadius="2">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0"
                     Text="Alternate Layout Properties" FontWeight="Bold" />
                    <ScrollViewer Grid.Row="1">
                        <StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Angle: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateAngleTextBox" Text="90" />
                                <TextBlock Text=" (0: right, 90: down, 180: left, 270: up)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Alignment: "
                           VerticalAlignment="Center" />
                                <Grid x:Name="AlternateAlignmentRadioButtonGroup" Tag="CenterChildren">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <RadioButton Grid.Row="0" Grid.Column="0"
                    Content="CenterChildren" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Grid.Row="0" Grid.Column="1" 
                    Content="CenterSubtrees" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="0"
                    Content="Start" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="1"
                    Content="End" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="2" Grid.Column="0"
                    Content="Bus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="2" Grid.Column="1"
                    Content="TopLeftBus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="3" Grid.Column="0"
                    Content="BottomRightBus" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="3" Grid.Column="1"
                    Content="BusBranching" Checked="RadioButton_Checked" />
                                </Grid>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateNodeSpacingTextBox" Text="20" />
                                <TextBlock Text=" (negative causes overlaps)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeIndent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateNodeIndentTextBox" Text="0" />
                                <TextBlock Text=" (when Start or End; >= zero)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="NodeIndentPastParent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateNodeIndentPastParentTextBox" Text="0" />
                                <TextBlock Text=" (fraction; 0-1)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="LayerSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateLayerSpacingTextBox" Text="50" />
                                <TextBlock Text=" (negative causes overlaps)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="LayerSpacingParentOverlap: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateLayerSpacingParentOverlapTextBox" Text="0" />
                                <TextBlock Text=" (fraction; 0-1)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Sorting: "
                           VerticalAlignment="Center" />
                                <Grid x:Name="AlternateSortingRadioButtonGroup" Tag="Ascending">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>
                                    <RadioButton Grid.Row="0" Grid.Column="0"
                    Content="Forward" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="0" Grid.Column="1"
                    Content="Reverse" Checked="RadioButton_Checked" />
                                    <RadioButton Grid.Row="1" Grid.Column="0"
                    Content="Ascending" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Grid.Row="1" Grid.Column="1"
                    Content="Descending" Checked="RadioButton_Checked" />
                                </Grid>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Compaction: "
                           VerticalAlignment="Center" />
                                <StackPanel x:Name="AlternateCompactionRadioButtonGroup" Tag="Block"
                          Orientation="Horizontal">
                                    <RadioButton Content="Block" Checked="RadioButton_Checked" IsChecked="True" />
                                    <RadioButton Content="None" Checked="RadioButton_Checked" />
                                </StackPanel>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="BreadthLimit: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateBreadthLimitTextBox" Text="0" />
                                <TextBlock Text=" (zero means no limit)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="RowSpacing: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateRowSpacingTextBox" Text="25" />
                                <TextBlock Text=" (negative causes overlaps)"
                         VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="RowIndent: "
                           VerticalAlignment="Center" />
                                <TextBox x:Name="AlternateRowIndentTextBox" Text="10" />
                                <TextBlock Text=" (>= zero)"
                           VerticalAlignment="Center" />
                            </StackPanel>
                        </StackPanel>
                    </ScrollViewer>
                </Grid>
            </Border>
        </Grid>
    </Border>

    <controls:GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="5" />

    <go:Diagram x:Name="myDiagram" Grid.Row="2"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch"
            NodeTemplate="{StaticResource nodeTemplate1}"
            LinkTemplate="{StaticResource linkTemplate1}">
        <go:Diagram.Layout>
            <go:TreeLayout
                Id="Gate" 
                Sorting="Ascending"
                Angle="90"
                Conditions="None"
                Comparer="{StaticResource theSimpleComparer}"/>
        </go:Diagram.Layout>
    </go:Diagram>
</Grid>

MainPage.xaml.cs


/* Copyright © Northwoods Software Corporation, 2008-2013. All Rights Reserved. */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Northwoods.GoXam;
using Northwoods.GoXam.Layout;
using Northwoods.GoXam.Model;

namespace GoSilverlightTreeTest2
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();

        myDiagram.Model = new GraphLinksModel<SimpleData, String, String, LinkData>();
        myDiagram.Model.Modifiable = true;

        GenerateTree_Click(null, null);
    }

    Random rand = new Random();

    // Takes the random collection of nodes and creates a random tree with them.
    // Respects the minimum and maximum number of links from each node.
    // (The minimum can be disregarded if we run out of nodes to link to)
    private void GenerateLinks()
    {
        LinksSource = new ObservableCollection<LinkData>();
        if (NodesSource.Count == 0) return;
        int minLinks, maxLinks;
        if (!int.TryParse(txtMinLinks.Text, out minLinks))
            minLinks = 1;
        if (!int.TryParse(txtMaxLinks.Text, out maxLinks) || minLinks > maxLinks)
            maxLinks = minLinks;

        List<SimpleData> available = NodesSource.ToList<SimpleData>();
        foreach (SimpleData next in NodesSource)
        {
            available.Remove(next);
            int children = rand.Next(minLinks, maxLinks + 1);
            for (int i = 1; i <= children; i++)
            {
                if (available.Count == 0) break;
                SimpleData to = available[0];
                available.Remove(to);
                LinksSource.Add(new LinkData() { From = next.Key, FromPort = "From", To = to.Key, ToPort = "To" });
            }
        }
    }

    // Creates a collection of randomly colored nodes.
    // Respects the maximum and minimum number of nodes.
    private void GenerateNodes()
    {
        NodesSource = new ObservableCollection<SimpleData>();
        int minNodes, maxNodes;
        if (!int.TryParse(txtMinNodes.Text, out minNodes))
            minNodes = 0;
        if (!int.TryParse(txtMaxNodes.Text, out maxNodes) || minNodes > maxNodes)
            maxNodes = minNodes;
        int numberOfNodes = rand.Next(minNodes, maxNodes + 1);

        for (NextNodeKey = 0; NextNodeKey < numberOfNodes; )
        {
            NodesSource.Add(new SimpleData()
            {
                Key = "Node" + NextNodeKey++.ToString(),
                Color = String.Format("#{0:X}{1:X}{2:X}", 120 + rand.Next(100), 120 + rand.Next(100), 120 + rand.Next(100))
            });
        }
        // Randomize the nodes a little:
        for (int i = 0; i < NodesSource.Count; i++)
        {
            int swap = rand.Next(0, NodesSource.Count);
            SimpleData temp = NodesSource[swap];
            NodesSource[swap] = NodesSource<em>;
            NodesSource<em> = temp;
        }
    }

    private ObservableCollection<SimpleData> NodesSource;
    private ObservableCollection<LinkData> LinksSource;
    private int NextNodeKey;

    // Generates a random tree respecting MinNodes/MaxNodes/MinLinks/MaxLinks
    private void GenerateTree_Click(object sender, RoutedEventArgs e)
    {
        GenerateNodes();
        GenerateLinks();
        myDiagram.Model.NodesSource = NodesSource;
        var lmodel = myDiagram.Model as GraphLinksModel<SimpleData, String, String, LinkData>;
        lmodel.LinksSource = LinksSource;

        myDiagram.LayoutDiagram();
    }

    // When a RadioButton becomes checked, set the Tag of the button container to the button's content (string)
    // For internationalization, this should use RadioButton.Tag to hold the value, not the RadioButton.Content
    private void RadioButton_Checked(object sender, RoutedEventArgs e)
    {
        RadioButton rb = sender as RadioButton;
        if (rb != null)
        {
            FrameworkElement group = VisualTreeHelper.GetParent(rb) as FrameworkElement;
            if (group != null)
            {
                group.Tag = rb.Content;
            }
        }
    }

    private void Node_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        // See if they clicked on a node
        var node = Part.FindAncestor<Node>(sender as UIElement);
        if (node == null)
            return;

        var nodeData = node.Data as SimpleData;
        if (nodeData == null)
            return;

        e.Handled = true;

        if (DiagramPanel.IsDoubleClick(e) || e.ClickCount > 1)
        {
            myDiagram.StartTransaction("AddNode");

            // Add the child node
            var newNode = new SimpleData()
            {
                Key = "Node" + NextNodeKey++.ToString(),
                Color = String.Format("#{0:X}{1:X}{2:X}", 120 + rand.Next(100), 120 + rand.Next(100), 120 + rand.Next(100))
            };
            NodesSource.Add(newNode);

            // Add the link
            LinksSource.Add(new LinkData() { From = nodeData.Key, FromPort = "From", To = newNode.Key, ToPort = "To" });

            myDiagram.CommitTransaction("AddNode");

            myDiagram.LayoutDiagram();
        }
        else
        {
            // Select the node
            myDiagram.SelectedParts.Clear();
            node.IsSelected = true;
        }
    }
}

// Compare vertexes by comparing the suffix of each node data key,
// treated as integer values rather than as strings.
public class SimpleComparer : IComparer<TreeVertex>
{
    public int Compare(TreeVertex x, TreeVertex y)
    {
        SimpleData a = x.Node.Data as SimpleData;
        SimpleData b = y.Node.Data as SimpleData;
        int num1 = int.Parse(a.Key.Replace("Node", ""));
        int num2 = int.Parse(b.Key.Replace("Node", ""));
        return num1.CompareTo(num2);
    }
}


// add some properties for each node data
public class SimpleData : GraphLinksModelNodeData<String>
{
    public String Color
    {
        get { return _Color; }
        set { if (_Color != value) { String old = _Color; _Color = value; RaisePropertyChanged("Color", old, value); } }
    }
    private String _Color = "White";
}

// no additional properties are needed for link data
public class LinkData : Northwoods.GoXam.Model.GraphLinksModelLinkData<String, String> { }

}

FYI, I handled this offline, due to the complexities involving different kinds of virtualization.