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

SankeyLayoutAlgorithmBase Class

The base class for classes that implement Sankey layout algorithms.

Namespace: DevExpress.Xpf.Charts.Sankey

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

<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
xref:DevExpress.Xpf.Charts.Sankey.SankeyLinearLayoutAlgorithm