Skip to main content
All docs
V25.1
  • DevExpress v25.1 Update — Your Feedback Matters

    Our What's New in v25.1 webpage includes product-specific surveys. Your response to our survey questions will help us measure product satisfaction for features released in this major update and help us refine our plans for our next major release.

    Take the survey Not interested

    SankeyDiagramControl.LayoutAlgorithm Property

    Specifies Sankey layout algorithm settings.

    Namespace: DevExpress.Xpf.Charts.Sankey

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

    NuGet Package: DevExpress.Wpf.Charts

    #Declaration

    public SankeyLayoutAlgorithmBase LayoutAlgorithm { get; set; }

    #Property Value

    Type Description
    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:

    Sankey node alignment - Top

    <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:

    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