SubGraphs?

Hello.

I have question about the Sub Graphs in your Framework. First of all let me introduce you to the problem I'm facing. I have the task to implement a CASE application for a new diagram notation called ProcGraph that our Institute has developed. The notation allows to model groups of states (super states), that hold states (sub states) and allow that some of the sub states can be contained within two different super states so there can exist a union of two different super states. Here is an example, which I have to be able to model in the tool I'm developing:



The super states Operating and Non drying are forming a union which holds the sub states Starting.Init and Washing.

I want to model both the super states as collapsible Sub Graphs, that would both have to hold Starting.Init and Washing, so that this two sub states cannot be dragged out of the union region. And that a collapsed super state is half within the the other super state and half out.

Can this somehow be accomplished in your framework??? If you have any ideas that would be great and we would than buy your framework.

Thank you a lot.

Toma¾ L. @ E2 @ "Jo¾ef Stefan" Institute

That’s interesting. The part hierarchy in GoDiagram is tree structured–i.e. each GoObject can have at most one Parent GoGroup. So one cannot naturally use GoSubGraphs in the way you want.
However, I bet there is another way of implementing what you want. I’ll try to work on this later today.
Oh, another point – I assume you understand that it isn’t possible in the general case for collapsed subgraphs to be half inside the other subgraph(s) and half outside when there’s three or more subgraphs sharing the same nodes. Even if there’s at most two subgraphs parenting each node, it still isn’t possible when a collapsed subgraph shares nodes with more than one subgraph. At the least, a collapsed subgraph might not be any smaller than it was when expanded.
Finally, what should happen when the user tries to copy or delete a subgraph?

