Problem with GoLabelledLinks in a group

I’m trying to derive a class from GoTextNode that will be used on a flowcharting style diagram. This node is basically a decision step but has the following requirements: Left, Top and Right ports are inputs, the bottom port is an output but cannot be linked to directly by the user. Instead the user will specify how many outputs the decision node can allow and then I will programatically create that many “port nodes” underneath the main node and create a link from the bottom port of the decision node to each of these “port nodes”. The user will then be allowed to create one link from each of the “port nodes” to whatever destination they choose. The user will not be allowed to select, move or delete the links I create or the “port nodes”.



This is a simple line diagram of what i’m trying to create (for three allowed outputs):



o

o o

o o

o o

o

|

----±—

| | |

* * *







The three *'s represent the “port nodes”. The “port nodes” are derived from GoTextNode and are added to the main decision node object. The links between the decision node and the “port nodes” are GoLabeledLink and are also added to the main decision node object. The individual items within the group are not selectable, only the group as a whole can be selected.



When I add one of these things to my GoView (by dragging from the pallette) the item is initially drawn OK. However if I move the group (using drag and drop in this case) the links between the main node and each of the “port nodes” do not move along with the rest of the group. From what I can tell the links move in the same direction as the rest of the group, but they seem to move double the actual distance moved by the rest of the group. Even though the links move double the distance, they do actually use the groups original position for their point of origin for the move operation.



I’ve tried everything to figure this out but after 2 days of messing around I am now totally out of ideas.



Has anyone else had this problem?



Regards

Ian

bit more info on this…



If i comment out the code that adds the link to main decision node instance then the link correctly follows the group when the group is moved. However this introduces another problem as the links are then invisible and can only be seen when the group is being dragged during the move operation.



I know this is going to turn out to be something real simple!

That’s odd, because when I take a few nodes and links and make a GoNode or a GoSubGraph out of them, using GoGroup.AddCollection, the resulting node moves around just fine. There isn’t any double-moving of the links.
I would think that double-moving might happen if both the link and the parent group were selected, and if the standard dragging tool wasn’t smart enough to recognize that a selected object was part of another selected object. But you said that those interior links are not Selectable, and GoToolDragging is smart enough to avoid double moves anyway. Perhaps you have overridden GoGroup.MoveChildren and/or GoGroup.LayoutChildren to cause such behavior, but that’s not likely either.
Did you confirm that the structure of your composite node is exactly what you think it should be? That is, you have a GoNode that contains the main GoTextNode plus three GoLinks and three GoTextNodes?



That isn't quite what I have here. In my case the main decision node (i.e the GoTextNode derived instance) is the group node (i.e thats the object that is added to the document). There is no higher-level GoNode holding everything together.

What i have is this...

DecisionNode derives from GoTextNode.
DecisionNodePort derives from GoTextNode.
DecisionPortLink derives from GoLabeledLink.

For a decision node with N port nodes, the DecisionNode constructor creates N DecisionNodePort instances, N DecisionPortLink instances, and N GoText instances (the GoText's are used for the link labels).

The link's FromPort, ToPort, and ToLabel properties are asssigned. The DecisionNodePort instance and the DecisionPortLink instance is then added using this.Add(). This is then repeated for the N required exit ports.

I'll change the code to use a GoNode to contain everything and see if that has any effect.

As an unrelated issue, I would hazard a guess that you don’t really need to have four GoPorts on each of your DecisionNodePorts.
Instead of using a GoTextNode for those “ports”, I would use a GoBasicNode, with LabelSpot = Middle, so there would just be a single GoPort there. That’s assuming you want both a text Label and a Shape at that port.

Adding a top level GoNode didn’t make any difference. I can easily repro what is happening in the FlowCharter sample by adding the following code to GraphNode.cs



In the switch statement in the GraphNode .ctor add the line



this.CreateExitPorts(4);



at the end of the GraphNodeKind.Decision case.



Then add the method below



<br /> private void CreateExitPorts(int portCount) <br /> { <br /> float leftPos, spacing, bottom; <br /> spacing = 30; <br /> <br /> // Work out the left Pos of the first exit port <br /> leftPos = this.Bounds.X + (this.Bounds.Width / 2); <br /> leftPos = leftPos - (((portCount - 1) * spacing) / 2); <br /> bottom = this.Bounds.Bottom; <br /> <br /> for (int i = 0; i < portCount; i++) <br /> { <br /> GoTextNode node = new GoTextNode(); <br /> node.Location = new PointF(leftPos, bottom + 30); <br /> <br /> this.Add(node); <br /> <br /> GoLabeledLink link = new GoLabeledLink(); <br /> link.FromPort = this.BottomPort; <br /> link.ToPort = node.TopPort; <br /> link.Deletable = false; <br /> link.Curviness = 0; <br /> link.Movable = false; <br /> link.Reshapable = false; <br /> link.Relinkable = false; <br /> link.Resizable = false; <br /> link.Style = GoStrokeStyle.Line; <br /> link.Orthogonal = true; <br /> <br /> GoText t = new GoText(); <br /> t.Text = string.Format("Link {0}", i); <br /> t.Selectable = false; <br /> t.Editable = false; <br /> link.ToLabel = t; <br /> <br /> this.Add(link); <br /> <br /> leftPos += spacing; <br /> } <br /> } <br /> <br /> <br /> <br />



I didn't bother describing the whole thing because it gets too complicated. Soooo...

DecisionPortNode overrides CreatePort and returns null for all ports except TopCenter and BottomCenter. The top port is one that cannot be linked to except through code. The bottom port is one of my own classes that overrides CanLinkFrom, CanLinkTo and IsValidLink so that I can validate when links are created by the user.

The problem is that the implementation of GoGroup.MoveChildren produces different results depending on the order in which the child objects are moved. The particular issue with links is that as either connected node is moved, the path and thus the Bounds of the link are recalculated by GoLink.CalculateStroke(). Then the link itself is moved by MoveChildren, causing an additional move.
If instead all links are moved first, and then all non-links, this problem goes away. In fact, since GoSubGraph does handle links within the group, you can just use the definition of GoSubGraph.MoveChildren:
protected override void MoveChildren(RectangleF prevRect) {
float dX = this.Left - prevRect.X;
float dY = this.Top - prevRect.Y;
foreach (GoObject obj in this) {
if (obj is IGoLink) {
RectangleF r = obj.Bounds;
obj.Bounds = new RectangleF(r.X + dX, r.Y + dY, r.Width, r.Height);
}
}
foreach (GoObject obj in this) {
if (!(obj is IGoLink)) {
RectangleF r = obj.Bounds;
obj.Bounds = new RectangleF(r.X + dX, r.Y + dY, r.Width, r.Height);
}
}
}
We’ll need to consider if this definition needs to be promoted into GoNode or GoGroup.



Walter, you da man!

Thanks it works a treat