Routing around Nodes

I have question related to this:
I have attached an image about the link I am getting now based on FlowChart.

I have tried both “Orthogonal” and “AvoidsNodes”, I couldn’t get a better link. I didn’t define from and to ports. Is there a easy way to route the blue point to side of the node? more like the 2nd attached picture

any way to move the link label as the second image?

Thanks,

That’s because that Link seems to be defined to go from the port at the bottom of the node to the port at the top of the other node. If you change the port to be coming from the left side and going to the port at the left side, you’ll get routing that is basically what you are asking for.

However, it will not automatically route far enough to the left to account for the long label, if you are trying to avoid having the label overlap any other nodes. That’s because the default routings do not take the existence or size of any labels into account. However, you could do so yourself by overriding Route.AddOrthoPoints.

Thanks, walter!
Any way to do the routing automatically without specifying the ports?
We have a requirement explicitly says “no ports”.

Well, it should be trivial for you to experiment by removing all of the port elements from your node template(s). Actually, you probably will want to make the main Shape the single “port” – just set go:Node.PortId="".

I am more concerned how to do routing without ports as you described above. I want to route like 2nd image without defining the ports. Anyway to do so?

Maybe – it depends a lot on all of the other requirements that you might have. I would try it and see what you get, at which time we can try to adapt.

I had some free time, so I tried what I suggested. I’m assuming you are starting from the “Flow Chart” sample.

So the “Standard” node template becomes very simple if you remove the separate ports:

<DataTemplate x:Key="Standard"> <go:SpotPanel Style="{StaticResource SpotPanelStyle}" go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"> <go:NodePanel Sizing="Auto" go:SpotPanel.Main="True"> <go:NodeShape x:Name="Shape" Style="{StaticResource NodeShapeStyle}" go:Node.PortId="" go:Node.LinkableTo="True" go:Node.LinkableFrom="True" go:NodePanel.Figure="{Binding Path=Data.Figure}" /> <TextBlock Style="{StaticResource TextBlockStyle}" Text="{Binding Path=Data.Text, Mode=TwoWay}" /> </go:NodePanel> </go:SpotPanel> </DataTemplate>
To get the routing that you want for links, I added a second link template to a DataTemplateDictionary (which I assigned to Diagram.LinkTemplateDictionary):

<DataTemplate x:Key="Reverse"> <go:LinkPanel go:Part.Reshapable="True"> <go:Link.Route> <go:Route Routing="AvoidsNodes" Curve="JumpOver" Curviness="10" FromSpot="LeftRightSides" ToSpot="LeftRightSides" RelinkableFrom="True" RelinkableTo="True" /> </go:Link.Route> <Path Fill="Black" go:LinkPanel.ToArrow="Standard" /> <go:LinkShape Stroke="Black" StrokeThickness="2" /> <!-- this is the label that is only shown when connected to a Decision node --> <TextBlock Text="{Binding Path=Data.Text, Mode=TwoWay}" TextWrapping="Wrap" MaxWidth="50" go:Part.TextEditable="True" go:LinkPanel.Index="1" go:LinkPanel.Alignment="0 0 -2 0" Visibility="{Binding Path=Link, Converter={StaticResource theLabelVisibilityConverter}}" /> </go:LinkPanel> </DataTemplate>
Then I just need to set Category=“Reverse” on the MyLinkData XML elements that want to come out from the sides of a node and go into the sides of a node.

Judging from your other screenshot, it sounds like you want there to be a label all the time. If so, you can remove the Visibility binding.

I suppose the other node data templates could stand simplification too, and then the XML data won’t need the port information on each link data, but that’s easy too.

Thanks, walter! I will give a try.

Another related question to this, when I comment out the ports, how can I draw a link from one node to another. I can do it with ports, but when it is gone, I cann’t do it any more. Any ideas?

The user still can – notice the attribute/assignment of go:Node.LinkableTo=“True” and go:Node.LinkableFrom=“True” on the main Shape?

Actually, I forgot to move the cursor assignment – add Cursor=“Hand” to that NodeShape.

I tried with category=“reverse”. it doesn’t look like the 2nd image.
I had put the ports back, still doesn’t look like the 2nd image.
And the Reverse template is having "FromSpot=“LeftRightSides” ToSpot=“LeftRightSides”, this is what my requirement trying to avoid to define. If there is no ports in node template, the link template will not have those ports defined. That will be a problem, won’t it?

No, the whole shape is acting as the single “port” for the node. So that’s not a problem, as you can see the links are there, connected to the nodes. However I don’t understand why using the templates that I quoted above didn’t work for you. It’s as if the “Reverse” template wasn’t being used. The category name is case-sensitive, so “reverse” wouldn’t work.

Do you have an algorithmic way for deciding when to connect to the sides rather than to the tops/bottoms? If so, that might provide a way to avoid having to set the Category on the link data.