Try this:
[Serializable]
public class StateNode : GoBasicNode {
public StateNode() {
Init();
}
public StateNode(GoDocument doc, String name, float x, float y) { // for coding convenience
Init();
this.Text = name;
this.Center = new PointF(x, y);
doc.Add(this);
}
private void Init() {
this.LabelSpot = Middle;
}
protected override GoPort CreatePort() {
// use a GoBoxPort instead of a regular GoPort, to get the links
// spread out evenly along the near sides
GoBoxPort p = new GoBoxPort();
p.Style = GoPortStyle.Rectangle;
p.Brush = null;
p.Pen = Pens.Black;
p.LinkPointsSpread = true;
return p;
}
protected override GoShape CreateShape(GoPort p) {
// use a rectangle instead of the default ellipse shape
GoRectangle r = new GoRectangle();
r.Selectable = false;
r.Resizable = false;
r.Reshapable = false;
r.Brush = Brushes.White;
r.Pen = Pens.Black;
return r;
}
}
[Serializable]
public class StateLink : GoLink {
public StateLink() {
Init();
}
public StateLink(GoDocument doc, GoPort a, GoPort b, bool filled) { // for coding convenience
Init();
this.FromPort = a;
this.ToPort = b;
this.ToArrowFilled = filled;
doc.Add(this);
}
private void Init() {
this.Relinkable = false;
this.Orthogonal = true;
this.ToArrow = true;
this.ToArrowShaftLength = this.ToArrowLength;
}
}
[Serializable]
public class SuperState : GoNode {
public SuperState() {
Init();
}
public SuperState(GoDocument doc, String name) { // for coding convenience
Init();
this.Text = name;
doc.Layers.Bottom.Add(this);
}
private void Init() {
this.Resizable = false; // don’t let users resize!
this.Reshapable = false;
GoBoxPort p = new GoBoxPort(); // a port for the SuperState as a whole
p.LinkPointsSpread = true;
p.Style = GoPortStyle.None; // not drawn
p.IsValidFrom = false; // don’t let users interactively link from or to it
p.IsValidTo = false;
this.Port = p;
GoText t = new GoText();
t.Selectable = false;
this.Label = t;
}
protected override void CopyChildren(GoGroup newgroup, GoCopyDictionary env) {
base.CopyChildren(newgroup, env);
// update the fields of the copied object
SuperState newobj = (SuperState)newgroup;
newobj.myPort = env[myPort] as GoPort;
newobj.myLabel = env[myLabel] as GoText;
}
public override void Remove(GoObject obj) {
base.Remove(obj);
// update the fields, if needed
if (myPort == obj)
myPort = null;
else if (myLabel == obj)
myLabel = null;
}
public override GoText Label {
get { return myLabel; }
set {
GoText old = myLabel;
if (old != value) {
if (old != null) Remove(old);
myLabel = value;
if (value != null) Add(value);
}
}
}
public GoPort Port {
get { return myPort; }
set {
GoPort old = myPort;
if (old != value) {
if (old != null) Remove(old);
myPort = value;
if (value != null) Add(value);
}
}
}
protected override RectangleF ComputeBounds() {
// the Bounds of this node is just the union of the Bounds of the Items
RectangleF r = GoDocument.ComputeBounds(myItems, null);
r.Inflate(15, 15);
return r;
}
public override RectangleF ExpandPaintBounds(RectangleF rect, GoView view) {
RectangleF r = rect;
// account for the width of the Pen
r.Inflate(1, 1);
return r;
}
public override void Paint(Graphics g, GoView view) {
base.Paint(g, view);
if (!myItems.IsEmpty) {
// draw an unfilled rectangle around the Items
RectangleF r = ComputeBounds();
// maybe you’ll want to parameterize the Pen and the Brush
GoShape.DrawRectangle(g, view, Pens.DarkBlue, null, r.X, r.Y, r.Width, r.Height);
}
}
protected override void MoveChildren(RectangleF old) {
// use the Initializing flag to disable the Update method below
bool oldInit = this.Initializing;
this.Initializing = true;
// move the Port and the Label
base.MoveChildren(old);
// also move all the Items, even though they do not belong to this node
RectangleF b = this.Bounds;
float dx = b.X - old.X;
float dy = b.Y - old.Y;
foreach (GoObject obj in myItems) {
obj.Position = new PointF(obj.Left + dx, obj.Top + dy);
}
this.Initializing = oldInit;
}
public override void LayoutChildren(GoObject childchanged) {
RectangleF r = this.Bounds;
// the Port should have the same Bounds as the whole node
if (myPort != null) myPort.Bounds = r;
// the Label is positioned at the top-left corner, shifted a little to the right for readability
if (myLabel != null) myLabel.SetSpotLocation(TopLeft, new PointF(r.X+3, r.Y));
}
private void Update() {
if (this.Initializing) return;
// have the node recompute its bounds by calling ComputeBounds and then setting its Bounds
this.InvalidBounds = true;
// then position the Port and Label appropriately
LayoutChildren(null);
}
protected override void OnObservedChanged(GoObject observed, int subhint, int oldI, object oldVal, RectangleF oldRect, int newI, object newVal, RectangleF newRect) {
base.OnObservedChanged(observed, subhint, oldI, oldVal, oldRect, newI, newVal, newRect);
// Has one of the Items changed position or size? If so, call Update.
if (subhint == GoObject.ChangedBounds) Update();
}
public void AddItem(GoObject obj) {
if (obj == null) return;
if (!myItems.Contains(obj)) {
myItems.Add(obj);
obj.AddObserver(this);
Update(); // recalculate the Bounds
}
}
public void RemoveItem(GoObject obj) {
if (obj == null) return;
if (myItems.Contains(obj)) {
myItems.Remove(obj);
obj.RemoveObserver(this);
Update(); // recalculate the Bounds
}
}
public bool ContainsItem(GoObject obj) {
return myItems.Contains(obj);
}
public int ItemCount {
get { return myItems.Count; }
}
public GoCollectionEnumerator GetItemEnumerator() {
return myItems.GetEnumerator();
}
private GoCollection myItems = new GoCollection(); // these GoObjects are NOT owned by this node
private GoText myLabel;
private GoPort myPort;
}

Initialize your document by:
// create a separate layer, behind the default one, to hold all the SuperStates
doc.Layers.CreateNewLayerBefore(null).Identifier = “SuperStates layer”;
// create all the SuperStates
SuperState operating = new SuperState(doc, “Operating”);
SuperState drying = new SuperState(doc, “Drying”);
SuperState nondrying = new SuperState(doc, “Non drying”);
// create all the StateNodes and StateLinks
StateNode sn1 = new StateNode(doc, “Starting.End”, 100, 100);
StateNode sn2 = new StateNode(doc, “Running”, 250, 100);
StateLink link12 = new StateLink(doc, sn1.Port, sn2.Port, false);

