FindNearestGridPoint

OK, so the problem there is just a text pasting side-effect in this forum. Which explains why the text thereafter became italicized.
Just to confirm – do all of the final links in the document get created (and drawn) correctly after a successful linking gesture by the user? And all of the temporary links really do get created, with non-null ports, and added to the view? So the problem really is only with the display of those temporary links?
I hope that canceling the linking operation by hitting ESCape works fine. As does undo/redo.
I just reviewed the changes between 2.1 and 2.4 for the implementations of GoLink and GoPort and GoToolLinking. There really weren’t any meaningful differences in GoLink or GoPort (other than new features and a few bug fixes, which I don’t believe affect you). GoToolLinking was similar, but there were two changes that might be relevant, even though they are basically quite compatible for almost everyone.
The first change, in GoToolLinking.CreateTemporaryLink, I already mentioned: it now makes a copy of GoView.NewLinkPrototype instead of creating an instance of GoView.NewLinkClass. (That’s also why the GoToolLinking.Orthogonal property became obsolete and was removed recently, since the GoLink.Orthogonal property can just be set on the GoView.NewLinkPrototype instead of having to be set later.) But I don’t think that’s the problem.
The second change is in GoToolLinking.CreateTemporaryPort. It used to just create an instance of GoPort and copy over the FromSpot/ToSpot/PortObject/Size properties. But now it creates an internal GoTemporaryPort that overrides all of the properties and methods relevant to the routing of the link, so that it uses the actual target GoPort in the document to get more realistic and dynamic routing information. The overall effect is better, particularly when the user is considering connecting to different kinds of ports.
So the problem is that the override of DoLinking is assuming that the temporary port is just a “plain” GoPort, instead of the smarter GoTemporaryPort. The best solution would be for you to set the GoTemporaryPort.Target property, but since the class is internal, you can’t. But you could override CreateTemporaryPort to be the original definition, and maybe that will work for you:
protected override IGoPort CreateTemporaryPort(IGoPort port, PointF pnt, bool forToPort, bool atEnd) {
GoPort tempPort = new GoPort();
if (port.GoObject is GoPort) {
GoPort origport = (GoPort)port.GoObject;
tempPort.FromSpot = origport.FromSpot;
tempPort.ToSpot = origport.ToSpot;
tempPort.PortObject = origport.PortObject;
tempPort.Size = origport.Size;
} else {
tempPort.FromSpot = GoObject.NoSpot;
tempPort.ToSpot = GoObject.NoSpot;
tempPort.Size = new SizeF();
}
tempPort.Center = pnt; // follows mouse movement
// these ports are added to the view, but are hidden
tempPort.Style = GoPortStyle.None;
this.View.Layers.Default.Add(tempPort);
return tempPort;
}

Now we are getting somewhere! The override of CreateTemporaryPort made the links appear during dragging as they did before. I thought we were done until I realized that your concern about UndoRedo was valid. If I create a link and then undo the operation, the link is still visible on the view (until I refresh the view by changing the zoom? or destroy and rebuild the view it then the link is gone as expected). If I click on the link that was supposed to be removed by the undo operation, I get this error message:
" Selected objects must belong to the view or its document Stack Trace = at Northwoods.Go.GoSelection.Add(GoObject obj)…"
Playing with this some more, I found a related problem that if I create a link, and then immediately select it and then hit the delete key it works as expected. Then if I create another link, select it and hit the delete key, the link does not go away the second time (until I change the zoom or refresh as mentioned above). What would prevent my link from disappearing? Why would changing the zoom correct it?
I stepped through the code and it appears that the code is operating the same the first and second time I delete a link, but the second time, the link does not go away in the view. The problem persists for any new links I draw and try to delete thereafter. It is interesting to know that existing links that already existed and were created in code during construction (not by dragging in this instance) are not effected and can always be deleted correctly.
I verified that the same behavior was occuring before we added the CreateTemporaryPort override.
////////////////////////
Relevant GoQSCToolLinking methods…
///


/// This method handles the mouse up event and ends the linking process.
/// #GO: GoToolLinking::DoMouseUp
///

