Is the following application somewhat like what you want?
It uses ForceDirectedLayout for the Diagram.Layout and a GridLayout for the Group.Layout.
It does suffer from not optimizing the positions of the nodes within each group to avoid links crossing over nodes – that would require some custom positioning of the nodes after the Diagram layout had been performed.
These files work in Silverlight 4 or 5 and in WPF 4.
[code]
<UserControl x:Class=“Partition.Partition”
xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=“http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:go=“http://schemas.nwoods.com/GoXam”>
<UserControl.Resources>
<go:StringBrushConverter x:Key=“theBrushConverter” />
<go:BooleanStringConverter x:Key=“theButtonConverter” TrueString=“-” FalseString=“+” />
<go:NodePanel Sizing=“Auto” go:Part.SelectionAdorned=“True”
go:Node.Location=“{Binding Path=Data.Location, Mode=TwoWay}”>
<go:NodeShape go:NodePanel.Figure=“Ellipse”
go:Node.PortId=“” go:Node.LinkableFrom=“True” go:Node.LinkableTo=“True”
Fill=“{Binding Path=Data.Color, Converter={StaticResource theBrushConverter}}” />
</go:NodePanel>
<go:DataTemplateDictionary x:Key="GDTD">
<DataTemplate x:Key="">
<Border CornerRadius="5" BorderThickness="2" Background="Transparent"
BorderBrush="{Binding Path=Data.Color, Converter={StaticResource theBrushConverter}}"
go:Part.SelectionAdorned="True"
go:Node.LocationElementName="myGroupPanel"
go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"
go:Node.Avoidable="False"
go:Group.IsSubGraphExpanded="True">
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Button x:Name="myCollapseExpandButton" Click="CollapseExpandButton_Click"
Content="{Binding Path=Group.IsExpandedSubGraph, Converter={StaticResource theButtonConverter}}"
Width="20" Margin="0 0 5 0"/>
<TextBlock Text="{Binding Path=Data.Key}" FontWeight="Bold" />
</StackPanel>
<go:GroupPanel x:Name="myGroupPanel" Padding="5" />
</StackPanel>
<go:Group.Layout>
<go:GridLayout CellSize="1 1" Spacing="20 20" WrappingColumn="3" />
</go:Group.Layout>
</Border>
</DataTemplate>
<DataTemplate x:Key="Collapsed">
<StackPanel Orientation="Horizontal"
go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"
go:Group.IsSubGraphExpanded="False">
<Button x:Name="myCollapseExpandButton" Click="CollapseExpandButton_Click"
Content="{Binding Path=Group.IsExpandedSubGraph, Converter={StaticResource theButtonConverter}}"
Width="20" />
<TextBlock Text="{Binding Path=Data.Key}" FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</go:DataTemplateDictionary></p><p> <DataTemplate x:Key="LinkTemplate">
<go:LinkShape x:Name="Path" StrokeThickness="2" go:Part.Reshapable="True"
Stroke="{Binding Path=Link.FromNode.Data.Color}" >
<go:Link.Route>
<go:Route Routing="AvoidsNodes" Curve="None" Corner="5" RelinkableFrom="True" RelinkableTo="True" />
</go:Link.Route>
</go:LinkShape>
</DataTemplate>
</UserControl.Resources>
<go:Diagram x:Name=“myDiagram” Padding=“10”
HorizontalContentAlignment=“Stretch” VerticalContentAlignment=“Stretch”
NodeTemplate=“{StaticResource NodeTemplate}”
GroupTemplateDictionary=“{StaticResource GDTD}”
LinkTemplate=“{StaticResource LinkTemplate}”>
go:Diagram.Layout
<go:ForceDirectedLayout />
</go:Diagram.Layout>
<go:Node Id=“Loading”>
</go:Node>
</go:Diagram>
[/code]
[code]/* Copyright © Northwoods Software Corporation, 2008-2012. All Rights Reserved. */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Northwoods.GoXam;
using Northwoods.GoXam.Model;
namespace Partition {
public partial class Partition : UserControl {
public Partition() {
InitializeComponent();
var model = new GraphModel<SimpleData, String>();
var nodes = new ObservableCollection<SimpleData>();
var subgraphs = new List<SimpleData>();
// create a bunch of subgraph data
for (int i = 0; i < 7; i++) {
SimpleData g = new SimpleData();
g.Key = String.Format("Group {0:D}", i);
g.Color = String.Format("#{0:X}{1:X}{2:X}", 80+rand.Next(100), 80+rand.Next(100), 80+rand.Next(100));
g.IsSubGraph = true;
g.IsSubGraphExpanded = true;
g.WasSubGraphExpanded = false;
subgraphs.Add(g);
nodes.Add(g);
}
// create a lot of regular node data that are members of those subgraphs
// But the PartManager won't create Nodes for these node data unless the parent group is expanded
for (int i = 0; i < subgraphs.Count; i++) {
for (int j = 0; j < 4; j++) {
SimpleData d = new SimpleData();
d.Color = String.Format("#{0:X}{1:X}{2:X}", 120+rand.Next(100), 120+rand.Next(100), 120+rand.Next(100));
d.Key = d.Color;
d.SubGraphKey = subgraphs[i].Key;
nodes.Add(d);
}
}
model.NodesSource = nodes;
List<SimpleData> simples = nodes.Where(d => !d.IsSubGraph).ToList();
for (int i = 0; i < simples.Count; i++) {
SimpleData d = simples[i];
d.FromKeys.Add(simples[i == 0 ? 0 : rand.Next(i)].Key);
}
model.Modifiable = true;
myDiagram.Model = model;
model.HasUndoManager = true;
// remove the "Loading..." message
myDiagram.InitialLayoutCompleted += (s, e) => {
Node n = myDiagram.PartsModel.FindNodeByKey("Loading");
if (n != null) myDiagram.PartsModel.RemoveNode(n);
};
}
Random rand = new Random();
private void CollapseExpandButton_Click(object sender, RoutedEventArgs e) {
// the Button is in the visual tree of a Node
Button button = (Button)sender;
Group sg = Part.FindAncestor<Group>(button);
if (sg != null) {
SimpleData subgraphdata = (SimpleData)sg.Data;
if (!subgraphdata.IsSubGraph) return;
// always make changes within a transaction
myDiagram.StartTransaction("CollapseExpand");
// toggle whether this node is expanded or collapsed
//sg.IsExpandedSubGraph = !sg.IsExpandedSubGraph;
// instead of data-binding go:Group.IsSubGraphExpanded, just replace the template
// with one that has that attached property set appropriately
subgraphdata.Category = sg.IsExpandedSubGraph ? "Collapsed" : "";
myDiagram.CommitTransaction("CollapseExpand");
}
}
}
// Because these properties are only set at initialization,
// their setters do not need to call RaisePropertyChanged.
public class SimpleData : GraphModelNodeData {
public String Color { get; set; }
}
}[/code]