StateNode sn3 = new StateNode(doc, “Starting.Init”, 100, 200);
StateNode sn4 = new StateNode(doc, “Washing”, 250, 200);
StateLink link31 = new StateLink(doc, sn3.Port, sn1.Port, false);
StateLink link42 = new StateLink(doc, sn4.Port, sn2.Port, true);
StateLink linkd4 = new StateLink(doc, drying.Port, sn4.Port, true);

StateNode sn5 = new StateNode(doc, “Stopped”, 100, 300);
StateNode sn6 = new StateNode(doc, “Stopping”, 250, 300);
StateLink link53 = new StateLink(doc, sn5.Port, sn3.Port, true);
StateLink link65 = new StateLink(doc, sn6.Port, sn5.Port, false);
StateLink linko6 = new StateLink(doc, operating.Port, sn6.Port, true);

// add StateNodes and StateLinks to the SuperStates, as desired
operating.AddItem(drying);
operating.AddItem(sn3);
operating.AddItem(sn4);
operating.AddItem(link31);
operating.AddItem(link42);
operating.AddItem(linkd4);

drying.AddItem(sn1);
drying.AddItem(sn2);
drying.AddItem(link12);

nondrying.AddItem(sn3); // note that SN3 and SN4 are shared by OPERATING and by NONDRYING
nondrying.AddItem(sn4);
nondrying.AddItem(sn5);
nondrying.AddItem(sn6);
nondrying.AddItem(link53);
nondrying.AddItem(link65);
Here’s what I get:

I really can’t figure out what collapse/expand should do, so I haven’t implemented that. That can be done by adding a GoCollapsibleHandle to the SuperState node (just like the Label) and having the SuperState node implement the IGoCollapsible interface.

I have updated the above code to add comments and allow the SuperStates to be selectable, and thus movable and deletable. I have also added constructors to make the graph-implementation code more compact, as you can see above.
You also need to implement a GoDocument.Changed event handler to detect whenever any object is removed from the document. That’s needed so that you can run over all of the SuperStates to remove any references to the deleted object, by calling SuperState.RemoveItem.

First of all thank you very much for your afford Walter.<?:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

The code you posted was pretty useful and it looks like your product is the most suitable for our product. Last week we had a meeting where we discussed the implementation problems of the ProcGraph notation. And so we changed a few things, maybe it would be good if I do a brief introduction to the ProcGraph notation. It consists of three types of diagrams:

1. Procedural Control Entities Dependencies Diagram (PCEDD), which shows the dependencies between Procedural Control Entities (PCEs) at the highest level, here is an example:

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

These entities can be decomposed in subentities, which can be seen in this example (EPT consists of the subentities; EPT.Core, Shaking & Dispergation):

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

Every entity (the subentities also) has a state-transition diagram like diagram called <?:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />Procedural Control Entities State transition Diagram (PCESTD).

2. PCESTDs show the states of a single PCE (but don’t show the dependencies with other PCEs). This is the type of diagram I first posted to you in this thread. Here was the biggest problem of the previous notation, which allowed that the super states could have intersections, like in this example:

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

But after a longer discussion we had realized, that we really don’t need intersections of super states in this type of diagram, so the previous example has changed to this:

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

3. Now there is a third diagram type, the Procedural Control Entities Dependencies State Transition Diagram (PCEDSTD), which shows the PCESTDs of two PCEs and defines the dependencies between the states of these two PCEs. Here are two examples, first EPT.Core versus SCT:

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

Secondly EPT.Core versus Shaking:

