Back to Devexpress

How to Use Custom Graph Layout Algorithms to Arrange Shapes in DiagramControl

wpf-116968-controls-and-libraries-diagram-control-examples-how-to-use-custom-graph-layout-algorithms-to-arrange-shapes-in-diagramcontrol.md

latest39.5 KB
Original Source

How to Use Custom Graph Layout Algorithms to Arrange Shapes in DiagramControl

  • Jun 07, 2019
  • 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 PositionInfo object 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.

View Example

vb
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
vb
Imports MsaglPoint = Microsoft.Msagl.Core.Geometry.Point
Imports System.Windows

Namespace MsaglHelpers
    Public Module Converter
         _
        Public Function MsaglPointToPointConvert(ByVal msaglPoint As MsaglPoint) As Point
            Return New Point(msaglPoint.X, msaglPoint.Y)
        End Function
    End Module
End Namespace
vb
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
vb
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
vb
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
vb
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
vb
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
vb
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
xaml
<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>
vb
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
vb
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
xaml
<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>
vb
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
vb
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
vb
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
csharp
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());
        }
    }
}
csharp
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();
        }
    }
}
csharp
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);
        }
    }
}
csharp
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);
        }
    }
}
csharp
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);
        }
    }
}
csharp
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;

namespace MsaglHelpers {
    public interface ILayoutCalculator {
        LayoutAlgorithmSettings LayoutAlgorithmSettings { get; }
        void CalculateLayout(GeometryGraph geometryGraph);
    }
}
csharp
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();
        }
    }
}
csharp
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);
        }
    }
}
csharp
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);
        }
    }
}
csharp
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;
        }
    }
}
csharp
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");
            }
        }
    }
}
csharp
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();
        }
    }
}
csharp
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);
        }
    }
}