How to Use Custom Graph Layout Algorithms to Arrange Shapes in DiagramControl
- 13 minutes to read
DiagramControl provides two methods that make it easier to use external graph layout algorithms to arrange diagram shapes. The GraphOperations.GetDiagramGraph method reads the diagram currently loaded into DiagramControl and returns the Graph object that contains collections of edges and nodes represented by diagram items. You can use this information to calculate positions for diagram shapes. Then, for every shape, create the PositionInfoobject containing the shape reference and its position. To apply the layout to the loaded diagram, call the DiagramControl.RelayoutDiagramItems method that accepts the collection of PositionInfo objects.This example demonstrates how the GetDiagramGraph and RelayoutDiagramItems methods can be used to connect the Microsoft Automatic Graph Layout (MSAGL) library to DiagramControl.
- GraphLayout.vb
- Converter.vb
- LayoutExampleWindow.xaml.vb
- RankingLayoutCalculator.vb
- PhyloTreeLayoutCalculator.vb
- DisconnectedGraphsLayoutCaculator.vb
- PhyloTreeLayout.vb
- ILayoutCalculator.vb
- LayoutExampleWindow.xaml)
- MainWindow.xaml.vb
- MsaglGeometryGraphHelpers.vb
- MainWindow.xaml
- MDSLayoutCalculator.vb
- SugiyamaLayoutCalculator.vb
- RoutingHelper.vb
- MsaglGeometryGraphHelpers.cs
- RankingLayoutCalculator.cs
- PhyloTreeLayout.cs
- SugiyamaLayoutCalculator.cs
- PhyloTreeLayoutCalculator.cs
- ILayoutCalculator.cs
- LayoutExampleWindow.xaml.cs
- MDSLayoutCalculator.cs
- GraphLayout.cs
- RoutingHelper.cs
- MainWindow.xaml.cs
- DisconnectedGraphsLayoutCaculator.cs
- Converter.cs
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Layout
Imports DevExpress.Diagram.Core.Routing
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Core.Routing
Imports System.Collections.Generic
Namespace MsaglHelpers
Public Class GraphLayout
Private Property GeometryGraph() As GeometryGraph
Private ReadOnly Property RoutingMode() As EdgeRoutingMode
Get
Return LayoutCalculator.LayoutAlgorithmSettings.EdgeRoutingSettings.EdgeRoutingMode
End Get
End Property
Protected Property LayoutCalculator() As ILayoutCalculator
Public Sub New(ByVal layoutCalculator As ILayoutCalculator)
Me.LayoutCalculator = layoutCalculator
End Sub
Public Overridable Function RelayoutGraphNodesPosition(ByVal graph As Graph(Of IDiagramItem)) As IEnumerable(Of PositionInfo(Of IDiagramItem))
GeometryGraph = MsaglGeometryGraphHelpers.CreateGeometryGraph(graph)
LayoutCalculator.CalculateLayout(GeometryGraph)
Return MsaglGeometryGraphHelpers.GetGetNodesPositionInfo(GeometryGraph)
End Function
Public Function GetDiagramConnectorType() As ConnectorType
Return RoutingHelper.GetDiagramConnectorType(RoutingMode)
End Function
End Class
End Namespace
Imports MsaglPoint = Microsoft.Msagl.Core.Geometry.Point
Imports System.Windows
Namespace MsaglHelpers
Public Module Converter
<System.Runtime.CompilerServices.Extension> _
Public Function MsaglPointToPointConvert(ByVal msaglPoint As MsaglPoint) As Point
Return New Point(msaglPoint.X, msaglPoint.Y)
End Function
End Module
End Namespace
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Layout
Imports DevExpress.Mvvm.Native
Imports MsaglHelpers
Imports System.Linq
Imports System.Windows
Namespace DXDiagram.CustomLayoutAlgorithms
''' <summary>
''' Interaction logic for LayoutExampleWindow.xaml
''' </summary>
Partial Public Class LayoutExampleWindow
Inherits Window
Private ReadOnly layout As GraphLayout
Private ReadOnly sourceGraphPath As String
Public Sub New(ByVal layout As GraphLayout, ByVal sourceGraphPath As String)
InitializeComponent()
Me.layout = layout
Me.sourceGraphPath = sourceGraphPath
AddHandler Loaded, AddressOf OnLoaded
End Sub
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
diagramControl.LoadDocument(sourceGraphPath)
End Sub
Private Sub RelayoutDiagramItem(ByVal sender As Object, ByVal e As RoutedEventArgs)
diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)))
diagramControl.Controller.RegisterRoutingStrategy(layout.GetDiagramConnectorType(), layout.GetDiagramRoutingStrategy())
diagramControl.Items.OfType(Of IDiagramConnector)().ForEach(Sub(connector)
connector.Type = layout.GetDiagramConnectorType()
connector.UpdateRoute()
End Sub)
diagramControl.FitToDrawing()
End Sub
End Class
End Namespace
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Core.Routing
Imports Microsoft.Msagl.Miscellaneous
Imports Microsoft.Msagl.Prototype.Ranking
Namespace MsaglHelpers
Public Class RankingLayoutCalculator
Implements ILayoutCalculator
Public ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings Implements ILayoutCalculator.LayoutAlgorithmSettings
Get
Return New RankingLayoutSettings() With {
.NodeSeparation = 30, .EdgeRoutingSettings = New EdgeRoutingSettings() With {.EdgeRoutingMode = EdgeRoutingMode.Rectilinear}
}
End Get
End Property
Public Sub CalculateLayout(ByVal geometryGraph As GeometryGraph) Implements ILayoutCalculator.CalculateLayout
Dim geomGraphComponents = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges)
Dim settings = TryCast(LayoutAlgorithmSettings, RankingLayoutSettings)
For Each components In geomGraphComponents
Dim layout = New RankingLayout(settings, components)
components.Margins = 30
layout.Run()
Next components
Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(geomGraphComponents, settings)
geometryGraph.UpdateBoundingBox()
End Sub
End Class
End Namespace
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Layout.Layered
Imports Microsoft.Msagl.Core.Geometry.Curves
Imports Microsoft.Msagl.Core.Routing
Imports System
Imports Microsoft.Msagl.Prototype.Phylo
Namespace MsaglHelpers
Public Class PhyloTreeLayoutCalculator
Implements ILayoutCalculator
Public ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings Implements ILayoutCalculator.LayoutAlgorithmSettings
Get
Return New SugiyamaLayoutSettings() With {
.Transformation = PlaneTransformation.Rotation(Math.PI), .EdgeRoutingSettings = New EdgeRoutingSettings() With {.EdgeRoutingMode = EdgeRoutingMode.StraightLine}
}
End Get
End Property
Public Sub CalculateLayout(ByVal phyloTree As GeometryGraph) Implements ILayoutCalculator.CalculateLayout
Microsoft.Msagl.Miscellaneous.LayoutHelpers.CalculateLayout(phyloTree, LayoutAlgorithmSettings, Nothing)
End Sub
End Class
End Namespace
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Layout.Layered
Imports Microsoft.Msagl.Core.Geometry.Curves
Imports Microsoft.Msagl.Core.Routing
Imports System
Namespace MsaglHelpers
Public Class DisconnectedGraphsLayoutCalculator
Implements ILayoutCalculator
Public ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings Implements ILayoutCalculator.LayoutAlgorithmSettings
Get
Return New SugiyamaLayoutSettings() With {
.Transformation = PlaneTransformation.Rotation(Math.PI), .EdgeRoutingSettings = New EdgeRoutingSettings() With {.EdgeRoutingMode = EdgeRoutingMode.StraightLine}
}
End Get
End Property
Public Sub CalculateLayout(ByVal geometryGraph As GeometryGraph) Implements ILayoutCalculator.CalculateLayout
Dim geomGraphComponents = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges)
Dim settings = TryCast(LayoutAlgorithmSettings, SugiyamaLayoutSettings)
For Each components In geomGraphComponents
Dim layout = New LayeredLayout(components, settings)
components.Margins = 100
layout.Run()
Next components
Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(geomGraphComponents, settings)
geometryGraph.UpdateBoundingBox()
End Sub
End Class
End Namespace
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Layout
Imports DevExpress.Diagram.Core.Routing
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Core.Routing
Imports Microsoft.Msagl.Prototype.Phylo
Imports System.Collections.Generic
Namespace MsaglHelpers
Public Class PhyloTreeLayout
Inherits GraphLayout
Private Property Tree() As PhyloTree
Public Sub New(ByVal layoutCalculator As ILayoutCalculator)
MyBase.New(layoutCalculator)
End Sub
Public Overrides Function RelayoutGraphNodesPosition(ByVal graph As Graph(Of IDiagramItem)) As IEnumerable(Of PositionInfo(Of IDiagramItem))
Tree = MsaglGeometryGraphHelpers.CreatePhyloTrees(graph)
LayoutCalculator.CalculateLayout(Tree)
Return MsaglGeometryGraphHelpers.GetGetNodesPositionInfo(Tree)
End Function
End Class
End Namespace
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Core.Routing
Namespace MsaglHelpers
Public Interface ILayoutCalculator
ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings
Sub CalculateLayout(ByVal geometryGraph As GeometryGraph)
End Interface
End Namespace
<Window x:Class="DXDiagram.CustomLayoutAlgorithms.LayoutExampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DXDiagram.CustomLayoutAlgorithms"
xmlns:dxdiag="clr-namespace:DevExpress.Xpf.Diagram;assembly=DevExpress.Xpf.Diagram.v16.1"
xmlns:dxdiagcore="clr-namespace:DevExpress.Diagram.Core;assembly=DevExpress.Diagram.v16.1.Core"
mc:Ignorable="d"
Title="LayoutExampleWindow" Height="900" Width="1200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button x:Name="relayoutButton" Content="Relayout diagram items" Click="RelayoutDiagramItem" Command="" Grid.Row="0"/>
<dxdiag:DiagramDesignerControl x:Name="diagramControl"
Grid.Row="1"
DockPanel.Dock="Right"
PageSize="1100,800"
FitToDrawingMargin="90"
ToolboxVisibility="Compact"
ZoomFactor="0.73"
SelectedStencils="BasicShapes,ArrowShapes"/>
</Grid>
</Window>
Imports MsaglHelpers
Imports DevExpress.Xpf.Ribbon
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Layout
Imports System.Linq
Imports DevExpress.Mvvm.Native
Imports DevExpress.Xpf.Core
Imports System
Namespace DXDiagram.CustomLayoutAlgorithms
''' <summary>
''' Interaction logic for MainWindow.xaml
''' </summary>
Partial Public Class MainWindow
Inherits DXRibbonWindow
Public Sub New()
InitializeComponent()
End Sub
Private Sub LoadSugiyama(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
diagramControl.LoadDocument("Data/SugiyamaLayout.xml")
End Sub
Private Sub ApplySugiyama(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
ApplyLayout(New GraphLayout(New SugiyamaLayoutCalculator()))
End Sub
Private Sub LoadDisconnectedGraphs(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
diagramControl.LoadDocument("Data/DisconnectedGraphs.xml")
End Sub
Private Sub ApplyDisconnectedGraphs(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
ApplyLayout(New GraphLayout(New DisconnectedGraphsLayoutCalculator()))
End Sub
Private Sub LoadPhyloTree(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
diagramControl.LoadDocument("Data/PhyloTree.xml")
End Sub
Private Sub ApplyPhyloTree(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
ApplyLayout(New PhyloTreeLayout(New PhyloTreeLayoutCalculator()))
End Sub
Private Sub LoadRanking(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
diagramControl.LoadDocument("Data/RankingLayout.xml")
End Sub
Private Sub ApplyRanking(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
ApplyLayout(New GraphLayout(New RankingLayoutCalculator()))
End Sub
Private Sub LoadMDS(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
diagramControl.LoadDocument("Data/MDSLayout.xml")
End Sub
Private Sub ApplyMDS(ByVal sender As Object, ByVal e As DevExpress.Xpf.Bars.ItemClickEventArgs)
ApplyLayout(New GraphLayout(New MDSLayoutCalculator()))
End Sub
Private Sub ApplyLayout(ByVal layout As GraphLayout)
Try
diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)))
diagramControl.Items.OfType(Of IDiagramConnector)().ForEach(Sub(connector)
connector.Type = layout.GetDiagramConnectorType()
connector.UpdateRoute()
End Sub)
diagramControl.FitToDrawing()
Catch e As Exception
DXMessageBox.Show(String.Format("Error message: '{0}'", e.Message), "Error has been occurred")
End Try
End Sub
End Class
End Namespace
Imports System.Linq
Imports DevExpress.Diagram.Core
Imports DevExpress.Mvvm.Native
Imports System.Collections.Generic
Imports DevExpress.Diagram.Core.Layout
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Prototype.Phylo
Imports Microsoft.Msagl.Core.Geometry.Curves
Imports Microsoft.Msagl.Core.Geometry
Namespace MsaglHelpers
Public NotInheritable Class MsaglGeometryGraphHelpers
Private Sub New()
End Sub
Public Shared Function GetGetNodesPositionInfo(ByVal geometryGraph As GeometryGraph) As IEnumerable(Of PositionInfo(Of IDiagramItem))
Return geometryGraph.Nodes.Select(Function(node) New PositionInfo(Of IDiagramItem)(DirectCast(node.UserData, IDiagramItem), Converter.MsaglPointToPointConvert(node.BoundingBox.LeftTop)))
End Function
Public Shared Function CreatePhyloTrees(ByVal graph As Graph(Of IDiagramItem)) As PhyloTree
Dim phyloTree As New PhyloTree()
For Each node In graph.Nodes
AddNode(phyloTree, node)
Next node
For Each edge In graph.Edges
AddPhyloTreeEdge(phyloTree, edge.From, edge.To, edge.Weight)
Next edge
Return phyloTree
End Function
Public Shared Function CreateGeometryGraph(ByVal graph As Graph(Of IDiagramItem)) As GeometryGraph
Dim geomGraph As New GeometryGraph()
For Each node In graph.Nodes
AddNode(geomGraph, node)
Next node
For Each edge In graph.Edges
AddEdge(geomGraph, edge.From, edge.To, edge.Weight)
Next edge
Return geomGraph
End Function
Public Shared Function AddNode(ByVal geometryGraph As GeometryGraph, ByVal item As IDiagramItem) As Node
Dim msaglNode As Node = geometryGraph.FindNodeByUserData(item)
If msaglNode Is Nothing Then
msaglNode = New Node(CreateCurve(item), item)
geometryGraph.Nodes.Add(msaglNode)
End If
Return msaglNode
End Function
Public Shared Sub AddEdge(ByVal geometryGraph As GeometryGraph, ByVal parentNodeSource As IDiagramItem, ByVal childNodeSource As IDiagramItem, ByVal weight As Double)
geometryGraph.Edges.Add(New Edge(AddNode(geometryGraph, parentNodeSource), AddNode(geometryGraph, childNodeSource)) With {.Weight = CInt((weight))})
End Sub
Public Shared Sub AddPhyloTreeEdge(ByVal phyloTree As PhyloTree, ByVal parentNodeSource As IDiagramItem, ByVal childNodeSource As IDiagramItem, Optional ByVal weight As Double = 1)
phyloTree.Edges.Add(New PhyloEdge(AddNode(phyloTree, parentNodeSource), AddNode(phyloTree, childNodeSource)) With {.Weight = CInt((weight))})
End Sub
Public Shared Function CreateCurve(ByVal item As IDiagramItem) As ICurve
Return CurveFactory.CreateRectangle(item.ActualSize.Width, item.ActualSize.Height, New Point())
End Function
End Class
End Namespace
<dxr:DXRibbonWindow x:Class="DXDiagram.CustomLayoutAlgorithms.MainWindow" mc:Ignorable="d" Title="MainWindow" Height="900" Width="1200"
xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
xmlns:local="clr-namespace:DXDiagram.CustomLayoutAlgorithms"
xmlns:dxdiag="clr-namespace:DevExpress.Xpf.Diagram;assembly=DevExpress.Xpf.Diagram.v16.1">
<DockPanel>
<dxr:RibbonControl DockPanel.Dock="Top">
<dxr:RibbonPageCategory Caption="MSAGL">
<dxr:RibbonPage Caption="Layout Actions">
<dxr:RibbonPageGroup Caption="Sugiyama Layout">
<dxb:BarButtonItem Content="Load sample file" ItemClick="LoadSugiyama" />
<dxb:BarButtonItem Content="Apply layout" ItemClick="ApplySugiyama" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="Disconnected Graphs">
<dxb:BarButtonItem Content="Load sample file" ItemClick="LoadDisconnectedGraphs" />
<dxb:BarButtonItem Content="Apply layout" ItemClick="ApplyDisconnectedGraphs" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="Phylo Tree">
<dxb:BarButtonItem Content="Load sample file" ItemClick="LoadPhyloTree" />
<dxb:BarButtonItem Content="Apply layout" ItemClick="ApplyPhyloTree" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="Ranking Layout">
<dxb:BarButtonItem Content="Load sample file" ItemClick="LoadRanking" />
<dxb:BarButtonItem Content="Apply layout" ItemClick="ApplyRanking" />
</dxr:RibbonPageGroup>
<dxr:RibbonPageGroup Caption="MDS Layout">
<dxb:BarButtonItem Content="Load sample file" ItemClick="LoadMDS" />
<dxb:BarButtonItem Content="Apply layout" ItemClick="ApplyMDS" />
</dxr:RibbonPageGroup>
</dxr:RibbonPage>
</dxr:RibbonPageCategory>
</dxr:RibbonControl>
<dxdiag:DiagramDesignerControl x:Name="diagramControl" dxb:MergingProperties.ElementMergingBehavior="InternalWithExternal" FitToDrawingMargin="90"
ToolboxVisibility="Compact" SelectedStencils="BasicShapes,ArrowShapes" />
</DockPanel>
</dxr:DXRibbonWindow>
Imports Microsoft.Msagl.Layout.MDS
Imports Microsoft.Msagl.Miscellaneous
Imports Microsoft.Msagl.Core.Routing
Imports Microsoft.Msagl.Core.Layout
Namespace MsaglHelpers
Public Class MDSLayoutCalculator
Implements ILayoutCalculator
Public ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings Implements ILayoutCalculator.LayoutAlgorithmSettings
Get
Return New MdsLayoutSettings() With {
.EdgeRoutingSettings = New EdgeRoutingSettings() With {.EdgeRoutingMode = EdgeRoutingMode.StraightLine}
}
End Get
End Property
Public Sub CalculateLayout(ByVal geometryGraph As GeometryGraph) Implements ILayoutCalculator.CalculateLayout
LayoutHelpers.CalculateLayout(geometryGraph, LayoutAlgorithmSettings, Nothing)
End Sub
End Class
End Namespace
Imports Microsoft.Msagl
Imports Microsoft.Msagl.Core.Geometry.Curves
Imports Microsoft.Msagl.Layout.Layered
Imports Microsoft.Msagl.Miscellaneous
Imports System
Imports Microsoft.Msagl.Core.Layout
Imports Microsoft.Msagl.Core.Routing
Namespace MsaglHelpers
Public Class SugiyamaLayoutCalculator
Implements ILayoutCalculator
Public ReadOnly Property LayoutAlgorithmSettings() As LayoutAlgorithmSettings Implements ILayoutCalculator.LayoutAlgorithmSettings
Get
Return New SugiyamaLayoutSettings() With {
.NodeSeparation = 30, .Transformation = PlaneTransformation.Rotation(Math.PI / 2), .EdgeRoutingSettings = New EdgeRoutingSettings() With {.EdgeRoutingMode = EdgeRoutingMode.SugiyamaSplines}
}
End Get
End Property
Public Sub CalculateLayout(ByVal geometryGraph As GeometryGraph) Implements ILayoutCalculator.CalculateLayout
LayoutHelpers.CalculateLayout(geometryGraph, LayoutAlgorithmSettings, Nothing)
End Sub
End Class
End Namespace
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Routing
Imports Microsoft.Msagl.Core.Routing
Namespace MsaglHelpers
Public NotInheritable Class RoutingHelper
Private Sub New()
End Sub
Public Shared Function GetDiagramConnectorType(ByVal routingMode As EdgeRoutingMode) As ConnectorType
If routingMode = EdgeRoutingMode.StraightLine Then
Return ConnectorType.Straight
End If
If routingMode = EdgeRoutingMode.Spline OrElse routingMode = EdgeRoutingMode.SplineBundling OrElse routingMode = EdgeRoutingMode.SugiyamaSplines Then
Return ConnectorType.Curved
End If
Return ConnectorType.RightAngle
End Function
End Class
End Namespace
using System.Linq;
using DevExpress.Diagram.Core;
using DevExpress.Mvvm.Native;
using System.Collections.Generic;
using DevExpress.Diagram.Core.Layout;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Prototype.Phylo;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Geometry;
namespace MsaglHelpers {
public static class MsaglGeometryGraphHelpers {
public static IEnumerable<PositionInfo<IDiagramItem>> GetGetNodesPositionInfo(GeometryGraph geometryGraph) {
return geometryGraph.Nodes.Select(node => new PositionInfo<IDiagramItem>((IDiagramItem)node.UserData, Converter.MsaglPointToPointConvert(node.BoundingBox.LeftTop)));
}
public static PhyloTree CreatePhyloTrees(Graph<IDiagramItem> graph) {
PhyloTree phyloTree = new PhyloTree();
foreach(var node in graph.Nodes) {
AddNode(phyloTree, node);
}
foreach(var edge in graph.Edges) {
AddPhyloTreeEdge(phyloTree, edge.From, edge.To, edge.Weight);
}
return phyloTree;
}
public static GeometryGraph CreateGeometryGraph(Graph<IDiagramItem> graph) {
GeometryGraph geomGraph = new GeometryGraph();
foreach(var node in graph.Nodes) {
AddNode(geomGraph, node);
}
foreach(var edge in graph.Edges) {
AddEdge(geomGraph, edge.From, edge.To, edge.Weight);
}
return geomGraph;
}
public static Node AddNode(GeometryGraph geometryGraph, IDiagramItem item) {
Node msaglNode = geometryGraph.FindNodeByUserData(item);
if(msaglNode == null) {
msaglNode = new Node(CreateCurve(item), item);
geometryGraph.Nodes.Add(msaglNode);
}
return msaglNode;
}
public static void AddEdge(GeometryGraph geometryGraph, IDiagramItem parentNodeSource, IDiagramItem childNodeSource, double weight) {
geometryGraph.Edges.Add(new Edge(AddNode(geometryGraph, parentNodeSource), AddNode(geometryGraph, childNodeSource)) { Weight = (int)weight });
}
public static void AddPhyloTreeEdge(PhyloTree phyloTree, IDiagramItem parentNodeSource, IDiagramItem childNodeSource, double weight = 1) {
phyloTree.Edges.Add(new PhyloEdge(AddNode(phyloTree, parentNodeSource), AddNode(phyloTree, childNodeSource)) { Weight = (int)weight });
}
public static ICurve CreateCurve(IDiagramItem item) {
return CurveFactory.CreateRectangle(item.ActualSize.Width, item.ActualSize.Height, new Point());
}
}
}
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
using Microsoft.Msagl.Miscellaneous;
using Microsoft.Msagl.Prototype.Ranking;
namespace MsaglHelpers {
public class RankingLayoutCalculator : ILayoutCalculator {
public LayoutAlgorithmSettings LayoutAlgorithmSettings {
get {
return new RankingLayoutSettings() {
NodeSeparation = 30,
EdgeRoutingSettings = { EdgeRoutingMode = EdgeRoutingMode.Rectilinear }
};
}
}
public void CalculateLayout(GeometryGraph geometryGraph) {
var geomGraphComponents = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges);
var settings = LayoutAlgorithmSettings as RankingLayoutSettings;
foreach(var components in geomGraphComponents) {
var layout = new RankingLayout(settings, components);
components.Margins = 30;
layout.Run();
}
Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(geomGraphComponents, settings);
geometryGraph.UpdateBoundingBox();
}
}
}
using DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Layout;
using DevExpress.Diagram.Core.Routing;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
using Microsoft.Msagl.Prototype.Phylo;
using System.Collections.Generic;
namespace MsaglHelpers {
public class PhyloTreeLayout : GraphLayout {
PhyloTree Tree { get; set; }
public PhyloTreeLayout(ILayoutCalculator layoutCalculator) : base(layoutCalculator) { }
public override IEnumerable<PositionInfo<IDiagramItem>> RelayoutGraphNodesPosition(Graph<IDiagramItem> graph) {
Tree = MsaglGeometryGraphHelpers.CreatePhyloTrees(graph);
LayoutCalculator.CalculateLayout(Tree);
return MsaglGeometryGraphHelpers.GetGetNodesPositionInfo(Tree);
}
}
}
using Microsoft.Msagl;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Layout.Layered;
using Microsoft.Msagl.Miscellaneous;
using System;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
namespace MsaglHelpers {
public class SugiyamaLayoutCalculator : ILayoutCalculator {
public LayoutAlgorithmSettings LayoutAlgorithmSettings {
get {
return new SugiyamaLayoutSettings() {
NodeSeparation = 30,
Transformation = PlaneTransformation.Rotation(Math.PI / 2),
EdgeRoutingSettings = { EdgeRoutingMode = EdgeRoutingMode.SugiyamaSplines }
};
}
}
public void CalculateLayout(GeometryGraph geometryGraph) {
LayoutHelpers.CalculateLayout( geometryGraph, LayoutAlgorithmSettings, null);
}
}
}
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Layout.Layered;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Routing;
using System;
using Microsoft.Msagl.Prototype.Phylo;
namespace MsaglHelpers {
public class PhyloTreeLayoutCalculator : ILayoutCalculator {
public LayoutAlgorithmSettings LayoutAlgorithmSettings {
get {
return new SugiyamaLayoutSettings() {
Transformation = PlaneTransformation.Rotation(Math.PI),
EdgeRoutingSettings = {
EdgeRoutingMode = EdgeRoutingMode.StraightLine,
}
};
}
}
public void CalculateLayout(GeometryGraph phyloTree) {
Microsoft.Msagl.Miscellaneous.LayoutHelpers.CalculateLayout(phyloTree, LayoutAlgorithmSettings, null);
}
}
}
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
namespace MsaglHelpers {
public interface ILayoutCalculator {
LayoutAlgorithmSettings LayoutAlgorithmSettings { get; }
void CalculateLayout(GeometryGraph geometryGraph);
}
}
using DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Layout;
using DevExpress.Mvvm.Native;
using MsaglHelpers;
using System.Linq;
using System.Windows;
namespace DXDiagram.CustomLayoutAlgorithms {
/// <summary>
/// Interaction logic for LayoutExampleWindow.xaml
/// </summary>
public partial class LayoutExampleWindow : Window {
readonly GraphLayout layout;
readonly string sourceGraphPath;
public LayoutExampleWindow(GraphLayout layout, string sourceGraphPath) {
InitializeComponent();
this.layout = layout;
this.sourceGraphPath = sourceGraphPath;
Loaded += OnLoaded;
}
void OnLoaded(object sender, RoutedEventArgs e) {
diagramControl.LoadDocument(sourceGraphPath);
}
void RelayoutDiagramItem(object sender, RoutedEventArgs e) {
diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)));
diagramControl.Controller.RegisterRoutingStrategy(layout.GetDiagramConnectorType(), layout.GetDiagramRoutingStrategy());
diagramControl.Items.OfType<IDiagramConnector>().ForEach(connector => { connector.Type = layout.GetDiagramConnectorType(); connector.UpdateRoute(); });
diagramControl.FitToDrawing();
}
}
}
using Microsoft.Msagl.Layout.MDS;
using Microsoft.Msagl.Miscellaneous;
using Microsoft.Msagl.Core.Routing;
using Microsoft.Msagl.Core.Layout;
namespace MsaglHelpers {
public class MDSLayoutCalculator : ILayoutCalculator {
public LayoutAlgorithmSettings LayoutAlgorithmSettings {
get {
return new MdsLayoutSettings() {
EdgeRoutingSettings = {
EdgeRoutingMode = EdgeRoutingMode.StraightLine
}
};
}
}
public void CalculateLayout(GeometryGraph geometryGraph) {
LayoutHelpers.CalculateLayout(geometryGraph, LayoutAlgorithmSettings, null);
}
}
}
using DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Layout;
using DevExpress.Diagram.Core.Routing;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
using System.Collections.Generic;
namespace MsaglHelpers {
public class GraphLayout {
GeometryGraph GeometryGraph { get; set; }
EdgeRoutingMode RoutingMode { get { return LayoutCalculator.LayoutAlgorithmSettings.EdgeRoutingSettings.EdgeRoutingMode; } }
protected ILayoutCalculator LayoutCalculator { get; set; }
public GraphLayout(ILayoutCalculator layoutCalculator) {
this.LayoutCalculator = layoutCalculator;
}
public virtual IEnumerable<PositionInfo<IDiagramItem>> RelayoutGraphNodesPosition(Graph<IDiagramItem> graph) {
GeometryGraph = MsaglGeometryGraphHelpers.CreateGeometryGraph(graph);
LayoutCalculator.CalculateLayout(GeometryGraph);
return MsaglGeometryGraphHelpers.GetGetNodesPositionInfo(GeometryGraph);
}
public ConnectorType GetDiagramConnectorType() {
return RoutingHelper.GetDiagramConnectorType(RoutingMode);
}
}
}
using DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Routing;
using Microsoft.Msagl.Core.Routing;
namespace MsaglHelpers {
public static class RoutingHelper {
public static ConnectorType GetDiagramConnectorType(EdgeRoutingMode routingMode) {
if(routingMode == EdgeRoutingMode.StraightLine) {
return ConnectorType.Straight;
}
if(routingMode == EdgeRoutingMode.Spline || routingMode == EdgeRoutingMode.SplineBundling || routingMode == EdgeRoutingMode.SugiyamaSplines) {
return ConnectorType.Curved;
}
return ConnectorType.RightAngle;
}
}
}
using MsaglHelpers;
using DevExpress.Xpf.Ribbon;
using DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Layout;
using System.Linq;
using DevExpress.Mvvm.Native;
using DevExpress.Xpf.Core;
using System;
namespace DXDiagram.CustomLayoutAlgorithms {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : DXRibbonWindow {
public MainWindow() {
InitializeComponent();
}
void LoadSugiyama(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
diagramControl.LoadDocument("Data/SugiyamaLayout.xml");
}
void ApplySugiyama(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
ApplyLayout(new GraphLayout(new SugiyamaLayoutCalculator()));
}
void LoadDisconnectedGraphs(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
diagramControl.LoadDocument("Data/DisconnectedGraphs.xml");
}
void ApplyDisconnectedGraphs(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
ApplyLayout(new GraphLayout(new DisconnectedGraphsLayoutCalculator()));
}
void LoadPhyloTree(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
diagramControl.LoadDocument("Data/PhyloTree.xml");
}
void ApplyPhyloTree(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
ApplyLayout(new PhyloTreeLayout(new PhyloTreeLayoutCalculator()));
}
void LoadRanking(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
diagramControl.LoadDocument("Data/RankingLayout.xml");
}
void ApplyRanking(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
ApplyLayout(new GraphLayout(new RankingLayoutCalculator()));
}
void LoadMDS(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
diagramControl.LoadDocument("Data/MDSLayout.xml");
}
void ApplyMDS(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e) {
ApplyLayout(new GraphLayout(new MDSLayoutCalculator()));
}
void ApplyLayout(GraphLayout layout) {
try {
diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)));
diagramControl.Items.OfType<IDiagramConnector>().ForEach(connector => { connector.Type = layout.GetDiagramConnectorType(); connector.UpdateRoute(); });
diagramControl.FitToDrawing();
} catch(Exception e) {
DXMessageBox.Show(string.Format("Error message: '{0}'", e.Message), "Error has been occurred");
}
}
}
}
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Layout.Layered;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Routing;
using System;
namespace MsaglHelpers {
public class DisconnectedGraphsLayoutCalculator : ILayoutCalculator {
public LayoutAlgorithmSettings LayoutAlgorithmSettings {
get {
return new SugiyamaLayoutSettings() {
Transformation = PlaneTransformation.Rotation(Math.PI),
EdgeRoutingSettings = {
EdgeRoutingMode = EdgeRoutingMode.StraightLine,
}
};
}
}
public void CalculateLayout(GeometryGraph geometryGraph) {
var geomGraphComponents = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges);
var settings = LayoutAlgorithmSettings as SugiyamaLayoutSettings;
foreach(var components in geomGraphComponents) {
var layout = new LayeredLayout(components, settings);
components.Margins = 100;
layout.Run();
}
Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(geomGraphComponents, settings);
geometryGraph.UpdateBoundingBox();
}
}
}
using MsaglPoint = Microsoft.Msagl.Core.Geometry.Point;
using System.Windows;
namespace MsaglHelpers {
public static class Converter {
public static Point MsaglPointToPointConvert(this MsaglPoint msaglPoint) {
return new Point(msaglPoint.X, msaglPoint.Y);
}
}
}