Skip to main content
A newer version of this page is available. .
All docs
V21.1

SankeyDiagramControl.LayoutAlgorithm Property

Specifies Sankey layout algorithm settings.

Namespace: DevExpress.Xpf.Charts.Sankey

Assembly: DevExpress.Xpf.Charts.v21.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

<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>
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, System.Drawing.Rectangle 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 System.Drawing.Rectangle(bounds.Left + node.LevelIndex * spaceBetweenLevels, bounds.Top + nodeCount * spaceBetweenNodes, nodeWidth, nodeHeight);
        }
    }
}
See Also