Link thickness on MouseOver

I’m trying to increase the thickness of my links when the mouse is over top of them in order to make them easier to select, but what I have set up is not behaving as expected. My link template looks like this:

<go:BooleanThicknessConverter x:Key="LinkThicknessChooser" TrueThickness="5" FalseThickness="3" />


<DataTemplate x:Key="Pipe">
                <go:LinkPanel go:Part.Reshapable="True"
                              go:Part.SelectionAdorned="True"
                              go:Part.SelectionElementName="Pipe"
                              go:Part.DropOntoBehavior="SplicesIntoLink">
                    <go:Link.Route>
                        <tools:MyLinkRoute Routing="AvoidsNodes" Curve="JumpOver" Corner="5"                                           
                                           FromEndSegmentLength="15" ToEndSegmentLength="15"                                           
                                           FromEndSegmentDirection="RotatedNodeOrthogonal"                                           
                                           ToEndSegmentDirection="RotatedNodeOrthogonal"
                                           RelinkToAdornmentTemplate="{StaticResource ReLinkToHandleTemplate}"
                                           RelinkFromAdornmentTemplate="{StaticResource ReLinkFromHandleTemplate}"
                                           ToShortLength="{Binding Path=Data.ToArrow.ArrowOffset, Mode=OneWay}"
                                           RelinkableFrom="True" RelinkableTo="True" />
                    </go:Link.Route>
                    <!---->                    
                    <go:LinkShape Name="linkBase"
                        Stroke="{Binding Path=Data.PipeColor, Mode=TwoWay}" 
                        StrokeThickness="{Binding Path=Link.IsMouseOver, Converter={StaticResource LinkThicknessChooser}}" />
                    <go:LinkShape Name="Pipe"
                        Stroke="{Binding Path=Link.IsDropOntoAccepted, Converter={StaticResource theStrokeChooser}}"
                        StrokeThickness="5" />
                    
                    <Path Name="arrowhead"
                        Stroke="{Binding Path=Data.PipeColor, Mode=TwoWay}"
                        Fill="{Binding Path=Data.PipeColor, Mode=TwoWay}"
                        StrokeThickness="2"
                        Data="{Binding Path=Data.ToArrow.Points, Mode=OneWay}"
                        go:LinkPanel.Alignment="MiddleRight"
                        go:LinkPanel.Index="-1"
                        go:LinkPanel.Orientation="Along"/>

                </go:LinkPanel>            
            </DataTemplate>

I would expect the path to default to a thickness of 3, and then when the mouse is over top of the link have the thickness increase to 5, but this is what I am seeing:

Mouse not over:

Mouse over:

Mouse not over:

Mouse over:

Any thoughts on what I am be overlooking?

Thanks
Ryan

You can’t use a BooleanThicknessConverter, because that returns a Thickness structure, not a double.

public class WidthConverter : Converter {
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value is Boolean) {
if ((Boolean)value == true) return 5.0;
if ((Boolean)value == false) return 2.0;
}
return base.Convert(value, targetType, parameter, culture);
}
}
and then:
<local:WidthConverter x:Key=“theWidthConverter” />
and:
StrokeThickness="{Binding Path=Link.IsMouseOver, Converter={StaticResource theWidthConverter}}"

I’ve changed to use the converter, but it still seems to be doing the same thing. I’ve noticed now (it may have been happening before as well) that when the link is selected and the mouse is over it has the correct thickness, but the link is thinner than it should be when the mouse is not over. This is what I am seeing with the link selected:

Mouse not over:

Mouse over:

Well, I don’t have your environment, of course, but when I dropped the code I quoted above into one of the sample apps, it worked as I think you would expect.

I’ve tried copying over the code from the Draggable Link demo into a new .Net v4 WPF project and then putting in my link template and it seems to be doing the same thing (the thickness is different depending on if the link is selected or not).

Here’s the 2 files I am using…

MainWindow.xaml