public override void DoMouseUp ()
{
GoQSCPort portNearest = null;
this.m_GoQSCView.SignalFlow.BeginTransaction(“Add Wire(s)”);
for (int i = 0; i < m_StartPorts.Count; i++)
{
if (i == 0)
portNearest = PickNearestPort (this.LastInput.DocPoint, m_StartPorts[0] as GoQSCPort);
else
{
portNearest = PickNextValidPort (portNearest, m_StartPorts[i] as GoQSCPort );
}
if (portNearest != null)
{
if (this.Forwards)
DoNewLink (m_StartPorts[i] as GoQSCPort, portNearest);
else
DoNewLink (portNearest, m_StartPorts[i] as GoQSCPort);
}
else
{
if (this.Forwards)
DoNoNewLink (m_StartPorts[i] as GoQSCPort, portNearest);
else
DoNoNewLink (portNearest, m_StartPorts[i] as GoQSCPort);
}
}

this.m_GoQSCView.SignalFlow.EndTransaction();
StopTool ();
m_StartPorts.Clear();
}
///


/// Clean up the link state before stopping this tool.
/// #GO: GoToolLinking::DoCancelMouse
///

public override void DoCancelMouse ()
{
for (int i = 0; i < m_StartPorts.Count; i++)
{
if (this.Forwards)
DoNoNewLink (m_StartPorts[i] as GoQSCPort, null);
else
DoNoNewLink (null, m_StartPorts[i] as GoQSCPort);
}
this.View.Cursor = this.View.DefaultCursor;
StopTool ();
}

///


/// Cleaning up from any kind of linking operation involves removing any temporary links
/// and ports from the view.
/// #GO: GoToolLinking::Stop
///

public override void Stop ()
{
this.Forwards = true;
for (int i = 0; i < m_StartPorts.Count; i++)
{
m_StartPorts[i] = null;
// Remove the temporary link
if (m_TmpLinks[i] != null)
{
GoObject obj = m_TmpLinks[i].GoObject;
if (obj != null && obj.IsInView)
obj.Layer.Remove (obj);
}
m_TmpLinks[i] = null;

// Remove the temporary start port
if (m_TmpStartPorts[i] != null)
{
GoObject obj = m_TmpStartPorts[i].GoObject;

if (obj != null && obj.IsInView)
obj.Layer.Remove (obj);
}
m_TmpStartPorts[i] = null;

// Remove the temporary end port
if (m_TmpEndPorts[i] != null)
{
GoObject obj = m_TmpEndPorts[i].GoObject;

if (obj != null && obj.IsInView)
obj.Layer.Remove (obj);
}
m_TmpEndPorts[i] = null;
}
if (this.ValidPortsCache != null)
{
this.ValidPortsCache.Clear ();
}
m_GoQSCView.AutoScrollRegion = m_AutoScrollSize;
}

That error message happens when calling GoSelection.Add with a GoObject that neither belongs to the GoSelection.View nor the GoSelection.View.Document. (I.e. if it isn’t currently part of a GoLayer in either the GoView or in its GoDocument.)
It sounds like you aren’t clearing some state/data, which would cause the different behavior the second time. But I’m just speculating.

It appears that the data is getting cleared correctly because if I kill my goview and reopen it, the links are gone as they should be.
Also they disappear if I zoom which is sort of weird…
Interesting - If I drag my nodes around after the faulty delete, the link does not move with the nodes…it is just a dead link floating there on the view not connected to any nodes

Well, it looks like I have it finally working! Not sure why I had to do this change as we know if was working before without it.
Basically in the OnDocumentChanged event that we hooked to each link I call the following method (when the link was removed of course)
this.m_GoQSCView.Document.Remove(this);
Shoudn’t this always get done automatically when the user deletes a link from the view?
With this change, everything seems to work including the Undo/Redo. Does this make any sense?
Have a good weekend,
Craig

