wpf-devexpress-dot-xpf-dot-charts-dot-sankey-dot-sankeylayoutalgorithmbase-dot-calculatenodebounds-x28-ienumerable-isankeynodelayoutitem-dxrectangle-x29.md
Calculates node bounds.
Namespace : DevExpress.Xpf.Charts.Sankey
Assembly : DevExpress.Xpf.Charts.v25.2.dll
NuGet Package : DevExpress.Wpf.Charts
public virtual void CalculateNodeBounds(
IEnumerable<ISankeyNodeLayoutItem> nodes,
DXRectangle bounds
)
Public Overridable Sub CalculateNodeBounds(
nodes As IEnumerable(Of ISankeyNodeLayoutItem),
bounds As DXRectangle
)
| Name | Type | Description |
|---|---|---|
| nodes | IEnumerable<ISankeyNodeLayoutItem> |
The node collection.
| | bounds | DevExpress.Utils.DXRectangle |
Sankey diagram bounds without titles and paddings.
|
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);
}
}
}
}
Imports DevExpress.Charts.Sankey
Imports DevExpress.Xpf.Charts
Imports DevExpress.Xpf.Charts.Sankey
Imports DevExpress.Xpf.Core
Imports System.Collections.Generic
Imports System.Linq
Namespace CustomNodeLayout
'...
Public Class SankeyViewModel
Public ReadOnly Property Data As List(Of SankeyItem)
Get
Return GetData()
End Get
End Property
Public Function GetData() As List(Of SankeyItem)
Dim data As List(Of SankeyItem) = New List(Of SankeyItem) From {
New SankeyItem With {
.Source = "France",
.Target = "UK",
.Value = 53
},
New SankeyItem With {
.Source = "Australia",
.Target = "UK",
.Value = 72
},
New SankeyItem With {
.Source = "France",
.Target = "Canada",
.Value = 81
},
New SankeyItem With {
.Source = "China",
.Target = "Canada",
.Value = 96
},
New SankeyItem With {
.Source = "UK",
.Target = "France",
.Value = 61
},
New SankeyItem With {
.Source = "Canada",
.Target = "France",
.Value = 89
}
}
Return data
End Function
End Class
Public Class SankeyItem
Public Property Source As String
Public Property Target As String
Public Property Value As Double
End Class
Public Class MyLayoutAlgorithm
Inherits SankeyLayoutAlgorithmBase
Private Sub SpreadLevelIndex(ByVal node As ISankeyNodeLayoutItem, ByVal Optional startingLevelIndex As Integer = 0)
node.LevelIndex = startingLevelIndex
If node.OutputLinks Is Nothing Then Return
For Each outputLink In node.OutputLinks
SpreadLevelIndex(outputLink.Target, startingLevelIndex + 1)
Next
End Sub
Protected Overrides Function CreateObject() As ChartDependencyObject
Return New MyLayoutAlgorithm()
End Function
Public Overrides Sub CalculateNodeBounds(ByVal nodes As IEnumerable(Of ISankeyNodeLayoutItem), ByVal bounds As DevExpress.Utils.DXRectangle)
For Each node In nodes
If node.InputLinks Is Nothing OrElse node.InputLinks.Count = 0 Then Me.SpreadLevelIndex(node)
Next
Dim nodeWidth As Integer = bounds.Width / 10
Dim nodeHeight As Integer = bounds.Height / 10
Dim levelCount As Integer = nodes.Max(Function(node) node.LevelIndex) + 1
Dim spaceBetweenLevels As Integer = (bounds.Width - nodeWidth) / (levelCount - 1)
Dim maxNodeCountInALevel As Integer = nodes.GroupBy(Function(node) node.LevelIndex).Max(Function(group) group.Count())
Dim spaceBetweenNodes As Integer = (bounds.Height - nodeHeight) / (maxNodeCountInALevel - 1)
Dim levelNodeCountPairs As Dictionary(Of Integer, Integer) = New Dictionary(Of Integer, Integer)()
Dim nodeCount As Integer = Nothing
For Each node In nodes
If Not levelNodeCountPairs.TryGetValue(node.LevelIndex, nodeCount) Then levelNodeCountPairs.Add(node.LevelIndex, 0)
levelNodeCountPairs(node.LevelIndex) += 1
node.Bounds = New DevExpress.Utils.DXRectangle(bounds.Left + node.LevelIndex * spaceBetweenLevels, bounds.Top + nodeCount * spaceBetweenNodes, nodeWidth, nodeHeight)
Next
End Sub
End Class
End Namespace
See Also
SankeyLayoutAlgorithmBase Class