Skip to main content
All docs
V25.1
  • SankeyLayoutAlgorithmBase Class

    The base class for classes that implement Sankey layout algorithms.

    Namespace: DevExpress.Xpf.Charts.Sankey

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

    NuGet Package: DevExpress.Wpf.Charts

    Declaration

    public abstract class SankeyLayoutAlgorithmBase :
        ChartDependencyObject,
        ISankeyLayoutAlgorithm

    The following members return SankeyLayoutAlgorithmBase objects:

    Remarks

    To implement a custom layout, create a descendant of the SankeyLayoutAlgorithmBase class. Override the SankeyLayoutAlgorithmBase.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
    xref:DevExpress.Xpf.Charts.Sankey.SankeyLinearLayoutAlgorithm