Back to Devexpress

SankeyDiagramControl.LayoutAlgorithm Property

wpf-devexpress-dot-xpf-dot-charts-dot-sankey-dot-sankeydiagramcontrol-3a32caba.md

latest10.6 KB
Original Source

SankeyDiagramControl.LayoutAlgorithm Property

Specifies Sankey layout algorithm settings.

Namespace : DevExpress.Xpf.Charts.Sankey

Assembly : DevExpress.Xpf.Charts.v25.2.dll

NuGet Package : DevExpress.Wpf.Charts

Declaration

csharp
public SankeyLayoutAlgorithmBase LayoutAlgorithm { get; set; }
vb
Public Property LayoutAlgorithm As SankeyLayoutAlgorithmBase

Property Value

TypeDescription
SankeyLayoutAlgorithmBase

Contains Sankey layout algorithm settings.

|

Remarks

Linear Layout Algorithm

You can use the predefined SankeyLinearLayoutAlgorithm. The SankeyLinearLayoutAlgorithm.NodeAlignment property specifies the node alignment.

The following example aligns nodes to the top of the SankeyDiagramControl:

xaml
<dxsa:SankeyDiagramControl x:Name="sankeyDiagramControl1" DataSource="{Binding Data}" 
                           SourceDataMember="Source" 
                           TargetDataMember="Target" 
                           WeightDataMember="Value">
    <!--...-->
    <dxsa:SankeyDiagramControl.LayoutAlgorithm>
        <dxsa:SankeyLinearLayoutAlgorithm NodeAlignment="Far"/>
    </dxsa:SankeyDiagramControl.LayoutAlgorithm>
</dxsa:SankeyDiagramControl>

Custom Layout Algorithm

To implement a custom layout, create a descendant of the SankeyLayoutAlgorithmBase class. Override the SankeyLayoutAlgorithmBase.CalculateNodeBounds method to specify node bounds:

xaml
<dx:ThemedWindow 
    x:Class="CustomNodeLayout.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxsa="http://schemas.devexpress.com/winfx/2008/xaml/sankey"
    xmlns:local="clr-namespace:CustomNodeLayout"
    Title="MainWindow" Height="300" Width="400">
    <Grid>
        <dxsa:SankeyDiagramControl x:Name="sankeyDiagramControl1" 
                                   DataSource="{Binding Data}" 
                                   SourceDataMember="Source" 
                                   TargetDataMember="Target" 
                                   WeightDataMember="Value">
            <dxsa:SankeyDiagramControl.DataContext>
                <local:SankeyViewModel/>
            </dxsa:SankeyDiagramControl.DataContext>
            <dxsa:SankeyDiagramControl.LayoutAlgorithm>
                <local:MyLayoutAlgorithm/>
            </dxsa:SankeyDiagramControl.LayoutAlgorithm>
        </dxsa:SankeyDiagramControl>
    </Grid>
</dx:ThemedWindow>
csharp
using DevExpress.Charts.Sankey;
using DevExpress.Xpf.Charts;
using DevExpress.Xpf.Charts.Sankey;
using DevExpress.Xpf.Core;
using System.Collections.Generic;
using System.Linq;
namespace CustomNodeLayout {
    //...
    public class SankeyViewModel {
        public List<SankeyItem> Data {
            get { return GetData(); }
        }
        public List<SankeyItem> GetData() {
            List<SankeyItem> data = new List<SankeyItem>{
            new SankeyItem { Source = "France", Target = "UK", Value = 53 },
            new SankeyItem { Source = "Australia", Target = "UK", Value = 72 },
            new SankeyItem { Source = "France", Target = "Canada", Value = 81 },
            new SankeyItem { Source = "China", Target = "Canada", Value = 96 },
            new SankeyItem { Source = "UK", Target = "France", Value = 61 },
            new SankeyItem { Source = "Canada", Target = "France", Value = 89 }
        };
            return data;
        }
    }
    public class SankeyItem {
        public string Source { get; set; }
        public string Target { get; set; }
        public double Value { get; set; }
    }
    public class MyLayoutAlgorithm : SankeyLayoutAlgorithmBase {
        void SpreadLevelIndex(ISankeyNodeLayoutItem node, int startingLevelIndex = 0) {
            node.LevelIndex = startingLevelIndex;
            if (node.OutputLinks == null)
                return;
            foreach (var outputLink in node.OutputLinks)
                SpreadLevelIndex(outputLink.Target, startingLevelIndex + 1);
        }
        protected override ChartDependencyObject CreateObject() => new MyLayoutAlgorithm();
        public override void CalculateNodeBounds(IEnumerable<ISankeyNodeLayoutItem> nodes, DevExpress.Utils.DXRectangle bounds) {
            foreach (var node in nodes)
                if (node.InputLinks == null || node.InputLinks.Count == 0)
                    SpreadLevelIndex(node);
            int nodeWidth = bounds.Width / 10;
            int nodeHeight = bounds.Height / 10;
            int levelCount = nodes.Max(node => node.LevelIndex) + 1;
            int spaceBetweenLevels = (bounds.Width - nodeWidth) / (levelCount - 1);
            int maxNodeCountInALevel = nodes.GroupBy(node => node.LevelIndex).Max(group => group.Count());
            int spaceBetweenNodes = (bounds.Height - nodeHeight) / (maxNodeCountInALevel - 1);
            Dictionary<int, int> levelNodeCountPairs = new Dictionary<int, int>();
            foreach (var node in nodes) {
                if (!levelNodeCountPairs.TryGetValue(node.LevelIndex, out int nodeCount))
                    levelNodeCountPairs.Add(node.LevelIndex, 0);
                levelNodeCountPairs[node.LevelIndex]++;
                node.Bounds = new DevExpress.Utils.DXRectangle(bounds.Left + node.LevelIndex * spaceBetweenLevels, bounds.Top + nodeCount * spaceBetweenNodes, nodeWidth, nodeHeight);
            }
        }
    }
}
vb
Imports DevExpress.Charts.Sankey
Imports DevExpress.Xpf.Charts
Imports DevExpress.Xpf.Charts.Sankey
Imports DevExpress.Xpf.Core
Imports System.Collections.Generic
Imports System.Linq