Thanks, walter! I made it working. I didn’t add LinkTemplateDictionary in the go:diagram. That is why it was falling apart.

walter, I got a new issue with this. Now I have two LinkTemplate, one is the old “LinkTemplate”, the other is “Reverse”. When I tried to modify the link, it draws, but not put the links on the diagram with the old code. I think it is confused with template the link should be used to draw?

I tried to change MyPartManager:PartManger "UpdateRouteDataPoints(Link link) to add a category to the link, it doesn’t work. Where should I take care of the two linktemplates when I add a link to the diagram?

many thanks,

Are you using any Diagram.Layout?

I don’t understand your question. There can be any number of DataTemplates in the Diagram.LinkTemplateDictionary. The Category property on the data chooses which DataTemplate to use when the data is represented by a Link. Trying to change the Category in an override of PartManager.UpdateRouteDataPoints is beyond the scope of that method.

I mainly focus on “Flow Chart” sample, I didn’t use any Diagram.Layout.

I can delete a link, but when I try to connect two nodes, the link temporarily shows, but won’t stay on the diagram. When I only have one linktemplate, it worked, but now I have two linktemplates, it doesn’t stay on the diagram any more.

Actually, one can make a single Link DataTemplate much smarter by using binding and a converter to return appropriate Spot values.

Here is the modified FlowChart.xaml:

[code]

<UserControl.Resources>
<local:LabelVisibilityConverter x:Key=“theLabelVisibilityConverter” />
<local:SpotConverter x:Key=“theSpotConverter” />

<!-- The brushes for Start and End nodes -->
<RadialGradientBrush x:Key="theGreenBrush" GradientOrigin="-0.2,-0.2">
  <GradientStop Color="White" Offset="0.35" />
  <GradientStop Color="Green" Offset="1" />
</RadialGradientBrush>
<RadialGradientBrush x:Key="theRedBrush" GradientOrigin="-0.2,-0.2">
  <GradientStop Color="White" Offset="0.35" />
  <GradientStop Color="Red" Offset="1" />
</RadialGradientBrush>

<!-- Note: Binding in styles does not work in Silverlight,
     so all such bindings have been moved to the target element. -->

<!-- Because SpotPanel is used as the root of each node template,
     in order to hold all of the (sometimes visible) ports,
     this style applies to all nodes -->
<Style TargetType="go:SpotPanel" x:Key="SpotPanelStyle">
  <Setter Property="go:Part.SelectionElementName" Value="Shape" />
  <Setter Property="go:Part.SelectionAdorned" Value="True" />
  <Setter Property="go:Node.LocationSpot" Value="Center" />
</Style>

<!-- For the primary shape of each node -->
<Style TargetType="go:NodeShape" x:Key="NodeShapeStyle">
  <Setter Property="Stroke" Value="Black" />
  <Setter Property="StrokeThickness" Value="1" />
  <Setter Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="-0.2,-0.2">
        <GradientStop Color="White" Offset="0.35" />
        <GradientStop Color="SkyBlue" Offset="1" />
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
  <!-- commented out to avoid UIPermission security exception in GoXbapDemo
  <Setter Property="Effect">
    <Setter.Value>
      <DropShadowEffect Color="Gray" />
    </Setter.Value>
  </Setter>
  -->
</Style>

<!-- For the text of each node -->
<Style TargetType="TextBlock" x:Key="TextBlockStyle">
  <Setter Property="TextWrapping" Value="Wrap" />
  <Setter Property="VerticalAlignment" Value="Center" />
  <Setter Property="HorizontalAlignment" Value="Center" />
  <Setter Property="Margin" Value="5" />
  <Setter Property="go:Part.TextEditable" Value="True" />
</Style>

<!-- The DataTemplateDictionary used to store all the
     DataTemplates for the Node categories. -->
