PortKey different from String

Hi, I’m trying to create a GraphLinksModel without using base classes GLMNodeData and GLMLinkData. Here are my node, link and port data.

And here is the kind of diagram I’m trying to do:

Here is the implementation of the model:

Public Class CustomModel
Inherits Northwoods.GoXam.Model.GraphLinksModel(Of INode, Guid, Connector, Stream)


Public Sub New()
MyBase.Modifiable = True
MyBase.NodeKeyPath = “Id”
MyBase.LinkFromParameterPath = “From”
MyBase.LinkToParameterPath = “To”
MyBase.NodesSource = New List(Of INode)
MyBase.LinksSource = New List(Of Stream)
End Sub




Protected Overrides Function InsertLink(fromdata As INode, fromparam As Connector, todata As INode, toparam As Connector) As Stream
If fromdata Is Nothing Then Return Nothing
If todata Is Nothing Then Return Nothing




Dim newStream As Stream = New Stream()
newStream.From = fromparam
newStream.To = toparam


Dim links As List(Of Stream) = TryCast(LinksSource, List(Of Stream))
links.Add(newStream)


Return newStream


End Function


End Class




And finally, the Node DataTemplate:


<go:SpotPanel go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">

            <go:SpotPanel.ToolTip>
                <StackPanel>
                    <TextBlock Text="{Binding Path=Data}" />
                    <TextBlock Text="{Binding Path=Data.Id}"  />
                    <TextBlock Text="{Binding Path=Data.Inlet1Connector.Id}"  />
                    <TextBlock Text="{Binding Path=Data.Inlet2Connector.Id}"  />
                    <TextBlock Text="{Binding Path=Data.OutletConnector.Id}"  />
                </StackPanel>
            </go:SpotPanel.ToolTip>
            
            <go:NodePanel>
                <go:NodeShape Width="50" Height="50" 
                              go:NodePanel.Figure="Gate"
                              Stroke="Black" StrokeThickness="2" Fill="LightSlateGray" />
            </go:NodePanel>
            
            <Rectangle go:SpotPanel.Spot="0 .33" Width="8" Height="8" Fill="Black" 
                       <b>go:Node.PortId="{Binding Path=Data.Inlet1Connector}"</b> go:Node.LinkableTo="True"
                       go:Node.LinkableMaximum="1"
                       go:Node.ToSpot="MiddleLeft" Cursor="Hand" />

            <Rectangle go:SpotPanel.Spot="0 .67" Width="8" Height="8" Fill="Black"
               <b>go:Node.PortId="{Binding Path=Data.Inlet2Connector}"</b> go:Node.LinkableTo="True"
               go:Node.LinkableMaximum="1"
               go:Node.ToSpot="MiddleLeft" Cursor="Hand" />
            
            <Rectangle go:SpotPanel.Spot="MiddleRight" Width="8" Height="8" Fill="Black"
               <b>go:Node.PortId="{Binding Path=Data.OutletConnector}"</b> go:Node.LinkableFrom="True"
               go:Node.FromSpot="MiddleRight" Cursor="Hand" />

        </go:SpotPanel>
    </DataTemplate></font>
But when I'm trying to create a link between to NodePort, I got this exception:

InvalidCastException
Unable to cast object of type ‘System.String’ to type ‘UtilityTest.Connector’.

No idea of what I’m doing wrong?

The problem is that there is a mismatch between the capabilities of GraphLinksModel, which supports a parameterized type for the PortKey, and Diagram/PartManager, which does not – ports are always identified by strings only. So as a practical matter the PortKey must be String.

I suppose we could extend this mechanism by allowing you to provide conversion functions.

See also this post.

Ok, I changed the PortKey to String but now, I have another problem. When I create a link between 2 ports, it passes through the overrides InsertLink method, but the link doesn’t appear in the diagram.

CustomModel:

Public Class CustomModel
Inherits Northwoods.GoXam.Model.GraphLinksModel(Of INode, Guid, String, Stream)