<Window x:Class="MouseOverTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:go="http://schemas.nwoods.com/GoXam"
        xmlns:local="clr-namespace:MouseOverTest"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <DataTemplate x:Key="NodeSelectionAdornmentTemplate">
            <go:SelectionHandle go:Part.Selectable="False"
            Stroke="DeepSkyBlue" StrokeThickness="1" StrokeDashArray="2 2" />
        </DataTemplate>

        <DataTemplate x:Key="NodeResizeAdornmentTemplate">
            <go:SpotPanel>
                <go:ToolHandle go:SpotPanel.Spot="0.0 0.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
                <go:ToolHandle go:SpotPanel.Spot="0.5 0.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
                <go:ToolHandle go:SpotPanel.Spot="1.0 0.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />

                <go:ToolHandle go:SpotPanel.Spot="0.0 0.5" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
                <go:ToolHandle go:SpotPanel.Spot="1.0 0.5" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />

                <go:ToolHandle go:SpotPanel.Spot="0.0 1.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
                <go:ToolHandle go:SpotPanel.Spot="0.5 1.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
                <go:ToolHandle go:SpotPanel.Spot="1.0 1.0" go:NodePanel.Figure="Rectangle" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
            </go:SpotPanel>
        </DataTemplate>

        <DataTemplate x:Key="NodeRotateAdornmentTemplate">
            <go:ToolHandle go:NodePanel.Figure="Ellipse" Width="6" Height="6" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" Cursor="Hand" />
        </DataTemplate>

        <DataTemplate x:Key="NodeTemplate">
            <go:SpotPanel
          go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"
          go:Node.LocationSpot="Center"
          go:Part.SelectionAdorned="True"
          go:Part.SelectionAdornmentTemplate="{StaticResource NodeSelectionAdornmentTemplate}"
          go:Part.Resizable="True"
          go:Part.ResizeElementName="Icon"
          go:Part.ResizeAdornmentTemplate="{StaticResource NodeResizeAdornmentTemplate}"
          go:Node.RotationAngle="{Binding Path=Data.Angle, Mode=TwoWay}"
          go:Part.Rotatable="True"
          go:Part.RotateAdornmentTemplate="{StaticResource NodeRotateAdornmentTemplate}">
                <go:NodePanel>
                    <go:NodeShape x:Name="Icon"
                go:NodePanel.Figure="{Binding Path=Data.Figure}"
                Stroke="Black" StrokeThickness="1"
                Fill="{Binding Path=Data.Color}"
                Width="{Binding Path=Data.Width, Mode=TwoWay}"
                Height="{Binding Path=Data.Height, Mode=TwoWay}"
                go:NodePanel.Spot1="0 0" go:NodePanel.Spot2="1 1"
                go:Node.PortId="" go:Node.LinkableFrom="True" go:Node.LinkableTo="True" Cursor="Hand"
                go:Node.LinkableDuplicates="True" go:Node.LinkableSelfNode="True" />
                    <Rectangle Fill="Transparent" Margin="12" 
                     HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
                    <TextBlock Text="{Binding Path=Data.Text, Mode=TwoWay}" TextWrapping="Wrap"
                     HorizontalAlignment="Center" VerticalAlignment="Center"
                     go:Part.TextEditable="True" />
                </go:NodePanel>
                <Rectangle Fill="Transparent" Width="6" Height="6"
                 go:SpotPanel.Spot="MiddleLeft" go:SpotPanel.Alignment="MiddleLeft"
                 go:Node.PortId="L" go:Node.LinkableFrom="True" go:Node.LinkableTo="True" Cursor="Hand"
                 go:Node.FromSpot="MiddleLeft" go:Node.ToSpot="MiddleLeft" />
                <Rectangle Fill="Transparent" Width="6" Height="6"
                 go:SpotPanel.Spot="MiddleTop" go:SpotPanel.Alignment="MiddleTop"
                 go:Node.PortId="T" go:Node.LinkableFrom="True" go:Node.LinkableTo="True" Cursor="Hand"
                 go:Node.FromSpot="MiddleTop" go:Node.ToSpot="MiddleTop" />
                <Rectangle Fill="Transparent" Width="6" Height="6"
                 go:SpotPanel.Spot="MiddleRight" go:SpotPanel.Alignment="MiddleRight"
                 go:Node.PortId="R" go:Node.LinkableFrom="True" go:Node.LinkableTo="True" Cursor="Hand"
                 go:Node.FromSpot="MiddleRight" go:Node.ToSpot="MiddleRight" />
                <Rectangle Fill="Transparent" Width="6" Height="6"
                 go:SpotPanel.Spot="MiddleBottom" go:SpotPanel.Alignment="MiddleBottom"
                 go:Node.PortId="B" go:Node.LinkableFrom="True" go:Node.LinkableTo="True" Cursor="Hand"
                 go:Node.FromSpot="MiddleBottom" go:Node.ToSpot="MiddleBottom" />
            </go:SpotPanel>
        </DataTemplate>

        <DataTemplate x:Key="PaletteNodeTemplate">
            <go:NodePanel go:Part.SelectionAdorned="True">
                <go:NodeShape go:NodePanel.Figure="{Binding Path=Data.Figure}"
              Stroke="Black" StrokeThickness="1"
              Fill="{Binding Path=Data.Color}"
              Width="{Binding Path=Data.Width, Mode=TwoWay}"
              Height="{Binding Path=Data.Height, Mode=TwoWay}"
              go:NodePanel.Spot1="0 0" go:NodePanel.Spot2="1 1" />
                <TextBlock Text="{Binding Path=Data.Text, Mode=TwoWay}" TextWrapping="Wrap"
                   HorizontalAlignment="Center" VerticalAlignment="Center" />
            </go:NodePanel>
        </DataTemplate>

        <DataTemplate x:Key="LinkSelectionAdornmentTemplate">
            <go:SelectionHandle go:NodePanel.Figure="None" Stroke="DeepSkyBlue" StrokeThickness="2" go:Part.Selectable="False" />
        </DataTemplate>

        <DataTemplate x:Key="LinkReshapeHandleTemplate">
            <go:ToolHandle go:NodePanel.Figure="Diamond" Width="7" Height="7" Fill="LightBlue" Stroke="DeepSkyBlue" StrokeThickness="1" />
        </DataTemplate>

        <local:WidthConverter x:Key="theWidthConverter" />
        <go:BooleanBrushConverter x:Key="theStrokeChooser" FalseBrush="Transparent" TrueBrush="Magenta" />
        
        <DataTemplate x:Key="LinkTemplate">
            <go:LinkPanel go:Part.Reshapable="True"
                              go:Part.SelectionAdorned="True"
                              go:Part.SelectionElementName="Pipe"
                              go:Part.DropOntoBehavior="SplicesIntoLink">

                <go:LinkShape Name="Pipe" Stroke="Black" StrokeThickness="{Binding Path=Link.IsMouseOver, Converter={StaticResource theWidthConverter}}" />
                <go:LinkShape Name="LinkHighlight" Stroke="{Binding Path=Link.IsDropOntoAccepted, Converter={StaticResource theStrokeChooser}}" StrokeThickness="5" />


                <Path Name="arrowhead" Stroke="Black" Fill="Black" StrokeThickness="2"
                        go:LinkPanel.ToArrow="Triangle"
                        go:LinkPanel.Alignment="MiddleRight"
                        go:LinkPanel.Index="-1"
                        go:LinkPanel.Orientation="Along"/>
            </go:LinkPanel>
        </DataTemplate>

        <DataTemplate x:Key="PaletteLinkTemplate">
            <go:LinkPanel Background="Transparent"
          go:Part.SelectionElementName="Path" go:Part.SelectionAdorned="True"
          go:Part.SelectionAdornmentTemplate="{StaticResource LinkSelectionAdornmentTemplate}">
                <go:LinkShape Stroke="Transparent" StrokeThickness="5" />
                <go:LinkShape x:Name="Path" Stroke="Black" StrokeThickness="1" />
                <Path Fill="Black" go:LinkPanel.ToArrow="Triangle" />
            </go:LinkPanel>
        </DataTemplate>

        <DataTemplate x:Key="TemporaryLinkTemplate">
            <go:LinkPanel go:Part.Selectable="False" go:Link.LayerName="Tool">
                <go:Link.Route>
                    <go:Route Routing="Orthogonal" />
                </go:Link.Route>
                <go:LinkShape x:Name="Path" Stroke="Blue" StrokeThickness="1" />
                <Path Fill="Black" go:LinkPanel.ToArrow="Triangle" />
            </go:LinkPanel>
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <go:Palette Grid.Row="0"
                x:Name="myPalette"
                BorderBrush="Black" BorderThickness="1" Padding="5"
                HorizontalContentAlignment="Left" VerticalContentAlignment="Top"
                NodeTemplate="{StaticResource PaletteNodeTemplate}"
                LinkTemplate="{StaticResource PaletteLinkTemplate}">
                <go:Diagram.PartManager>
                    <local:CustomPartManager />
                </go:Diagram.PartManager>
                <go:Diagram.Layout>
                    <go:GridLayout CellSize="10 10" />
                </go:Diagram.Layout>
            </go:Palette>
            <StackPanel Grid.Row="1">
            </StackPanel>
        </Grid>
        <go:Diagram Grid.Column="1"
                x:Name="myDiagram" AllowDrop="True"
                Padding="10"
                HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
                NodeTemplate="{StaticResource NodeTemplate}"
                LinkTemplate="{StaticResource LinkTemplate}"
                GridVisible="True" GridSnapEnabled="True">
            <go:Diagram.PartManager>
                <local:CustomPartManager />
            </go:Diagram.PartManager>
            <go:Diagram.LinkingTool>
                <go:LinkingTool PortGravity="20"
          TemporaryLinkTemplate="{StaticResource TemporaryLinkTemplate}" />
            </go:Diagram.LinkingTool>
            <go:Diagram.RelinkingTool>
                <go:RelinkingTool PortGravity="20"
          TemporaryLinkTemplate="{StaticResource TemporaryLinkTemplate}" />
            </go:Diagram.RelinkingTool>
            <go:Diagram.DraggingTool>
                <go:DraggingTool DraggableLinks="True" />
            </go:Diagram.DraggingTool>
            <go:Diagram.RotatingTool>
                <local:CustomRotatingTool SnapAngleMultiple="15" SnapAngleEpsilon="15" />
            </go:Diagram.RotatingTool>
            <go:Diagram.GridPattern>
                <go:GridPattern CellSize="10 10">
                    <Path Stroke="LightGray" StrokeThickness="0.2" go:GridPattern.Figure="HorizontalLine" />
                    <Path Stroke="LightGray" StrokeThickness="0.2" go:GridPattern.Figure="VerticalLine" />
                    <Path Stroke="LightGray" StrokeThickness="0.3" go:GridPattern.Figure="HorizontalLine" go:GridPattern.Interval="2" />
                    <Path Stroke="LightGray" StrokeThickness="0.3" go:GridPattern.Figure="VerticalLine" go:GridPattern.Interval="2" />
                    <Path Stroke="Gray" StrokeThickness="0.3" go:GridPattern.Figure="HorizontalLine" go:GridPattern.Interval="4" />
                    <Path Stroke="Gray" StrokeThickness="0.3" go:GridPattern.Figure="VerticalLine" go:GridPattern.Interval="4" />
                    <Path Stroke="Black" StrokeThickness="0.3" go:GridPattern.Figure="HorizontalLine" go:GridPattern.Interval="8" />
                    <Path Stroke="Black" StrokeThickness="0.3" go:GridPattern.Figure="VerticalLine" go:GridPattern.Interval="8" />
                </go:GridPattern>
            </go:Diagram.GridPattern>
        </go:Diagram>
    </Grid>