![](upload://93YzZ6F884ZJQiTVhwYR7i4TEMG.jpeg)

Now here comes in handy the Non drying rectangle which really isn’t a state of a PCE (in this case EPT.Core) but is used only to show the dependency of a multiple states and represent this with a single relationship, which is show EPT.Core versus SCT. If we hadn’t this feature we would have to draw 4 relations from Starting.Init, Washing, Stopped & Stopping to the relation between states Running and Stopping of the SCT PCE. In the second example EPT.Core versus Shaking there is no Non drying rectangle, because we don’t need it.

This Non drying rectangle would have to be implemented so that it would adapt to any of the four states inside it, it shall never let one of these states outside. So in the start we would select the states that would have to be grouped, and then make a mouse right click and choose the appropriate function from the mouse context menu. I see you have implemented a similar thing in your example; I support this idea that there would be a new layer for this groups. Maybe you can post the last modified example, which hasn’t landed on the forum.

I have another question, could all this elements show in ProcGraph be represented with the GoBoxNode, because we need dynamic ports, where relations do not intersect on the edges of rectangles. I’m trying to implement this, so that I can model the diagram that is shown the second image in this post. But I simply cannot shape the box the way I want. I want it to surround the round rectangle but it just won’t react neither to the Bounds, the Location are the Size properties. What should I do? Here is the code:

namespace IJS_ProcGraph_Tool.ProcGraphElements

{

class Entity : GoBoxNode

{

public Entity (int x, int y) : base()

{

//this.Location = new PointF((float)x, (float)y);

//this.Size = new SizeF(400f, 200f);

GoRoundedRectangle myRectangle = new GoRoundedRectangle();

myRectangle.Size = new Size(100, 50);

myRectangle.Center = new PointF((float)x, (float)y);

myRectangle.Brush = Brushes.CadetBlue;

myRectangle.Pen = Pens.Black;

this.Bounds = myRectangle.Bounds;

this.Add(myRectangle);

}

}

}

Regards,

Tomaz

I had updated the code in my post, rather than posting again with mostly the same code, so all the code is the latest I had worked on.
GoBoxNode, and in fact many of the predefined node classes, have the shape automatically resized to fit around the main object. For GoTextNode that’s a GoText label. For GoBoxNode that’s the Body object, which can be anything, including a GoGroup of various objects.
The expectation of having orthogonal links come in or go out of a port, perhaps spread out along the side, is implemented by GoBoxPort. GoBoxNode certainly uses a GoBoxPort, but you’ll note that the custom GoNode I defineds, StateNode and SuperState, also uses a GoBoxPort to get that behavior.
Adding a GoRectangle to a GoBoxNode is probably useless because GoBoxNode.LayoutChildren doesn’t know about that extra object, so it ignores it. That means the rectangle’s Bounds aren’t modified as the rest of the child objects of the GoBoxNode are moved or resized.

How can I get rid of the ugly grey border of the BoxNode, around my round rectangle which I asociated with the body property of the boxNode? I can’t find the right property!

  • I can’t get to display the lable. Wierd.
    i tried:
    GoText label = new GoText();
    label.Selectable = false;
    label.AutoResizes = false;
    label.StringTrimming = StringTrimming.EllipsisWord;
    label.Text = “Welcome”;
    label.Bounds = new RectangleF(3, 3, 50, 50);
    this.Add(label);

    and
    //we display a dialog for the user
    GoText myLabel = new GoText();
    myLabel.Text = nameInput();
    myEntity.Label = myLabel;

Non works, so that I can see the label

To remove the gray that is filling the GoBoxNode.Port:
GoBoxNode.Port.Brush = null
Perhaps the text of the label is not visible because you explicitly made the label too small. Do you really need to set GoText.AutoResizes false?

Hey there, thanks for your reply Walter.<?:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” /><o:p></o:p>
I managed to solve all the previous problems. But now I’m facing another problem, I need to use the GoView.PickObject Method.<o:p></o:p>
That’s what I’m doing here:<o:p></o:p>
Entity clickedEntity = this.View.PickObject(true, false, this.LastInput.DocPoint, false) as Entity;<o:p></o:p>
But the code will always return me null, no matter what the input Boolean parameters are. I managed to become an instance of GoRoundedRectangle, when I casted to GoDiagram. But my Entity class inherits from a BoxNode and I can’t get a reference to that out of the PickObject method.<o:p></o:p>
Here is the code of the Entity class (hope this will help):<o:p></o:p>

namespace IJS_ProcGraph_Tool.ProcGraphElements

{

[Serializable]

class Entity : GoBoxNode

{

public Entity (int x, int y, string name) : base()

{

//we create a group

GoGroup myEntity = new GoGroup();

//we create the basic shape of the entitiy

GoRoundedRectangle myRectangle = new GoRoundedRectangle();

myRectangle.Size = new Size(100, 50);

myRectangle.Brush = null;

myRectangle.Pen = Pens.Black;

myRectangle.Selectable = false;

myRectangle.DragsNode = true;

//we create a Label object

GoBasicNode myLabel = new GoBasicNode();

myLabel.Bounds = myRectangle.Bounds;

myLabel.LabelSpot = Middle;

myLabel.Brush = Brushes.Transparent;

myLabel.Pen = Pens.Transparent;

myLabel.Selectable = false;

myLabel.DragsNode = true;

//we initialize the name of the Entitiy that will be displayed

GoText label = new GoText();

label.Selectable = false;

label.AutoResizes = true;

label.StringTrimming = StringTrimming.EllipsisWord;

label.Text = name;

myLabel.Label = label;

//we add this basic shapes to the group

myEntity.Add(myRectangle);

myEntity.Add(myLabel);

//we initalize this Box node

this.Location = new PointF(x, y);

this.Body = myEntity; //we tell that the group is the body of the box node

this.Port.Brush = null;

this.PortBorderMargin = new SizeF(0,0);

}

}

} I really don't know what to do. I need this functionality because I'm implementing a custom linking tool, which is vital for our project. Have a nice day! Tomaz

It’s likely that at any point you will be picking a child object of the group (i.e. node) that you care about. Sometimes you want to know the particular object, which might be a GoText label or a GoPort or some GoShape. It appears that in this case you don’t and you just want to know which node is at the given point.
So before you assume that the object you pick is an “Entity”, you should navigate from what might be a child object to what is likely the parent node object. You can just use the GoObject.ParentNode property:
GoObject obj = this.View.PickObject(true, false, this.LastInput.DocPoint, true);
if (obj != null) {
Entity ent = obj.ParentNode as Entity;
if (ent != null) { . . . }
}
You’ll note that many of the examples use GoObject.ParentNode or GoObject.TopLevelObject in this situation.

Ok that helped me. (But the last bool must be set to true, or else it wont detect none object) Thanks a lot!<?:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

No as I said I’m trying to modify a link drawing tool, I’m doing this on the base of the code that is supplied in the OrgCharter sample in the RelationshipTool class.

But I’m using BoxNodes, so the temporary links would have to be connected to the proper dynamic spot at the edge of the node, that functionality is enabled with the BocNodePort, but in my Tool I don’t know how I can make the first port of the line that dynamic spot, because the line is always drawn from the center and it snaps always to the left upper corner of the next entity.

How can I achieve the desired behavior?

Here is the code:

private void makeTemporaryDependencie()

{

//create a new link

if (myLink == null)

{

GoLink tempDependencie = new GoLink();

tempDependencie.Style = GoStrokeStyle.Line;

//DRAWING STYLE:

//orthogonal drawing

if (MainWindow.App.DrawingOfDependencies == DependenciesDrawingStyle.Orthogonal)

tempDependencie.Orthogonal = true;

//normal drawing

else

tempDependencie.Orthogonal = false;

//ARROWS

if (MainWindow.App.StyleOfArrow == ArrowStyle.OnEnd)

tempDependencie.ToArrow = true;

else if (MainWindow.App.StyleOfArrow == ArrowStyle.OnStart)

tempDependencie.FromArrow = true;

else //Both sides

{

tempDependencie.ToArrow = true;

tempDependencie.FromArrow = true;

}

GoPort fromPort = new GoPort();

//fromPort.Style = GoPortStyle.Rectangle;

fromPort.FromSpot = startEntity.Port.FromSpot;

fromPort.Bounds = startEntity.Port.Bounds;

tempDependencie.FromPort = fromPort;

GoPort toPort = new GoPort();

toPort.Size = new System.Drawing.SizeF(1, 1);

toPort.Position = this.LastInput.DocPoint;

toPort.ToSpot = GoObject.MiddleTop;

tempDependencie.ToPort = toPort;

//the link is temporary a view object

this.View.Layers.Default.Add(tempDependencie);

myLink = tempDependencie;

}

}

------------------------------------------------------------ -----------

public override void DoMouseDown()

{

Entity clickedEntity = null;

//we detect any kind of child object

GoObject obj = this.View.PickObject(true, false, this.LastInput.DocPoint, false);

if (obj != null)

//if the child is a part of a Entitiy parent, than give me the reference to it

clickedEntity = obj.ParentNode as Entity;

//when we have no start entity picked out

if (startEntity == null)

{

//the point of mouse click is NOT a part an entity

if (clickedEntity == null)

//we do nothing

return;

else //the point of mouse click IS a part an entity

{

startEntity = clickedEntity;

//TODO: notify the user through the status bar

makeTemporaryDependencie(); //we draw the temporay link

}

}

//when the start entity is picked and we search for the end entitiy

else

{

if (canLink(startEntity, clickedEntity))

{

//clear up the temporary link

View.Layers.Default.Remove(myLink);

myLink = null;

//create the link in the document

IGoLink finalDependencie = View.CreateLink(startEntity.Port, clickedEntity.Port);

//if the creation sucseeded

if (finalDependencie != null)

{

TransactionResult = "New dependencie";

View.RaiseLinkCreated(finalDependencie.GoObject);

View.Selection.Select(finalDependencie.GoObject);

}

//end of the user interaction

StopTool();

}

}

}

public override void DoMouseMove()

{

if (myLink != null && myLink.ToPort != null)

{

GoPort endPort = myLink.ToPort as GoPort;

//only if the end port exists

if (endPort != null)

{

endPort.Position = LastInput.DocPoint;

Entity endEntity = null;

//we detect any kind of child object

GoObject obj = this.View.PickObject(true, false, this.LastInput.DocPoint, false);

if (obj != null)

//if the child is a part of a Entitiy parent, than give me the reference to it

endEntity = obj.ParentNode as Entity;

if (canLink(startEntity, endEntity))

{

endPort.Position = endEntity.Port.Position;

endPort.ToSpot = endEntity.Port.ToSpot;

}

}

}

}

I’m facing yet another problem, how can I adjust the shape at the final drawn link? The temporary link has the right shape and everything. But the final link is nor orthogonal nor has an arrow. Because I create it so I don’t know how to manipulate it:

//clear up the temporary link

View.Layers.Default.Remove(myLink);

myLink = null;

//create the link in the document

IGoLink finalDependencie = View.CreateLink(startEntity.Port, clickedEntity.Port);

//if the creation sucseeded

if (finalDependencie != null)

{

TransactionResult = "New dependencie";

View.RaiseLinkCreated(finalDependencie.GoObject);

View.Selection.Select(finalDependencie.GoObject);

}

//end of the user interaction

StopTool();

Thank you

It sounds like you shouldn’t be defining a complete tool (inheriting from GoTool) but a special case of GoToolLinkingNew. That should solve most of the kinds of problems you are talking about, since that class already handles all those cases.
There are some examples that show inheriting from GoToolLinkingNew (and perhaps GoToolRelinking).
But what is it that you are trying to do that is different from the default behavior of GoToolLinkingNew?

The desired behavior I’m looking for is similar to those in the examples: FlowCharter, OrgCharter and MovableLinkApp. The first two linking tools inherit form GoTool and the last inherits from the GoToolLinkingNew. <?:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

But the last example doesn't work form my custom node: Entity. The problem is that it doesn’t detect the entity when I click on it and there is no example for doing this with the GoToolLinkingNew only with the GoTool. But in GoTool there is no PickNearestPort(PointF dc) method which I could override. (so that, I could have the proper port finding behavior in the class that inherits from GoTool). I think the problem is the lack of detailed documentation for the default tools so we must bother you always.

I would need to detect my nodes (Entity) in the tool that inherits form GoToolLinkingNew, but I don’t know enough about your GoDiagram framework (yet) to do this.

You still haven’t given me a hint how to control the look (arrows, line style…) of the final link that is created.

Sorry for the dumb questions.

Both FlowCharter and OrgCharter have multiple ways of drawing links. They both make use of the standard linking tools (GoToolLinkingNew and GoToolRelinking, which both inherit from GoToolLinking). And they both define a separate tool for modal linking (invoked by a menu command) that requires two separate mouse clicks. You seem to want the second kind of tool – a modal tool requiring two clicks. In this case what you are doing by copying the RelationshipTool class seems a reasonable thing to do.
RelationshipTool defines a FindNearestPort method so that it can choose the port that is closest to where the user clicked. However, a GoBoxNode only has a single Port, so you don’t really need to worry about that.
Regarding the appearance of the link, since the tool calls GoView.CreateLink, you just need to set the properties of GoView.NewGoLink. Or just set GoView.NewLinkPrototype to an instance of your own IGoLink-implementing class.
Perhaps it’s worth inheriting from GoToolLinking, just so you can use the StartNewLink method. That will make sure the temporary link is just like the final one.

<span style=“font-size: 12pt; font-family: “Times New Roman”;” lang=“EN-US”>Thanks Walter!

In this time I solved a lot of problems with GoDiagram and the possibility
grows that we will use it for our project.

However I’m facing some new problems, maybe you could help me to solve them or
give me a push in the right direction.

  1. I’m using the grid in my GoView and the performance of the application has dramatically
    fallen. Even the GoRubberBand is moving very very slow. Is this normal?
    Here are the settings of the grid:

<span style=“font-size: 12pt; font-family: “Times New Roman”;” lang=“EN-US”>2. I’m using a GoSubGraph for my modeling and it looks cool. But every time I
collapse it and expand it back the links get messed up and that’s a major
flaw. Here is an example:

a) I model my diagram:

