Skip to main content
All docs
V24.2

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

Calculates node bounds.

Namespace: DevExpress.Xpf.Charts.Sankey

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

NuGet Package: DevExpress.Wpf.Charts

Declaration

public virtual void CalculateNodeBounds(
    IEnumerable<ISankeyNodeLayoutItem> nodes,
    DXRectangle bounds
)

Parameters

Name Type Description
nodes IEnumerable<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:

Custom Sankey layout algorithm

<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>
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);
            }
        }
    }
}
See Also