</Window>

MainWindow.xaml.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;
using Northwoods.GoXam;
using Northwoods.GoXam.Model;
using Northwoods.GoXam.Tool;

namespace MouseOverTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
      InitializeComponent();

      // palette
      var pmodel = new MyModel();
      pmodel.ValidUnconnectedLinks = ValidUnconnectedLinks.Allowed;
      pmodel.NodesSource = new ObservableCollection<MyData>() {
          new MyData() { Text="Alpha", Color="Pink" },
          new MyData() { Text="Beta", Color="PapayaWhip", Figure=NodeFigure.Diamond },
          new MyData() { Text="Gamma", Color="PeachPuff", Figure=NodeFigure.Database }
      };
      pmodel.LinksSource = new ObservableCollection<MyLinkData>() {
          new MyLinkData() { Points=new List<Point>() { new Point(0, 0), new Point(25, 0), new Point(25, 25), new Point(50, 25) } },
          new MyLinkData() { Points=new List<Point>() { new Point(50, 50), new Point(25, 50), new Point(25, 75), new Point(0, 75) } },
      };
      myPalette.LayoutCompleted += LoadLinkRoutes;
      myPalette.Model = pmodel;

      // initial diagram
      var model = new MyModel();
      model.Modifiable = true;
      model.ValidUnconnectedLinks = ValidUnconnectedLinks.Allowed;
      model.NodesSource = new ObservableCollection<MyData>() {
          new MyData() { Key="A", Text="A", Color="Yellow", Figure=NodeFigure.ACvoltageSource, Location = new Point(50, 200) },
          new MyData() { Key="B", Text="B", Color="Aquamarine", Figure=NodeFigure.Alternative, Location = new Point(200, 50) },
          new MyData() { Key="C", Text="C", Color="Wheat", Figure=NodeFigure.AndGate, Location = new Point(200, 200), Width=100, Height=50 },
      };
      model.LinksSource = new ObservableCollection<MyLinkData>() {
          new MyLinkData() { From="A", Text="only connected to A", Points=new List<Point>() { new Point(50, 230), new Point(50, 240), new Point(50, 244), new Point(103, 244), new Point(103, 283), new Point(103, 293) } }
      };
      myDiagram.LayoutCompleted += LoadLinkRoutes;
      myDiagram.Model = model;
      model.HasUndoManager = true;
      myDiagram.AllowDrop = true;  // must be in code for Silverlight3
    }

        private void LoadLinkRoutes(Object s, EventArgs e)
        {
            // just set the Route points once per Load
            myDiagram.LayoutCompleted -= LoadLinkRoutes;
            foreach (Link link in myDiagram.Links)
            {
                var d = link.Data as MyLinkData;
                if (d == null || d.Points == null) continue;
                link.Route.Points = (IList<Point>)d.Points;
            }
            myDiagram.PartManager.UpdatesRouteDataPoints = true;  // OK for CustomPartManager to update Transition.Points automatically
        }
  }

    public class WidthConverter : Converter
    {
        public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Boolean)
            {
                if ((Boolean)value == true) return 5.0;
                if ((Boolean)value == false) return 2.0;
            }
            return base.Convert(value, targetType, parameter, culture);
        }
    }

  public class MyModel : GraphLinksModel<MyData, String, String, MyLinkData> {
    // nothing to add or override, for now
  }


  // data representing important state about each node
