diff --git a/wpf-toc.html b/wpf-toc.html index 5dcba57eac..4f69340755 100644 --- a/wpf-toc.html +++ b/wpf-toc.html @@ -892,6 +892,7 @@
  • MindMap tree layout
  • Hierarchical tree layout
  • Radial-Tree layout
  • +
  • Force-Directed-Tree ayout
  • Overview Control
  • diff --git a/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout-nodes-connectors.png b/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout-nodes-connectors.png new file mode 100644 index 0000000000..152c32fe69 Binary files /dev/null and b/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout-nodes-connectors.png differ diff --git a/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout.png b/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout.png new file mode 100644 index 0000000000..f6739d930a Binary files /dev/null and b/wpf/Diagram/Automatic-Layouts/Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout.png differ diff --git a/wpf/Diagram/Automatic-Layouts/ForceDirectedTreeLayout.md b/wpf/Diagram/Automatic-Layouts/ForceDirectedTreeLayout.md new file mode 100644 index 0000000000..dbb6c60c12 --- /dev/null +++ b/wpf/Diagram/Automatic-Layouts/ForceDirectedTreeLayout.md @@ -0,0 +1,736 @@ +--- +layout: post +title: Force-Directed Tree layout in WPF Diagram control | Syncfusion® +description: Learn Force-Directed Tree layout support in Syncfusion® WPF Diagram (SfDiagram) with simple, clear XAML and C# examples. +platform: wpf +control: SfDiagram +documentation: ug +--- + +# Force-Directed Tree layout in WPF Diagram (SfDiagram) + +The Force-Directed Tree layout arranges nodes using a physics simulation: nodes repel each other to reduce overlap while connectors behave like springs that pull related nodes together. This produces organic, visually balanced diagrams that work well for social graphs, dependency maps, and knowledge networks. + +## Properties for Configure force-directed tree layout (WPF) +The below mentioned properties are used to configure the force-directed tree layout: + + **MaximumIteration** + + Number of simulation cycles the algorithm runs to stabilize node positions. + > Note: Minimum recommended: 100. Higher values often produce more stable layouts but increase CPU time. + + **RepulsionStrength** + + Magnitude of the repulsive force between nodes, preventing overlap and crowding. + > Note: Typical minimum: 3000. Increase for more separation; decrease for denser layouts. + +**AttractionStrength** + + How strongly connected nodes are pulled toward each other (range 0..1). + > Note: Values closer to 1 create tighter clusters; lower values allow connected nodes to spread out to ease congestion. + +--- + +## Create a layout using Nodes and Connectors + +Define nodes and connectors directly in XAML, then let the `ForceDirectedTree` layout arrange them. + +{% tabs %} +{% highlight xaml %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{% endhighlight %} + +{% highlight c# %} +// Configure the layout and create nodes/connectors in code-behind + +CreatedNode(); +Diagram.LayoutManager = new LayoutManager() +{ + Layout = new ForceDirectedTree() + { + AttractionStrength = 0.6, + RepulsionStrength = 25000, + MaximumIteration = 2500, + } +}; + +private void CreatedNode() +{ + // create nodes first + string[] labels = new[] + { + "Team", // 1 root + "PO-1", // 2 + "PO-2", // 3 + "PO-3", // 4 + "PO-4", // 5 + "PO-5", // 6 + "Team-1", // 7 + "Team-2", // 8 + "Team-3", // 9 + "Team-4", //10 + "Team-1", //11 + "Team-2", //12 + "Team-3", //13 + "Team-4", //14 + "Team-1", //15 + "Team-2", //16 + "Team-3", //17 + "Team-4", //18 + "Team-1", //19 + "Team-2", //20 + "Team-3", //21 + "Team-4", //22 + "Team-1", //23 + "Team-2", //24 + "Sales Team", //25 + "AGM-1", //26 + "AGM-2", //27 + "Team-4", //28 + "Team-3", //29 + "Team-2" //30 + }; + + for (int i = 1; i <= 30; i++) + { + var role = GetRoleForIndex(i); // "Root","Parent","Child" + var id = $"Node{i}"; + var label = labels[i - 1]; + (Diagram.Nodes as NodeCollection).Add(CreateNode(id, role, label)); + } + + // Connectors (parent -> child) + var cons = Diagram.Connectors as ConnectorCollection; + + // Level 0 -> Level 1 + cons.Add(Edge("Node1", "Node2")); + cons.Add(Edge("Node1", "Node3")); + cons.Add(Edge("Node1", "Node4")); + cons.Add(Edge("Node1", "Node5")); + + // Level 1 -> Level 2 + cons.Add(Edge("Node2", "Node6")); + cons.Add(Edge("Node2", "Node7")); + cons.Add(Edge("Node2", "Node8")); + + cons.Add(Edge("Node3", "Node9")); + cons.Add(Edge("Node3", "Node10")); + cons.Add(Edge("Node3", "Node11")); + + cons.Add(Edge("Node4", "Node12")); + cons.Add(Edge("Node4", "Node13")); + cons.Add(Edge("Node4", "Node14")); + + cons.Add(Edge("Node5", "Node15")); + cons.Add(Edge("Node5", "Node16")); + cons.Add(Edge("Node5", "Node17")); + + // Leaves and deeper children + cons.Add(Edge("Node7", "Node18")); + cons.Add(Edge("Node10", "Node19")); + cons.Add(Edge("Node14", "Node20")); + cons.Add(Edge("Node11", "Node21")); + + cons.Add(Edge("Node12", "Node22")); + cons.Add(Edge("Node12", "Node23")); + cons.Add(Edge("Node12", "Node24")); + + cons.Add(Edge("Node13", "Node25")); + cons.Add(Edge("Node13", "Node26")); + cons.Add(Edge("Node13", "Node27")); + + cons.Add(Edge("Node14", "Node28")); + cons.Add(Edge("Node14", "Node29")); + cons.Add(Edge("Node14", "Node30")); +} + +// Determine role by index (adjust ranges as desired) +private string GetRoleForIndex(int index) +{ + if (index == 1) return "Root"; + if (index >= 2 && index <= 5) return "Parent"; + return "Child"; +} + +private CustomNodeViewModel CreateNode(string id, string role, string label) +{ + double size = role == "Root" ? 140 : role == "Parent" ? 100 : 60; + var geom = new EllipseGeometry(new Point(size / 2, size / 2), size / 2, size / 2); + + var node = new CustomNodeViewModel + { + ID = id, + UnitWidth = size, + UnitHeight = size, + Tag = role, + Shape = geom, + Annotations = new ObservableCollection + { + new AnnotationEditorViewModel + { + Content = label + } + } + }; + + return node; +} + +private ConnectorViewModel Edge(string sourceId, string targetId) +{ + return new ConnectorViewModel + { + ID = $"{sourceId}->{targetId}", + SourceNodeID = sourceId, + TargetNodeID = targetId + }; +} +{% endhighlight %} +{% endtabs %} + +![WPF Diagram with Force-Directed Layout](Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout-nodes-connectors.png) + +[View sample in GitHub](https://github.com/SyncfusionExamples/WPF-Diagram-Examples/) + +--- + +## Create a Force-Directed Tree from a data source + +Bind a collection to `DataSourceSettings`. At least one item should have a null/empty `Manager` to act as a root. Configure the `LayoutManager` with `ForceDirectedTree`. + +{% tabs %} +{% highlight xaml %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{% endhighlight %} + +{% highlight c# %} +// C#: Create the layout using a data source in code-behind + +public class ForceDirectedDetails +{ + public string Id { get; set; } + public string Role { get; set; } + public string Manager { get; set; } + public string Color { get; set; } + public double Width { get; set; } + public double Height { get; set; } +} + +private List GetForceDirectedData() +{ + return new List + { + new() { Id = "Dev", Role = "Team", Color = "Orange", Width = 140, Height = 140 }, + + new() { Id = "Dept", Role = "PO-5", Manager = "Dev", Color = "Blue", Width = 100, Height = 100 }, + new() { Id = "PO1", Role = "PO-1", Manager = "Dev", Color = "Blue", Width = 100, Height = 100 }, + new() { Id = "PO2", Role = "PO-2", Manager = "Dev", Color = "Blue", Width = 100, Height = 100 }, + new() { Id = "PO3", Role = "PO-3", Manager = "Dev", Color = "Blue", Width = 100, Height = 100 }, + new() { Id = "PO4", Role = "PO-4", Manager = "Dev", Color = "Blue", Width = 100, Height = 100 }, + + new() { Id = "PO1_T1", Role = "Team-1", Manager = "PO1", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO1_T2", Role = "Team-2", Manager = "PO1", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO1_T3", Role = "Team-3", Manager = "PO1", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO1_T4", Role = "Team-4", Manager = "PO1", Color = "Green", Width = 80, Height = 80 }, + + new() { Id = "PO2_T1", Role = "Team-1", Manager = "PO2", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO2_T2", Role = "Team-2", Manager = "PO2", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO2_T3", Role = "Team-3", Manager = "PO2", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO2_T4", Role = "Team-4", Manager = "PO2", Color = "Green", Width = 80, Height = 80 }, + + new() { Id = "PO3_T1", Role = "Team-1", Manager = "PO3", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO3_T2", Role = "Team-2", Manager = "PO3", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO3_T3", Role = "Team-3", Manager = "PO3", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO3_T4", Role = "Team-4", Manager = "PO3", Color = "Green", Width = 80, Height = 80 }, + + new() { Id = "PO4_T1", Role = "Team-1", Manager = "PO4", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO4_T2", Role = "Team-2", Manager = "PO4", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO4_T3", Role = "Team-3", Manager = "PO4", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "PO4_T4", Role = "Team-4", Manager = "PO4", Color = "Green", Width = 80, Height = 80 }, + + new() { Id = "Sales", Role = "Sales Team", Manager = "Dept", Color = "Green", Width = 80, Height = 80 }, + new() { Id = "AGM1", Role = "AGM-1", Manager = "Sales", Color = "Red", Width = 60, Height = 60 }, + new() { Id = "AGM2", Role = "AGM-2", Manager = "Sales", Color = "Red", Width = 60, Height = 60 } + }; +} + +// Apply binding and layout (e.g., in Window Loaded) +Diagram.DataSourceSettings = new DataSourceSettings() +{ + Id = "Id", + ParentId = "Manager", + DataSource = GetForceDirectedData() +}; + +Diagram.LayoutManager = new LayoutManager() +{ + Layout = new ForceDirectedTree() + { + AttractionStrength = 0.7, + RepulsionStrength = 25000, + MaximumIteration = 500 + } +}; + +// Optional: Fit the entire diagram to the viewport + (Diagram.Info as IGraphInfo).Commands.FitToPage.Execute(null); +{% endhighlight %} +{% endtabs %} + +![WPF Diagram with Force-Directed Layout](Automatic-Layouts_images/wpf-diagram-force-directed-tree-layout.png) + +[View sample in GitHub](https://github.com/SyncfusionExamples/WPF-Diagram-Examples/tree/master/Samples/Automatic%20Layout/Force%20Directed%20Tree%20layout/ForceDirectedTreeDataSource) + +## See also