<span style=“font-size: 12pt; font-family: “Times New Roman”;” lang=“EN-US”>b)I collapse it:

<span style=“font-size: 12pt; font-family: “Times New Roman”;” lang=“EN-US”>c) and then expand it again
and this comes out :

That's some work for the user to fix that. Is there any way how the links could be saved and loaded, when I collapse/expand the subgraph?

  1. I want to trace the changes of the Selection property so I can enable some
    align buttons. I’m doing this with the SelectionFinished event and it works
    fine if I select my entities with the GoRubberBand, but when I do it with a mouse
    click + CTRL button it doesn’t function. Is there an alternative event which
    happens in both cases???

  2. Sometimes I have the problem in connecting my entities with
    links, they just won’t snap :/ is there any maximum number of links that
    can be connected to a BoxPort??? Or else there is a bug in my custom
    linking tool.

Thanks for your help.<o:p></o:p>

  1. Dash style lines draw a lot slower than solid lines.
  2. You can override GoSubGraph.SaveChildBounds to call the base method and then enumerate the ExternalLinks and save their stroke points in the SavedPaths hash table.
    Then for expansion override FinishExpand to restore the stroke points of all the external links.
  3. You need to implement ObjectGotSelection and ObjectLostSelection event handlers. SelectionFinished and SelectionStarting are only raised when there are potentially many changes to the GoView.Selection happening at once, and are typically just used to optimize the behavior of the Object[Got/Lost]Selection event handlers.
  4. There’s no limit on the number of links at a port, by default. (Search for “MaxLinks” in the samples if you did want such a limit.) That seems an unlikely bug that you would have introduced, so maybe there’s something else wrong.
    Have you considered the various GoPort properties dealing with “Valid” links? Or overridden either GoPort.IsValidLink or GoToolLinking.IsValidLink?