#if !SILVERLIGHT
  [Serializable]
#endif
  public class MyData : GraphLinksModelNodeData<String> {
    public NodeFigure Figure {
      get { return _Figure; }
      set { if (_Figure != value) { NodeFigure old = _Figure; _Figure = value; RaisePropertyChanged("Figure", old, value); } }
    }
    private NodeFigure _Figure = NodeFigure.Rectangle;

    public String Color {
      get { return _Color; }
      set { if (_Color != value) { String old = _Color; _Color = value; RaisePropertyChanged("Color", old, value); } }
    }
    private String _Color = "White";

    public double Width {
      get { return _Width; }
      set { if (_Width != value) { double old = _Width; _Width = value; RaisePropertyChanged("Width", old, value); } }
    }
    private double _Width = 60;

    public double Height {
      get { return _Height; }
      set { if (_Height != value) { double old = _Height; _Height = value; RaisePropertyChanged("Height", old, value); } }
    }
    private double _Height = 60;

    public double Angle {
      get { return _Angle; }
      set { if (_Angle != value) { double old = _Angle; _Angle = value; RaisePropertyChanged("Angle", old, value); } }
    }
    private double _Angle;

    // Because we added properties that we want to persist,
    // we should override these two methods to write/read those properties.
    public override XElement MakeXElement(XName n) {
      XElement e = base.MakeXElement(n);
      e.Add(XHelper.AttributeEnum<NodeFigure>("Figure", this.Figure, NodeFigure.Rectangle));
      e.Add(XHelper.Attribute("Color", this.Color, "White"));
      e.Add(XHelper.Attribute("Width", this.Width, 50.0));
      e.Add(XHelper.Attribute("Height", this.Height, 50.0));
      e.Add(XHelper.Attribute("Angle", this.Angle, 0.0));
      return e;
    }

    public override void LoadFromXElement(XElement e) {
      base.LoadFromXElement(e);
      this.Figure = XHelper.ReadEnum<NodeFigure>("Figure", e, NodeFigure.Rectangle);
      this.Color = XHelper.Read("Color", e, "White");
      this.Width = XHelper.Read("Width", e, 50.0);
      this.Height = XHelper.Read("Height", e, 50.0);
      this.Angle = XHelper.Read("Angle", e, 0.0);
    }
  }


  // data representing important state about each link