Yes, the user’s deletion of a link causes the link to be removed from the document.
No, that doesn’t make any sense.
OK, as a proof of concept, I figured it might be easiest to just go ahead and implement a simplified version of what you want to do. It all worked the first time (after fixing a couple of typos), including cancelling and undo/redo. No oddities regarding deleted links still being visible because of improper updating.
To simplify things, it always draws only two parallel links. It only works in the Forwards direction. There might be some other assumptions this code makes. It includes two methods that are work-arounds for the issue you uncovered (not being able to manipulate those new GoTemporaryPorts); these two methods can be deleted in the future.
[Serializable]
public class DoubleLinkTool : GoToolLinkingNew {
public DoubleLinkTool(GoView v) : base(v) { }
public IGoPort OriginalStartPort2;
public IGoPort StartPort2; // temp
public IGoPort EndPort2; // temp
public IGoLink Link2; // temp
public IGoPort FindNextPort(IGoPort port1) {
if (port1 == null) return null;
bool found = false;
foreach (IGoPort p in port1.Node.Ports) {
if (found) return p;
if (p == port1) found = true;
}
return null;
}
public override void StartNewLink(IGoPort port, PointF dc) {
base.StartNewLink(port, dc);
//??? assume Forwards
IGoPort port2 = FindNextPort(port);
this.OriginalStartPort2 = port2;
this.StartPort2 = CreateTemporaryPort(port2, port2.GoObject.Center, false, false);
this.EndPort2 = CreateTemporaryPort(port2, dc, true, true);
this.Link2 = CreateTemporaryLink(this.StartPort2, this.EndPort2);
}
public override void DoLinking(PointF dc) {
if (this.EndPort == null) return;
if (this.EndPort.GoObject == null) return;
IGoPort iport = PickNearestPort(dc);
ImitatePort(this.EndPort, iport);
RectangleF newrect;
if (iport != null && iport.GoObject != null) {
newrect = iport.GoObject.Bounds;
} else {
newrect = new RectangleF(dc.X, dc.Y, 0, 0);
}
this.EndPort.GoObject.Bounds = newrect;
iport = FindNextPort(iport);
ImitatePort(this.EndPort2, iport);

if (iport != null && iport.GoObject != null) {
newrect = iport.GoObject.Bounds;
} else {
newrect = new RectangleF(dc.X, dc.Y+15, 0, 0);
}
this.EndPort2.GoObject.Bounds = newrect;
}
public override void DoMouseUp() {
IGoPort port = PickNearestPort(this.LastInput.DocPoint);
if (port != null) {
//??? assume Forwards
DoNewLink(this.OriginalStartPort, port);
DoNewLink(this.OriginalStartPort2, FindNextPort(port));
} else {
IGoPort invalidPort = PickPort(this.LastInput.DocPoint);
//??? assume Forwards
DoNoNewLink(this.OriginalStartPort, invalidPort);
DoNoNewLink(this.OriginalStartPort2, FindNextPort(invalidPort));
}
StopTool();
}
public override void DoCancelMouse() {
//??? assume Forwards
DoNoNewLink(this.StartPort, null);
DoNoNewLink(this.StartPort2, null);
this.View.CursorName = “default”;
StopTool();
}
public override void Stop() {
this.OriginalStartPort2 = null;
// if the link or either temporary port were added to a view, remove it
if (this.Link2 != null) {
GoObject obj = this.Link2.GoObject;
if (obj != null && obj.IsInView)
obj.Remove();
}
this.Link2 = null;
if (this.StartPort2 != null) {
GoObject obj = this.StartPort2.GoObject;
if (obj != null && obj.IsInView)
obj.Remove();
}
this.StartPort2 = null;
if (this.EndPort2 != null) {
GoObject obj = this.EndPort2.GoObject;
if (obj != null && obj.IsInView)
obj.Remove();
}
this.EndPort2 = null;
base.Stop();
}
// work-around for versions 2.2 - 2.5.2
protected override IGoPort CreateTemporaryPort(IGoPort port, PointF pnt, bool forToPort, bool atEnd) {
GoPort tempPort = new GoPort();
if (port.GoObject is GoPort) {
GoPort origport = (GoPort)port.GoObject;
tempPort.FromSpot = origport.FromSpot;
tempPort.ToSpot = origport.ToSpot;
tempPort.PortObject = origport.PortObject;
tempPort.Size = origport.Size;
} else {
tempPort.FromSpot = GoObject.NoSpot;
tempPort.ToSpot = GoObject.NoSpot;
tempPort.Size = new SizeF();
}
tempPort.Center = pnt; // follows mouse movement
// these ports are added to the view, but are hidden
tempPort.Style = GoPortStyle.None;
this.View.Layers.Default.Add(tempPort);
return tempPort;
}
// work-around for versions 2.2 - 2.5.2
protected void ImitatePort(IGoPort endport, IGoPort iport) {
GoPort tempPort = endport as GoPort;
GoPort p = iport as GoPort;
if (p != null) {
// have the temporary port act like the real one it might be connected to
if (tempPort != null) {
tempPort.FromSpot = p.FromSpot;
tempPort.ToSpot = p.ToSpot;
tempPort.PortObject = p.PortObject;
}
} else {
if (tempPort != null) {
tempPort.FromSpot = GoObject.NoSpot;
tempPort.ToSpot = GoObject.NoSpot;
tempPort.PortObject = null;
}
}
}
}
So my guess is that you have some other code somewhere that is causing the weird behavior. Maybe some false assumptions about links or ports always being part of a GoDocument, rather than sometimes being part of a GoView as temporary objects.

Hi Walter,
I ran some tests using your code and it does seem to work OK. Links are deleted properly. Still not quite sure what is going on with my code. Maybe something to do with all of the OnDocumentChanged events being registered in each link? When I have some time, I will move this code to the view where it belongs.