Public Sub New()
MyBase.Modifiable = True
MyBase.NodeKeyPath = “Id”
MyBase.LinkFromParameterPath = “FromToString”
MyBase.LinkToParameterPath = “ToToString”
MyBase.LinkFromPath = “”
MyBase.LinkToPath = “”
MyBase.NodesSource = New List(Of INode)
MyBase.LinksSource = New List(Of Stream)
End Sub




Protected Overrides Function InsertLink(fromdata As INode, fromparam As String, todata As INode, toparam As String) As Stream
If fromdata Is Nothing Then Return Nothing
If todata Is Nothing Then Return Nothing


Dim newStream As Stream = New Stream()
newStream.From = New Guid(fromparam)
newStream.To = New Guid(toparam)


Dim links As List(Of Stream) = TryCast(LinksSource, List(Of Stream))
links.Add(newStream)


Return newStream


End Function


End Class

XAML:


<go:SpotPanel go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">


go:NodePanel
<go:NodeShape Width=“50” Height=“50”
go:NodePanel.Figure=“Gate”
Stroke=“Black” StrokeThickness=“2” Fill=“LightSlateGray” />
</go:NodePanel>

<Rectangle go:SpotPanel.Spot=“0 .33” Width=“8” Height=“8” Fill=“Black”
go:Node.PortId="{Binding Path=Data.Inlet1Connector.Name}" go:Node.LinkableTo=“True”
go:Node.LinkableMaximum=“1”
go:Node.ToSpot=“MiddleLeft” Cursor=“Hand” />


<Rectangle go:SpotPanel.Spot=“0 .67” Width=“8” Height=“8” Fill=“Black”
go:Node.PortId="{Binding Path=Data.Inlet2Connector.Name}" go:Node.LinkableTo=“True”
go:Node.LinkableMaximum=“1”
go:Node.ToSpot=“MiddleLeft” Cursor=“Hand” />

<Rectangle go:SpotPanel.Spot=“MiddleRight” Width=“8” Height=“8” Fill=“Black”
go:Node.PortId="{Binding Path=Data.OutletConnector.Name}" go:Node.LinkableFrom=“True”
go:Node.FromSpot=“MiddleRight” Cursor=“Hand” />


</go:SpotPanel>

You appear to be using a List as the LinksSource and NodesSource collections. Because they do not implement INotifyCollectionChanged, you need to explicitly call model.DoLinkAdded or .DoNodeAdded when you add a link data or a node data to the respective collections. Otherwise GoXam cannot know that the collections have been modified.

That’s why most (all?) of the samples use ObservableCollection – to avoid having to call those model methods. Using collections that implement INotifyCollectionChanged is expected for normal binding and model usage in WPF and Silverlight.

Good point! But even by changing Node and LinkSource to ObservableCollection, I’m still not able to see link in the diagram.

And you have implemented FromToStream and ToToStream as properties of Stream that do what?

And how are the From and To properties declared and what do they do?

FromToString and ToToString are the From and To property of the class Stream but in String format. I call it this way just for a test purpose. In other words, FromToString corresponds to the FromPort identifier and ToToString correspond to the ToPort identifier.

And for your information, Stream class doesn’t inherit from GLMLinkData.

I don't understand why you are setting model.LinkFromPath = "".

Remember that you need to provide both the node's key and the port's key within the node for both ends of each link.

To tell you the truth, it’s the first time that I work with Ports and I’m a little bit confused. I set model.LinkFromParameterPath = “FromToString” because Stream.FromToString corresponds to the FromPort. And I set model.LinkFromPath = “” because I thought that because Stream already knew from where it comes (FromToString), LinkFromPath was not necessary…

Should I add a new property referring to the From and ToNode in the class Stream?

Should I add a new property referring to the From and ToNode in the class Stream?

I assume that the answer to my question is Yes, because I added these property to my Stream class and now it works!!

By the way, how do we do Quotes in this forum?

Use BBcodes, as described in the link below.

Ok. Thank you for your help Walter!