Back to Devexpress

SankeyLayoutAlgorithmBase.CalculateNodeBounds(IEnumerable<ISankeyNodeLayoutItem>, DXRectangle) Method

wpf-devexpress-dot-xpf-dot-charts-dot-sankey-dot-sankeylayoutalgorithmbase-dot-calculatenodebounds-x28-ienumerable-isankeynodelayoutitem-dxrectangle-x29.md

latest9.9 KB
Original Source

SankeyLayoutAlgorithmBase.CalculateNodeBounds(IEnumerable<ISankeyNodeLayoutItem>, DXRectangle) Method

Calculates node bounds.

Namespace : DevExpress.Xpf.Charts.Sankey

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

NuGet Package : DevExpress.Wpf.Charts

Declaration

csharp
public virtual void CalculateNodeBounds(
    IEnumerable<ISankeyNodeLayoutItem> nodes,
    DXRectangle bounds
)
vb
Public Overridable Sub CalculateNodeBounds(
    nodes As IEnumerable(Of ISankeyNodeLayoutItem),
    bounds As DXRectangle
)

Parameters

NameTypeDescription
nodesIEnumerable<ISankeyNodeLayoutItem>

The node collection.

| | bounds | DevExpress.Utils.DXRectangle |

Sankey diagram bounds without titles and paddings.

|

Remarks

To implement a custom layout, create a descendant of the SankeyLayoutAlgorithmBase class. Override the 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

SankeyLayoutAlgorithmBase Class

SankeyLayoutAlgorithmBase Members

DevExpress.Xpf.Charts.Sankey Namespace