SankeyLayoutAlgorithmBase.CalculateNodeBounds(IEnumerable<ISankeyNodeLayoutItem>, DXRectangle) Method
Calculates node bounds.
Namespace: DevExpress.Xpf.Charts.Sankey
Assembly: DevExpress.Xpf.Charts.v24.1.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:
<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