Namespace CustomNodeLayout
    '...
    Public Class SankeyViewModel
        Public ReadOnly Property Data As List(Of SankeyItem)
            Get
                Return GetData()
            End Get
        End Property
        Public Function GetData() As List(Of SankeyItem)
            Dim data As List(Of SankeyItem) = New List(Of SankeyItem) From {
            New SankeyItem With {
                    .Source = "France",
                    .Target = "UK",
                    .Value = 53
                },
            New SankeyItem With {
                    .Source = "Australia",
                    .Target = "UK",
                    .Value = 72
                },
            New SankeyItem With {
                    .Source = "France",
                    .Target = "Canada",
                    .Value = 81
                },
            New SankeyItem With {
                    .Source = "China",
                    .Target = "Canada",
                    .Value = 96
                },
            New SankeyItem With {
                    .Source = "UK",
                    .Target = "France",
                    .Value = 61
                },
            New SankeyItem With {
                    .Source = "Canada",
                    .Target = "France",
                    .Value = 89
                }
        }
            Return data
        End Function
    End Class
    Public Class SankeyItem
        Public Property Source As String
        Public Property Target As String
        Public Property Value As Double
    End Class
    Public Class MyLayoutAlgorithm
        Inherits SankeyLayoutAlgorithmBase
        Private Sub SpreadLevelIndex(ByVal node As ISankeyNodeLayoutItem, ByVal Optional startingLevelIndex As Integer = 0)
            node.LevelIndex = startingLevelIndex
            If node.OutputLinks Is Nothing Then Return
            For Each outputLink In node.OutputLinks
                SpreadLevelIndex(outputLink.Target, startingLevelIndex + 1)
            Next
        End Sub
        Protected Overrides Function CreateObject() As ChartDependencyObject
            Return New MyLayoutAlgorithm()
        End Function
        Public Overrides Sub CalculateNodeBounds(ByVal nodes As IEnumerable(Of ISankeyNodeLayoutItem), ByVal bounds As DevExpress.Utils.DXRectangle)
            For Each node In nodes
                If node.InputLinks Is Nothing OrElse node.InputLinks.Count = 0 Then Me.SpreadLevelIndex(node)
            Next
            Dim nodeWidth As Integer = bounds.Width / 10
            Dim nodeHeight As Integer = bounds.Height / 10
            Dim levelCount As Integer = nodes.Max(Function(node) node.LevelIndex) + 1
            Dim spaceBetweenLevels As Integer = (bounds.Width - nodeWidth) / (levelCount - 1)
            Dim maxNodeCountInALevel As Integer = nodes.GroupBy(Function(node) node.LevelIndex).Max(Function(group) group.Count())
            Dim spaceBetweenNodes As Integer = (bounds.Height - nodeHeight) / (maxNodeCountInALevel - 1)
            Dim levelNodeCountPairs As Dictionary(Of Integer, Integer) = New Dictionary(Of Integer, Integer)()
            Dim nodeCount As Integer = Nothing
            For Each node In nodes
                If Not levelNodeCountPairs.TryGetValue(node.LevelIndex, nodeCount) Then levelNodeCountPairs.Add(node.LevelIndex, 0)
                levelNodeCountPairs(node.LevelIndex) += 1
                node.Bounds = New DevExpress.Utils.DXRectangle(bounds.Left + node.LevelIndex * spaceBetweenLevels, bounds.Top + nodeCount * spaceBetweenNodes, nodeWidth, nodeHeight)
            Next
        End Sub
    End Class
End Namespace

See Also

SankeyDiagramControl Class

SankeyDiagramControl Members

DevExpress.Xpf.Charts.Sankey Namespace