Thanks I solved everything except problem 4) !

I debugged the custom linking tool I have, which inherits from GoTool.

The problem is in this function in the call of IsValidLink() it just returns false, but I don’t know why. Here:

    private bool canLink(Entity start, Entity end)
    {
          if (start == null) 
              return false;
          if (start.Port == null)
              return false;
          if (end == null)
              return false;
          if (end.Port == null)
              return false;
          return start.Port.IsValidLink(end.Port);
    }

…any suggestions what I can do? I use this function on the same entity 6 times before and it works after that I just won’t. That’s an odd bug and because I can’t debug the IsValidLink() method, I just don’t know what to do.

Thanks.

The same entity 6 times from the same other entity? Or from 6 other entities?
Did you check the GoPort properties with “Valid” in their names? Have you set GoDocument.ValidCycle to a value that might affect what you are doing?

Hello I have checked all this Properties, but found nothing.

Here is an example where I have problems:

I allways draw links to the EPT.Core entity and everything works fine utill I try to draw this link:

It won’t pass the (returns false)-> start.Port.IsValidLink(end.Port)
So it won’t dock! Here are the properties of of both ports:

EPT.Core

RVD

Than I try it the other way to draw the link form EPT.Core to RVD:

And it docks! Here are the Ports in this specific situation:

EPT.Core

RVD

And I really don’t have a clue why it doesn’t work!? Can you help? The problem is that I don’t know what happens in the IsValidLink() method.

Thank you.