<go:DataTemplateDictionary x:Key="NodeTemplateDictionary">
  <!-- There is a separate DataTemplate for each Node category.
     This allows for large differences between categories.-->
  <DataTemplate x:Key="Comment">
    <go:SpotPanel Style="{StaticResource SpotPanelStyle}"
                go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <go:NodePanel Sizing="Auto" go:SpotPanel.Main="True">
        <go:NodeShape x:Name="Shape" Style="{StaticResource NodeShapeStyle}"
                    Stroke="LightGray" Fill="LemonChiffon"
                    go:NodePanel.Figure="{Binding Path=Data.Figure}" />
        <TextBlock Style="{StaticResource TextBlockStyle}"
                 Text="{Binding Path=Data.Text, Mode=TwoWay}" />
      </go:NodePanel>
    </go:SpotPanel>
  </DataTemplate>

  <DataTemplate x:Key="Start">
    <go:SpotPanel Style="{StaticResource SpotPanelStyle}"
                  go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <go:NodePanel Sizing="Auto" go:SpotPanel.Main="True">
        <go:NodeShape x:Name="Shape" Style="{StaticResource NodeShapeStyle}"
                      go:Node.PortId="" go:Node.LinkableFrom="True" Cursor="Hand"
                      go:NodePanel.Figure="{Binding Path=Data.Figure}"
                      Fill="{StaticResource theGreenBrush}" />
        <TextBlock Style="{StaticResource TextBlockStyle}"
                   Text="{Binding Path=Data.Text, Mode=TwoWay}" />
      </go:NodePanel>
    </go:SpotPanel>
  </DataTemplate>

  <DataTemplate x:Key="Standard">
    <go:SpotPanel Style="{StaticResource SpotPanelStyle}"
                  go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <go:NodePanel Sizing="Auto" go:SpotPanel.Main="True">
        <go:NodeShape x:Name="Shape" Style="{StaticResource NodeShapeStyle}"
                      go:Node.PortId="" go:Node.LinkableTo="True" go:Node.LinkableFrom="True" Cursor="Hand"
                      go:NodePanel.Figure="{Binding Path=Data.Figure}" />
        <TextBlock Style="{StaticResource TextBlockStyle}"
                   Text="{Binding Path=Data.Text, Mode=TwoWay}" />
      </go:NodePanel>
    </go:SpotPanel>
  </DataTemplate>

  <DataTemplate x:Key="End">
    <go:SpotPanel Style="{StaticResource SpotPanelStyle}"
                  go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <go:NodePanel Sizing="Auto" go:SpotPanel.Main="True">
        <go:NodeShape x:Name="Shape" Style="{StaticResource NodeShapeStyle}"
                      go:Node.PortId="" go:Node.LinkableTo="True" Cursor="Hand"
                      go:NodePanel.Figure="{Binding Path=Data.Figure}"
                      Fill="{StaticResource theRedBrush}" />
        <TextBlock Style="{StaticResource TextBlockStyle}"
                   Text="{Binding Path=Data.Text, Mode=TwoWay}" />
      </go:NodePanel>
    </go:SpotPanel>
  </DataTemplate>
</go:DataTemplateDictionary>

<!-- A fairly standard LinkTemplate with AvoidsNodes routing,
     a JumpOver curve, and a slight curviness.
     The user can reconnect either end of the link.
     It has a polygon shaped like an arrowhead and a label. -->
<DataTemplate x:Key="LinkTemplate">
  <go:LinkPanel go:Part.Reshapable="True">
    <go:Link.Route>
      <go:Route Routing="AvoidsNodes" Curve="JumpOver" Curviness="10"
                FromSpot="{Binding Path=Link, Converter={StaticResource theSpotConverter}, ConverterParameter=True}"
                ToSpot="{Binding Path=Link, Converter={StaticResource theSpotConverter}}"
                RelinkableFrom="True" RelinkableTo="True" />
    </go:Link.Route>
    <Path Fill="Black" go:LinkPanel.ToArrow="Standard" />
    <go:LinkShape Stroke="Black" StrokeThickness="1.5" />
    <!-- this is the label that is only shown when connected to a Decision node -->
    <TextBlock Text="{Binding Path=Data.Text, Mode=TwoWay}"
               TextWrapping="Wrap" MaxWidth="50" go:Part.TextEditable="True"
               go:LinkPanel.Index="1" go:LinkPanel.Alignment="0 0 -2 0"
               Visibility="{Binding Path=Link,
                            Converter={StaticResource theLabelVisibilityConverter}}" />
  </go:LinkPanel>
</DataTemplate>

</UserControl.Resources>

[/code] Besides removing all of the port elements, I removed XAML that the ports used.

The only changes to the C# file are the addition of a converter:

public class SpotConverter : Converter { public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Link link = value as Link; if (link != null && link.FromNode != null && link.ToNode != null) { if (parameter != null) { MyNodeData d = link.FromNode.Data as MyNodeData; if (d != null && d.Key.StartsWith("Conditional")) return Spot.LeftRightSides; } if (link.FromNode.Bounds.Top > link.ToNode.Bounds.Bottom) return Spot.LeftRightSides; } if (parameter != null) return Spot.MiddleBottom; return Spot.MiddleTop; } }
and the deletion of the Node_MouseEnter, Node_MouseLeave, and SetPortsVisible functions, since that functionality is no longer useful.

Also, I removed the FromPort=“0” ToPort=“1” settings on the link data in the XML file, although this is not required. If it doesn’t find a port with a particular identifier (such as “1”), it uses the default port (the one named with the empty string), which in the updated XAML is just the main Shape. But you didn’t want to use ports at all, so I figured I should remove any vestiges from the data, also.