Adding new edges to existing ports only

Tips & Tricks

Summary

This article describes how to ensure that new edges will only added to existing ports.

Description

When a new edge is created, the CreateEdgeInputMode queries the INode's lookup for an IPortCandidateProvider implementation. The default candidate provider implementation tries to find an unoccupied port candidate and uses the center if none is found.

To suggest all existing ports as candidates, one has to implement an own port candidate provider:

public class ExistingPortsCandidateProvider extends AbstractPortCandidateProvider
{

  /**
   * The constructor sets the port owner, i.e. the node
   */    
  public function ExistingPortsCandidateProvider( owner:IPortOwner ) {
    setPortOwner(owner);
  }

  /**
   * Returns a list with port candidates representing the node's ports
   */    
  protected override function getPortCandidates(graph:IGraph):Iterable {
    var list:ArrayList = new ArrayList();
    var it:Iterator = this.portOwner.ports.iterator();
    while( it.hasNext() ) {
      list.addItem( DefaultPortCandidate.create( IPort(it.next()) ) );
    }
    return list;
  }

}

There are three ways to install this customized port candidate provider:

Register the port candidate provider in the node's lookup

Installing the candidate provider into the node's lookup has the advantage that it is possible to install it only to selected nodes: var lookup:IMutableLookup = node.lookup( IMutableLookup ) as IMutableLookup; if (lookup != null) { lookup.registerLookup(IPortCandidateProvider, new ExistingPortsCandidateProvider(node)); }

Register the port candidate provider in the graph's lookup chain (yFiles FLEX 1.2 and higher)

To change the behaviour of all nodes, the easiest way is to use the lookup chain of the graph implementation. To do so, first he has to write a simple ILookupChainLink implementation:

public class ExistingPortsLookupChainLink extends AbstractContextLookupChainLink
{
  public override function lookupForItem(item:Object, type:Class):Object {
    if (item is INode && type == IPortCandidateProvider) {
      return new ExistingPortsCandidateProvider(item as INode);
    } 
    return super.lookupForItem(item, type);
  }
}

This chain link can be installed with:

var decorator:ILookupDecorator = graphCanvas.graph.lookup( ILookupDecorator ) as ILookupDecorator;
if( null != decorator ) {
  if( decorator.canDecorate( INode ) ) {
    decorator.addLookup(INode,  new ExistingPortsLookupChainLink() );
  }
}

Note, that this mechanism is available since yFiles FLEX 1.2. For earlier versions, one has register the custom port candidate provider "manually" to all nodes.

Register the port candidate provider in the graph's NodeDecorator (yFiles FLEX 1.4.0.2 and higher)

This is a more convenient way but effectively works the same as using a custom IContextLookupChainLink.
A com.yworks.graph.model.GraphDecorator has to be looked up from the graph so the portCandidateProviderDecorator of its nodeDecorator property can be used to add the ExistingPortsCandidateProvider:

var graphDecorator:GraphDecorator = graphCanvas.graph.lookup(GraphDecorator) as GraphDecorator;
graphDecorator.nodeDecorator.portCandidateProviderDecorator.setFactory(
  function(node:INode):IPortCandidateProvider {
    return new ExistingPortsCandidateProvider(node);
  });

Categories this article belongs to:
yFiles FLEX > Architectural Concepts > Look-up Mechanism
yFiles FLEX > Displaying and Editing Graphs > Styles-related Features
yFiles FLEX > Displaying and Editing Graphs > Visual Representation of Graph Elements
yFiles FLEX > Displaying and Editing Graphs > User Interaction
Applies to:
yFiles FLEX: 1.4, 1.5, 1.6, 1.7, 1.8
Keywords:
ports - port candidates - edge creation - lookup - lookup chain