Call the base.FindTargetPort first, then decide whether you like that target port.
Yes, possible pairs of ports will be checked for link relationship validity, and that boolean value is cached so that the validity of that pair of ports is not checked again. But FindTargetPort is called continually during mouse moves and on mouse up.
I don’t know if giving you the implementations will help you, but let’s try. In LinkingBaseTool:
/// <summary>
/// Find a port with which the user could complete a valid link.
/// </summary>
/// <param name="toend">true if looking for a "to" port</param>
/// <returns>
/// a <c>FrameworkElement</c> representing a valid port,
/// or null if no such port is near the current mouse point (within <see cref="PortGravity"/> distance)
/// </returns>
/// <remarks>
/// <para>
/// This finds elements near to the current mouse point for which a valid link connection is possible.
/// For example, when <paramref name="toend"/> is true, this looks for elements (i.e. "ports") in nodes that
/// have <see cref="Node.GetLinkableTo"/> return true and for which <see cref="IsValidTo"/> is true.
/// </para>
/// <para>
/// For each port element found, this calls <see cref="IsValidLink"/> to find out if a link between
/// the original node/port and the found node/port would be valid.
/// The result is saved in the <see cref="ValidPortsCache"/> for faster decisions later during
/// the operation of this tool.
/// The closest valid port is returned.
/// </para>
/// </remarks>
protected virtual FrameworkElement FindTargetPort(bool toend) {
Diagram diagram = this.Diagram;
Point p = diagram.LastMousePointInModel;
double gravity = this.PortGravity;
if (gravity <= 0) gravity = 0.1;
IEnumerable<FrameworkElement> nearports;
if (toend)
nearports = diagram.Panel.FindElementsNear<FrameworkElement>(p, gravity, FindValidToPort, x => true, SearchLayers.Nodes);
else
nearports = diagram.Panel.FindElementsNear<FrameworkElement>(p, gravity, FindValidFromPort, x => true, SearchLayers.Nodes);
double bestDist = Double.PositiveInfinity;
FrameworkElement bestPort = null;
foreach (FrameworkElement port in nearports) {
if (port == null) continue;
Node node = Diagram.FindAncestor<Node>(port);
if (node == null) continue;
Point toPoint = node.GetElementPoint(port, Spot.Center); //?? assumes center point of port
double dx = p.X - toPoint.X;
double dy = p.Y - toPoint.Y;
double dist = dx*dx + dy*dy; // don't bother taking sqrt
if (dist < bestDist) { // closest so far
bool valid = true;
// check cache of IsValidLink calls
if (this.ValidPortsCache.TryGetValue(port, out valid)) {
// known to be either valid or invalid
if (valid) { // known to be a valid port for a link
bestPort = port;
bestDist = dist;
} // else known not valid: don't need to call IsValidLink again
} else { // but if not cached, try IsValidLink in the appropriate direction
if ((toend && IsValidLink(this.OriginalFromNode, this.OriginalFromPort, node, port)) ||
(!toend && IsValidLink(node, port, this.OriginalToNode, this.OriginalToPort))) {
// now known valid, remember in cache
this.ValidPortsCache[port] = true;
bestPort = port;
bestDist = dist;
} else {
// now known not valid, remember in cache
this.ValidPortsCache[port] = false;
}
}
}
}
if (bestPort != null) {
Node targetnode = Diagram.FindAncestor<Node>(bestPort);
if (targetnode != null && (targetnode.Layer == null || targetnode.Layer.AllowLink)) {
return bestPort;
}
}
return null;
}
/// <summary>
/// Mouse movement results in the temporary node moving to where the valid <see cref="LinkingBaseTool.TargetPort"/> is located,
/// or to where the mouse is if there is no valid target port nearby.
/// </summary>
/// <remarks>
/// This calls <see cref="LinkingBaseTool.FindTargetPort"/> to update the <see cref="LinkingBaseTool.TargetPort"/>
/// given the new mouse point.
/// If a valid target port is found, this calls <see cref="LinkingBaseTool.CopyPortProperties"/> to move the
/// temporary node/port and make them appear like the target node/port.
/// If no valid target port is found, this calls <set cref="LinkingBaseTool.SetNoTargetPortProperties"/>
/// to move the temporary node to where the mouse currently is and to remove any node/port appearance.
/// </remarks>
public override void DoMouseMove() {
if (this.Active) {
Diagram diagram = this.Diagram;
this.TargetPort = FindTargetPort(this.Forwards);
if (this.TargetPort != null) {
Node targetnode = Diagram.FindAncestor<Node>(this.TargetPort);
if (targetnode != null) {
if (this.Forwards) {
CopyPortProperties(targetnode, this.TargetPort, this.TemporaryToNode, this.TemporaryToPort, true);
return;
} else {
CopyPortProperties(targetnode, this.TargetPort, this.TemporaryFromNode, this.TemporaryFromPort, false);
return;
}
}
}
// found no potential port
if (this.Forwards) {
SetNoTargetPortProperties(this.TemporaryToNode, this.TemporaryToPort);
} else {
SetNoTargetPortProperties(this.TemporaryFromNode, this.TemporaryFromPort);
}
}
}
In LinkingTool:
/// <summary>
/// A mouse-up ends the linking operation; if there is a valid <see cref="LinkingBaseTool.TargetPort"/> nearby,
/// this calls <see cref="IDiagramModel.AddLink"/> to create a new link.
/// </summary>
/// <remarks>
/// If a new link is created in the model, the corresponding <see cref="Link"/> is selected in the diagram
/// and the "link drawn" event is raised.
/// In any case this stops the tool.
/// </remarks>
public override void DoMouseUp() {
if (this.Active) {
Diagram diagram = this.Diagram;
if (diagram == null) return;
PartManager mgr = diagram.PartManager;
if (mgr == null) return;
this.TransactionResult = null;
Object fromdata = null;
Object fromid = null;
Object todata = null;
Object toid = null;
IDiagramModel model = null;
this.TargetPort = FindTargetPort(this.Forwards);
Node targetnode = null;
if (this.TargetPort != null) {
targetnode = Diagram.FindAncestor<Node>(this.TargetPort);
if (targetnode != null) {
if (this.Forwards) {
if (this.OriginalFromNode != null) {
fromdata = this.OriginalFromNode.Data;
fromid = this.OriginalFromNode.GetPortName(this.OriginalFromPort);
}
todata = targetnode.Data;
toid = targetnode.GetPortName(this.TargetPort);
} else {
fromdata = targetnode.Data;
fromid = targetnode.GetPortName(this.TargetPort);
if (this.OriginalToNode != null) {
todata = this.OriginalToNode.Data;
toid = this.OriginalToNode.GetPortName(this.OriginalToPort);
}
}
model = mgr.FindCommonDataModel(fromdata, todata);
}
} else { // not connecting to a port; set FROMDATA or TODATA, but not both
if (this.Forwards) {
if (this.OriginalFromNode != null) {
ILinksModel lmodel = this.OriginalFromNode.Model as ILinksModel;
if (lmodel != null && lmodel.ValidUnconnectedLinks == ValidUnconnectedLinks.Allowed) {
fromdata = this.OriginalFromNode.Data;
fromid = this.OriginalFromNode.GetPortName(this.OriginalFromPort);
model = lmodel;
}
}
} else {
if (this.OriginalToNode != null) {
ILinksModel lmodel = this.OriginalToNode.Model as ILinksModel;
if (lmodel != null && lmodel.ValidUnconnectedLinks == ValidUnconnectedLinks.Allowed) {
todata = this.OriginalToNode.Data;
toid = this.OriginalToNode.GetPortName(this.OriginalToPort);
model = lmodel;
}
}
}
}
if (model != null) {
Object linkdata = model.AddLink(fromdata, fromid, todata, toid);
Link link = null;
if (linkdata != null) link = mgr.FindLinkForData(linkdata, model);
if (link == null) link = mgr.FindLinkForData(fromdata, todata, model);
if (link != null) {
if (this.TargetPort == null) {
// need to tell Route that it ought to end at Diagram.LastMousePointInModel
if (this.Forwards) {
link.Route.DefaultToPoint = this.Diagram.LastMousePointInModel;
} else {
link.Route.DefaultFromPoint = this.Diagram.LastMousePointInModel;
}
}
if (diagram.AllowSelect) {
diagram.Select(link);
}
// set the TransactionResult before raising event, in case it changes the result or cancels the tool
this.TransactionResult = "NewLink";
RaiseEvent(Diagram.LinkDrawnEvent, new DiagramEventArgs(link));
} else {
model.ClearUnresolvedReferences();
}
}
}
StopTool();
}