#if !SILVERLIGHT
  [Serializable]
#endif
  public class MyLinkData : GraphLinksModelLinkData<String, String> {
    // nothing to add or override, for now
  }


  // no data-binding of Route.Points means we have to copy the Points data explicitly
  public class CustomPartManager : PartManager {
    public CustomPartManager() {
      this.UpdatesRouteDataPoints = true;  // call UpdateRouteDataPoints when Link.Route.Points has changed
    }

    // copy Route.Points to MyLinkData
    public override ICopyDictionary CopyParts(IEnumerable<Part> coll, IDiagramModel destmodel) {
      ICopyDictionary dict = base.CopyParts(coll, destmodel);
      foreach (object data in dict.SourceCollection.Links) {
        MyLinkData origdata = data as MyLinkData;
        Link origlink = FindLinkForData(origdata, this.Diagram.Model);
        if (origlink != null && origlink.Route.PointsCount > 1) {
          // copy the MyLinkData.Points
          MyLinkData copieddata = dict.FindCopiedLink(origdata) as MyLinkData;
          if (copieddata != null) {
            copieddata.Points = new List<Point>(origlink.Route.Points);
            // now transfer to the Link.Route.Points
            Link copiedlink = FindLinkForData(copieddata, this.Diagram.Model);
            if (copiedlink != null) {
              copiedlink.Route.Points = (IList<Point>)copieddata.Points;
            }
          }
        }
      }
      return dict;
    }

    // use MyLinkData.Points, if any, when creating a Link
    protected override void OnLinkAdded(Link link) {
      base.OnLinkAdded(link);
      MyLinkData data = link.Data as MyLinkData;
      if (data != null && data.Points != null) {
        link.Route.Points = (IList<Point>)data.Points;
      }
    }

    // this supports undo/redo of link route reshaping
    protected override void UpdateRouteDataPoints(Link link) {
      if (!this.UpdatesRouteDataPoints) return;   // in coordination with Load_Click and LoadLinkRoutes, above
      MyLinkData data = link.Data as MyLinkData;
      if (data != null) {
        data.Points = new List<Point>(link.Route.Points);
      }
    }
  }


  // put rotation handle above node instead of on the right side
  public class CustomRotatingTool : RotatingTool {
    private const String ToolCategory = "Rotate";
    public override void UpdateAdornments(Part part) {
      if (part == null || part is Link) return;  // this tool never applies to Links
      Adornment adornment = null;
      if (part.IsSelected) {
        FrameworkElement selelt = part.SelectionElement;
        if (selelt != null && part.CanRotate() && Part.IsVisibleElement(selelt)) {
          adornment = part.GetAdornment(ToolCategory);
          if (adornment == null) {
            DataTemplate template = part.RotateAdornmentTemplate;
            if (template == null) template = Diagram.FindDefault<DataTemplate>("DefaultRotateAdornmentTemplate");
            adornment = part.MakeAdornment(selelt, template);
            if (adornment != null) {
              adornment.Category = ToolCategory;
              adornment.LocationSpot = Spot.Center;
            }
          }
          if (adornment != null) {
            adornment.Location = part.GetElementPoint(selelt, new Spot(0.5, 0, 0, -30));  // above middle top
          }
        }
      }
      part.SetAdornment(ToolCategory, adornment);
    }

    protected override void DoRotate(double newangle) {
      base.DoRotate(newangle + 90);
    }

    public override void DoCancel() {
      base.DoRotate(this.OriginalAngle);
      StopTool();
    }
  }
}

Another note from further testing… links that are drawn from top-right to bottom-left and links that are from bottom-left to top-right adjust as expected. Links that are drawn from top-left to bottom-right and the other way around, or vertical/horizontal don’t adjust properly.

Thanks for the bug report. I have sent you e-mail.