GoWPF 3.0.4 Links not perfectly visually connected to to node shape

Hi Walter,

when i draw some nodes with different shapes and i connect links between those, each start and end of a link seems to perfectly “visually connect” to the outer border of the shape. - (Using only a center port in my sample)

Example:
LinksSnappedPerferctlyToNodeShape

My problem is, if i serialize the data, close and reopen it in a diagram, all the links which are not coming or going to a non-rectangle shaped node are not “visually connected” perfectly anymore and i wonder why this might be the case.

Example:

Do you have any idea why this is the case?
Many thanks meanwhile, Hannes

It seems like, it either tries to connect to a rectangle shape, or to the adorner itself:
image

This is my default selection template:

<DataTemplate x:Key="NodeSelectionAdornmentTemplate">
    <go:SelectionHandle go:Part.Selectable="False"
                        Stroke="{StaticResource BrushYellow}" 
                        StrokeThickness="2" 
                        StrokeDashArray="2 2" />
</DataTemplate>

But i think the adorner think, is not the issue.
Anyway i am binding the shape:
image

Could this maybe cause such a behavior?

Links only connect with Nodes, so the presence or absence of Adornments does not really matter.
Are you saving and loading link routes at all?
Is the shape element declared to be a port?
Why is the NodePanel.Figure Binding TwoWay?

ad) Are you saving and loading link routes at all?
Nope, i dont do this. - They get recalculated initially.

ad) Is the shape element declared to be a port?
The NodeTemplate is: SpotPanel > NodePanel > NodeShape and on the NodeShape go:NodePanel.Figure is declared.

ad) Why is the NodePanel.Figure Binding TwoWay?
Made it OneWay again, was just a relict from my testing.

I’m not able to reproduce this problem. Could you please share your Node template?
I assume your SelectionAdornmentTemplate and Link template aren’t relevant to the problem.

