We can't think of any way to arrange containers with minimized link crossings without knowing what kind of content they have. If the container contains a row of nodes without links between them as in your screenshot, you can use the SortByIncomingLinks method below to sort them and eliminate crossings of external links.
If the containers' content is completely arbitrary, e.g. with child nodes connected too, you could use the constrained spring layout method shown to arrange them. This method depends on the initial container size so you might have to set some container.Bounds size appropriate for the number of its children before running it. Also if the container has a lot of children, you might have to increase the number of SpringLayout iterations.
Arrange(diagram, outerLayout, innerLayout);
SortByIncomingLinks();
//ConstrainedSpringLayout();
void SortByIncomingLinks()
{
foreach (DiagramNode node in diagram.Nodes)
{
ContainerNode ctr = node as ContainerNode;
if (ctr != null)
SortByIncomingLinks(ctr);
}
}
void SortByIncomingLinks(ContainerNode ctr)
{
List<DiagramNode> children = new List<DiagramNode>();
foreach (DiagramNode node in ctr.SubordinateGroup.AttachedNodes)
children.Add(node);
children.Sort(CompareByInLinks);
float x = ctr.Bounds.X + 5;
foreach (DiagramNode node in children)
{
node.Move(x, node.Bounds.Y);
x = node.Bounds.Right + 5;
}
}
int CompareByInLinks(DiagramNode n1, DiagramNode n2)
{
DiagramLink link1 = null;
if (n1.IncomingLinks.Count > 0)
link1 = n1.IncomingLinks[0];
DiagramLink link2 = null;
if (n2.IncomingLinks.Count > 0)
link2 = n2.IncomingLinks[0];
if (link1 != null && link2 == null)
return -1;
if (link1 == null && link2 != null)
return 1;
if (link1 == null && link2 == null)
return 0;
PointF p1 = link1.ControlPoints[link1.ControlPoints.Count - 2];
PointF p2 = link2.ControlPoints[link2.ControlPoints.Count - 2];
return p1.X.CompareTo(p2.X);
}
void ConstrainedSpringLayout()
{
foreach (DiagramNode node in diagram.Nodes)
{
ContainerNode ctr = node as ContainerNode;
if (ctr != null)
ConstrainedSpringLayout(ctr);
}
}
void ConstrainedSpringLayout(ContainerNode ctr)
{
DiagramItemCollection childNodesAndLinks = GetContainerItems(ctr);
// find external nodes connected to nodes in the container
List<DiagramNode> externalNodes = new List<DiagramNode>();
List<DiagramLink> externalLinks = new List<DiagramLink>();
foreach (DiagramItem item in childNodesAndLinks)
{
DiagramNode node = item as DiagramNode;
if (node == null)
continue;
foreach (DiagramLink link in node.GetAllLinks())
{
DiagramNode adjacent = GetOtherEnd(link, node);
if (!childNodesAndLinks.Contains(adjacent))
{
externalNodes.Add(adjacent);
externalLinks.Add(link);
}
}
}
// set the frozen flag on the external nodes to prevent spring layout from moving them
foreach (DiagramNode node in externalNodes)
{
node.LayoutTraits[SpringLayoutTraits.Frozen] = true;
childNodesAndLinks.Add(node);
}
foreach (DiagramLink link in externalLinks)
childNodesAndLinks.Add(link);
RectangleF layoutRect = ctr.Bounds;
layoutRect.Y += ctr.CaptionHeight;
layoutRect.Height -= ctr.CaptionHeight;
layoutRect.Inflate(-5, -5);
SpringLayout layout = new SpringLayout();
layout.LayoutArea = layoutRect;
layout.SplitGraph = false;
layout.Arrange(diagram, childNodesAndLinks);
}
The attached image shows the result of ConstrainedSpringLayout.
You could analyze the content of each node and choose which method to use based on it, and also add handling for other special cases when you think they can be arranged better than the force-directed method does.
I hope that helps,
Stoyan