Back to Devexpress

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

windowsforms-118079-controls-and-libraries-diagrams-examples-how-to-use-custom-graph-layout-algorithms-to-arrange-shapes-in-diagramcontrol.md

latest13.9 KB
Original Source

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

  • Nov 13, 2018
  • 5 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 extension 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 library to DiagramControl.

View Example

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 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 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 DevExpress.Diagram.Core;
using DevExpress.Diagram.Core.Layout;
using DevExpress.XtraDiagram.Utils;
using DevExpress.XtraEditors;
using MsaglHelpers;
using System;
using System.Linq;
using System.Windows.Forms;

namespace XtraDiagram.CustomLayoutAlgorithms {
    public partial class CustomLayoutForm : XtraForm {
        public CustomLayoutForm() {
            InitializeComponent();
        }
        void LoadSugiyama(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            diagramControl.LoadDocument("Data/SugiyamaLayout.xml");
        }

        void ApplySugiyama(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            ApplyLayout(new GraphLayout(new SugiyamaLayoutCalculator()));
        }
        void LoadDisconnectedGraphs(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            diagramControl.LoadDocument("Data/DisconnectedGraphs.xml");
        }

        void ApplyDisconnectedGraphs(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            ApplyLayout(new GraphLayout(new DisconnectedGraphsLayoutCalculator()));
        }

        void LoadPhyloTree(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            diagramControl.LoadDocument("Data/PhyloTree.xml");
        }

        void ApplyPhyloTree(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            ApplyLayout(new PhyloTreeLayout(new PhyloTreeLayoutCalculator()));
        }
        void LoadRanking(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            diagramControl.LoadDocument("Data/RankingLayout.xml");
        }

        void ApplyRanking(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            ApplyLayout(new GraphLayout(new RankingLayoutCalculator()));
        }
        void LoadMDS(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            diagramControl.LoadDocument("Data/MDSLayout.xml");
        }

        void ApplyMDS(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
            ApplyLayout(new GraphLayout(new MDSLayoutCalculator()));
        }

        void ApplyLayout(GraphLayout layout) {
            try {
                diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)));

                foreach(var connector in diagramControl.Items.OfType<IDiagramConnector>()) {
                    connector.Type = layout.GetDiagramConnectorType();
                    connector.UpdateRoute();
                };
                diagramControl.FitToDrawing();
            } catch(Exception e) {
                XtraMessageBox.Show(string.Format("Error message: '{0}'", e.Message), "Error has been occurred");
            }
        }

    }
}
vb
Imports DevExpress.Diagram.Core
Imports DevExpress.Diagram.Core.Layout
Imports DevExpress.XtraDiagram.Utils
Imports DevExpress.XtraEditors
Imports MsaglHelpers
Imports System
Imports System.Linq
Imports System.Windows.Forms

Namespace XtraDiagram.CustomLayoutAlgorithms
    Partial Public Class CustomLayoutForm
        Inherits XtraForm

        Public Sub New()
            InitializeComponent()
        End Sub
        Private Sub LoadSugiyama(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem9.ItemClick
            diagramControl.LoadDocument("Data/SugiyamaLayout.xml")
        End Sub

        Private Sub ApplySugiyama(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem10.ItemClick
            ApplyLayout(New GraphLayout(New SugiyamaLayoutCalculator()))
        End Sub
        Private Sub LoadDisconnectedGraphs(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem1.ItemClick
            diagramControl.LoadDocument("Data/DisconnectedGraphs.xml")
        End Sub

        Private Sub ApplyDisconnectedGraphs(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem2.ItemClick
            ApplyLayout(New GraphLayout(New DisconnectedGraphsLayoutCalculator()))
        End Sub

        Private Sub LoadPhyloTree(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem3.ItemClick
            diagramControl.LoadDocument("Data/PhyloTree.xml")
        End Sub

        Private Sub ApplyPhyloTree(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem4.ItemClick
            ApplyLayout(New PhyloTreeLayout(New PhyloTreeLayoutCalculator()))
        End Sub
        Private Sub LoadRanking(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem5.ItemClick
            diagramControl.LoadDocument("Data/RankingLayout.xml")
        End Sub

        Private Sub ApplyRanking(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem6.ItemClick
            ApplyLayout(New GraphLayout(New RankingLayoutCalculator()))
        End Sub
        Private Sub LoadMDS(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem7.ItemClick
            diagramControl.LoadDocument("Data/MDSLayout.xml")
        End Sub

        Private Sub ApplyMDS(ByVal sender As Object, ByVal e As DevExpress.XtraBars.ItemClickEventArgs) Handles barButtonItem8.ItemClick
            ApplyLayout(New GraphLayout(New MDSLayoutCalculator()))
        End Sub

        Private Sub ApplyLayout(ByVal layout As GraphLayout)
            Try
                diagramControl.RelayoutDiagramItems(layout.RelayoutGraphNodesPosition(GraphOperations.GetDiagramGraph(diagramControl)))

                For Each connector In diagramControl.Items.OfType(Of IDiagramConnector)()
                    connector.Type = layout.GetDiagramConnectorType()
                    connector.UpdateRoute()
                Next connector
                diagramControl.FitToDrawing()
            Catch e As Exception
                XtraMessageBox.Show(String.Format("Error message: '{0}'", e.Message), "Error has been occurred")
            End Try
        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 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 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
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