<DataTemplate x:Key="NodeTemplate">
    <go:SpotPanel go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  go:Node.ZOrder="{Binding Path=Data.ZIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  go:Part.SelectionAdorned="True"
                  go:Part.SelectionAdornmentTemplate="{StaticResource NodeSelectionAdornmentTemplate}"
                  Cursor="Hand" Background="Transparent"
                  go:Part.Resizable="{Binding Path=Data.Resizable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  go:Part.ResizeElementName="Icon"
                  go:Part.ResizeAdornmentTemplate="{StaticResource NodeResizeAdornmentTemplate}"
                  go:Node.RotationAngle="{Binding Path=Data.Angle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  go:Part.Rotatable="{Binding Path=Data.Rotatable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  go:Part.RotateAdornmentTemplate="{StaticResource NodeRotateAdornmentTemplate}">

        <go:SpotPanel.ToolTip>
            <StackPanel>
                <TextBlock Margin="2">
                    <!--<Run Text="{localization:LocalizationMarkup Key=PanelLevel.Palette.ToolTip.Name}" />-->
                    <Run Text="{Binding Path=Data.ElementType.Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" FontWeight="DemiBold"/>
                </TextBlock>
                <TextBlock Margin="2">
                    <!--<Run Text="{localization:LocalizationMarkup Key=PanelLevel.Palette.ToolTip.Description}" />-->
                    <Run Text="{Binding Path=Data.ElementType.Description, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                </TextBlock>
            </StackPanel>
        </go:SpotPanel.ToolTip>

        <go:NodePanel x:Name="NodePanel" Background="Transparent">
            <ContextMenuService.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.SelectAll}"
                          Command="{Binding Path=Data.SelectAllCommand}">
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconSelect}"/>
                        </MenuItem.Icon>
                    </MenuItem>

                    <MenuItem Header="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.SelectAllOfType}"
                          Command="{Binding Path=Data.SelectAllOfTypeCommand}">
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconSelect}"/>
                        </MenuItem.Icon>
                    </MenuItem>

                    <MenuItem Header="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.AddAllOfTypeToSelection}"
                          Command="{Binding Path=Data.AddAllOfTypeToSelectionCommand}">
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconSelect}"/>
                        </MenuItem.Icon>
                    </MenuItem>

                    <MenuItem Header="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.RemoveAllOfTypeFromSelection}"
                          Command="{Binding Path=Data.RemoveAllOfTypeFromSelectionCommand}">
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconSelect}"/>
                        </MenuItem.Icon>
                    </MenuItem>

                    <Separator />

                    <MenuItem>
                        <MenuItem.Header>
                            <TextBlock>
                                <Run Text="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.BringToBack}" />
                                <Run Text="(" />
                                <Run Text="{Binding Path=Data.ZIndex, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                                <Run Text=")" />
                            </TextBlock>
                        </MenuItem.Header>
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconBringToBack}"/>
                        </MenuItem.Icon>
                        <MenuItem.Items>
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-6" Header="-6"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-5" Header="-5"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-4" Header="-4"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-3" Header="-3"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-2" Header="-2"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="-1" Header="-1"  />
                            <MenuItem Command="{Binding Path=Data.BringToBackCommand}" CommandParameter="0" Header="0" />
                        </MenuItem.Items>
                    </MenuItem>

                    <MenuItem>
                        <MenuItem.Header>
                            <TextBlock>
                                <Run Text="{localization:LocalizationMarkup Key=PanelLevel.Node.ContextMenue.BringToFront}" />
                                <Run Text="(" />
                                <Run Text="{Binding Path=Data.ZIndex, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                                <Run Text=")" />
                            </TextBlock>
                        </MenuItem.Header>
                        <MenuItem.Icon>
                            <ContentControl Width="18" ContentTemplate="{StaticResource IconBringToFront}"/>
                        </MenuItem.Icon>
                        <MenuItem.Items>
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="0" Header="0" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="1" Header="1" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="2" Header="2" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="3" Header="3" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="4" Header="4" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="5" Header="5" />
                            <MenuItem Command="{Binding Path=Data.BringToFrontCommand}" CommandParameter="6" Header="6" />
                        </MenuItem.Items>
                    </MenuItem>
                </ContextMenu>
            </ContextMenuService.ContextMenu>

            <go:NodeShape x:Name="Icon"
                          go:NodePanel.Figure="{Binding Path=Data.Shape, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:ElementShapeToNodeFigureConverter}}"
                          Stroke="{Binding Path=Data.BorderColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}" 
                          StrokeThickness="{Binding Path=Data.BorderThickness, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
                          Fill="{Binding Path=Data.BackColor, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                          Width="{Binding Path=Data.Width, FallbackValue='40', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                          Height="{Binding Path=Data.Height, FallbackValue='40', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"                            
                          MinWidth="10" MinHeight="10"
                          go:NodePanel.Spot1="0 0" go:NodePanel.Spot2="1 1"
                          go:Node.PortId="Center" 
                          go:Node.LinkableFrom="{Binding Path=Data.LinkableFrom, FallbackValue='True', Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                          go:Node.LinkableTo="{Binding Path=Data.LinkableTo, FallbackValue='True', Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                          go:Node.LinkableMaximum="{Binding Path=Data.LinkableMaximum, FallbackValue='1', Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                          Cursor="Cross"
                          go:Node.LinkableDuplicates="True" go:Node.LinkableSelfNode="False" />

            <!--#region Inner drag object (Move node) -->

            <Ellipse x:Name="InnerDragObject" Fill="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Ellipse.Margin>
                    <MultiBinding Converter="{converters:EllipseMarginConverter}">
                        <Binding Path="Data.Width"/>
                        <Binding Path="Data.Height"/>
                    </MultiBinding>
                </Ellipse.Margin>
            </Ellipse>

            <!--#endregion-->
            

            <TextBlock Text="{Binding Path=Data.TopLeftParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Left" 
                       VerticalAlignment="Top"
                       Margin="2,-2,0,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.TopRightParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Right" 
                       VerticalAlignment="Top"
                       Margin="0,-2,2,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.CenterParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Center"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.BottomLeftParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Left" 
                       VerticalAlignment="Bottom"
                       Margin="2,0,0,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.BottomRightParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Right" 
                       VerticalAlignment="Bottom"
                       Margin="0,0,2,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.CenterLeftParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Left" 
                       VerticalAlignment="Center"
                       Margin="2,0,0,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.CenterRightParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Right" 
                       VerticalAlignment="Center"
                       Margin="0,0,2,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.CenterTopParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Top"
                       Margin="0,-2,0,0"
                       go:Part.TextEditable="False" />

            <TextBlock Text="{Binding Path=Data.CenterBottomParameterDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible="True" Cursor="Hand"
                       Foreground="{Binding Path=Data.ForeColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={converters:MediaColorToBrushConverter}}"
                       TextWrapping="Wrap"
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Bottom"
                       Margin="0,0,0,0"
                       go:Part.TextEditable="False" />

            <!--#endregion-->
            <ContentControl 
            Width="12" 
            Height="12" 
            Visibility="{Binding Path=Data.HasError, Converter={converters:BoolToVisibilityConverter}}" 
            Style="{StaticResource ErrorNodeStyle}"
            HorizontalAlignment="Right" 
            VerticalAlignment="Top" Margin="0,0,-12,0"/>
            
        </go:NodePanel>
    </go:SpotPanel>
</DataTemplate>

Ugh – there were a lot of undefined references I had to comment out in order to get your template working.

The problem is:

go:Node.PortId="Center"

Did you really mean for the port identifier to be named “Center”? I don’t know what your link data source has in it, but I’m guessing it doesn’t have each link’s connected ports to have the identifier “Center”. If the data don’t specify port identifiers, or if they specify an identifier for which no port exists, it defaults to the port with the default identifier, the empty string “”.

So the above attribute should probably be:

go:Node.PortId=""

Thank you Walter. - Now it is working as expected.