Back to Devexpress

SankeyDiagramControl Class

windowsforms-devexpress-dot-xtracharts-dot-sankey.md

latest30.4 KB
Original Source

SankeyDiagramControl Class

Displays a multilevel Sankey diagram.

Namespace : DevExpress.XtraCharts.Sankey

Assembly : DevExpress.XtraCharts.v25.2.UI.dll

NuGet Package : DevExpress.Win.Charts

Declaration

csharp
[DXLicenseWinForms]
[SerializationContext(typeof(SankeySerializationContext))]
public class SankeyDiagramControl :
    Control,
    ISankeyContainer,
    IToolTipControlClient,
    ISupportLookAndFeel,
    ISankeyRenderProvider,
    IChartRenderProvider,
    IPrintable,
    IBasePrintable,
    IPrintingTarget,
    IComponentPrintable,
    IComponentExportable,
    IScaleDpiProvider
vb
<SerializationContext(GetType(SankeySerializationContext))>
<DXLicenseWinForms>
Public Class SankeyDiagramControl
    Inherits Control
    Implements ISankeyContainer,
               IToolTipControlClient,
               ISupportLookAndFeel,
               ISankeyRenderProvider,
               IChartRenderProvider,
               IPrintable,
               IBasePrintable,
               IPrintingTarget,
               IComponentPrintable,
               IComponentExportable,
               IScaleDpiProvider

Remarks

A Sankey diagram depicts transfers or flows between entities (also called nodes) in a system. This diagram can help locate the most important contributions to a flow.

The following image displays diagram elements:

Each link connects the source and target nodes and has an assigned value - weight. The width of link is proportional to its weight.

Demos

The Sankey Diagram control ships with a set of demo applications. To try out the control, run the Demo Center.

Add to the Project

Drag and drop the SankeyDiagramControl component from the Toolbox to the form to add a Sankey diagram to the project.

This adds references to the following assemblies to the project:

  • DevExpress.Charts.v25.2.Core.dll
  • DevExpress.Data.Desktop.v25.2.dll
  • DevExpress.Data.v25.2.dll
  • DevExpress.Printing.v25.2.Core.dll
  • DevExpress.XtraPrinting.v25.2.dll
  • DevExpress.Utils.v25.2.dll
  • DevExpress.Utils.v25.2.UI.dll
  • DevExpress.XtraBars.v25.2.dll
  • DevExpress.XtraCharts.v25.2.dll
  • DevExpress.XtraCharts.v25.2.UI.dll
  • DevExpress.XtraCharts.v25.2.Wizard.dll
  • DevExpress.XtraEditors.v25.2.dll

Bind to Data

Use the DataSource property to bind the control to a data source. You can assign this property an object that implements any of the following interfaces: IList, IListSource, or IBindingList.

Then, specify the names of data members that store data for source nodes, target nodes and weights:

  • SourceDataMember - Specifies the name of a data member that contains source node labels.

  • TargetDataMember - Specifies the name of a data member that contains target node labels.

  • WeightDataMember (Optional) - Specifies the name of a data member that contains link weights. If the WeightDataMember property is not specified, weights are equal to 1.

  • C#

  • VB.NET

csharp
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using DevExpress.XtraCharts.Sankey;
namespace SankeySample {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e) {
            sankeyDiagramControl1.DataSource = GetSankeyItems();
            sankeyDiagramControl1.SourceDataMember = "Source";
            sankeyDiagramControl1.TargetDataMember = "Target";            
            sankeyDiagramControl1.WeightDataMember = "Value";
            sankeyDiagramControl1.Titles.Add(new SankeyTitle { Text = "Export/Import" });
        }
        List<SankeyItem> GetSankeyItems() {
            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; }
    }
}
vb
Imports System
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports DevExpress.XtraCharts.Sankey

Namespace SankeySample
    Public Partial Class Form1
        Inherits Form

        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
            sankeyDiagramControl1.DataSource = GetSankeyItems()
            sankeyDiagramControl1.SourceDataMember = "Source"
            sankeyDiagramControl1.TargetDataMember = "Target"            
            sankeyDiagramControl1.WeightDataMember = "Value"
            sankeyDiagramControl1.Titles.Add(New SankeyTitle With {
                .Text = "Export/Import"
            })
        End Sub

        Private Function GetSankeyItems() 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
End Namespace

Result:

Customize Nodes

Use the SankeyDiagramControl.NodeLabel property to access node label settings. The following options allow you to configure text for node labels:

csharp
sankeyDiagramControl1.NodeLabel.TextOrientation = TextOrientation.TopToBottom;
sankeyDiagramControl1.NodeLabel.MaxWidth = 200;
sankeyDiagramControl1.NodeLabel.MaxLineCount = 1;
sankeyDiagramControl1.NodeLabel.TextAlignment = StringAlignment.Center;
sankeyDiagramControl1.NodeLabel.DXFont = new DevExpress.Drawing.DXFont("Tahoma", 10);
vb
sankeyDiagramControl1.NodeLabel.TextOrientation = TextOrientation.TopToBottom
sankeyDiagramControl1.NodeLabel.MaxWidth = 200
sankeyDiagramControl1.NodeLabel.MaxLineCount = 1
sankeyDiagramControl1.NodeLabel.TextAlignment = StringAlignment.Center
sankeyDiagramControl1.NodeLabel.DXFont = New DevExpress.Drawing.DXFont("Tahoma", 10)

You can also handle the CustomizeNode event to access a specific node and customize it based on a certain condition:

csharp
sankeyDiagramControl1.CustomizeNode += OnCustomizeNode;
//...
private void OnCustomizeNode(object sender, CustomizeSankeyNodeEventArgs e) {
    if (e.Label.Text == "France" && e.Node.Level == 0) {
        e.Label.DXFont = new DevExpress.Drawing.DXFont("Tahoma", 16);
        e.Label.Text = "France (Source)";
    }
}
vb
Private Sub OnCustomizeNode(ByVal sender As Object, ByVal e As CustomizeSankeyNodeEventArgs)
    If e.Label.Text Is "France" AndAlso e.Node.Level = 0 Then
        e.Label.DXFont = New DevExpress.Drawing.DXFont("Tahoma", 16)
        e.Label.Text = "France (Source)"
    End If
End Sub

Sort Nodes

The control automatically arranges nodes based on underlying data. If you wish to rearrange nodes or specify a custom sort order, create a class that implements IComparer<SankeyNode>. Then, assign an object of this class to the NodeComparer property.

The following code arranges nodes in descending order of their TotalWeight values:

csharp
private void Form1_Load(object sender, EventArgs e) {
    sankeyDiagramControl1.NodeComparer = new MyNodeComparer();
}

public class MyNodeComparer : IComparer<SankeyNode> {
    public int Compare(SankeyNode x, SankeyNode y) {
        return y.TotalWeight.CompareTo(x.TotalWeight);
    }
}
vb
Friend Class SurroundingClass
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
        sankeyDiagramControl1.NodeComparer = New MyNodeComparer()
    End Sub

    Public Class MyNodeComparer
        Inherits IComparer(Of SankeyNode)

        Public Function Compare(ByVal x As SankeyNode, ByVal y As SankeyNode) As Integer
            Return y.TotalWeight.CompareTo(x.TotalWeight)
        End Function
    End Class
End Class

Define Node Layout Algorithm

You can use the predefined SankeyLinearLayoutAlgorithm to change node position. The SankeyLinearLayoutAlgorithm.NodeAlignment property specifies the node alignment.

The following example aligns nodes to the bottom of the Sankey diagram:

csharp
SankeyLinearLayoutAlgorithm layoutAlgorithm = (SankeyLinearLayoutAlgorithm)sankey.LayoutAlgorithm;
layoutAlgorithm.NodeAlignment = SankeyNodeAlignment.Near;
vb
Dim layoutAlgorithm As SankeyLinearLayoutAlgorithm = CType(sankey.LayoutAlgorithm, SankeyLinearLayoutAlgorithm)
layoutAlgorithm.NodeAlignment = SankeyNodeAlignment.Near

You can also implement a custom layout. See the following help topic for more information: SankeyLayoutAlgorithmBase.

The Sankey diagram control highlights a node or a link when the mouse cursor hovers over this element. A node is highlighted with the related links. The image below shows the highlighted Canada node:

Use the EnableHighlighting property to disable highlighting:

csharp
private void Form1_Load(object sender, EventArgs e) {
  sankeyDiagramControl1.EnableHighlighting = false;
}
vb
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
    sankeyDiagramControl1.EnableHighlighting = False
End Sub

The SelectionMode property defines whether a user can select links and nodes. Nodes are selected with the related links. The image below shows the selected Canada node:

When SelectionMode is None (default), a user cannot select Sankey elements. Set this property to Single, Multiple, or Extended to allow users to select Sankey nodes and links.
The following code allows a user to select multiple Sankey elements:

csharp
private void Form1_Load(object sender, EventArgs e) {
    sankeyDiagramControl1.SelectionMode = SankeySelectionMode.Multiple;
}
vb
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
    sankeyDiagramControl1.SelectionMode = SankeySelectionMode.Multiple
End Sub

When a user selects a Sankey node or link, the SelectedItemsChanging event is raised. You can use this event to conditionally cancel the Sankey element selection. The following example prevents the France node selection:

csharp
private void sankeyDiagramControl1_SelectedItemsChanging(object sender, SankeySelectedItemsChangingEventArgs e) {
  foreach (SankeyNode node in e.NewNodes) {
    if (node.Tag == "France") {
        e.Cancel = true;
    }    
  }
}
vb
Private Sub sankeyDiagramControl1_SelectedItemsChanging(ByVal sender As Object, ByVal e As SankeySelectedItemsChangingEventArgs)
  For Each node As SankeyNode In e.NewNodes
    If node.Tag Is "France" Then
        e.Cancel = True
    End If
  Next
End Sub

The SelectedItemsChanged event is raised after the Sankey selection state is changed. Use this event to get currently selected elements. The following example uses the SankeySelectedItemsChangedEventArgs.SelectedLinks property to add selected links to a custom selectedExportItems collection:

csharp
HashSet<ExportItem> selectedExportItems = new HashSet<ExportItem>();

private void sankeyDiagramControl1_SelectedItemsChanged(object sender, SankeySelectedItemsChangedEventArgs e){
    selectedExportItems.Clear();
    foreach (SankeyLink link in e.SelectedLinks) {
        selectedExportItems.Add((ExportItem)link.Tags[0]);
    }
}
//...
public class ExportItem {
    public string Exporter { get; set; }
    public string Importer { get; set; }
    public double Sum { get; set; }

    public ExportItem(string from, string to, double weight) {
        this.Exporter = from;
        this.Importer = to;
        this.Sum = weight;
    }
}
vb
Private selectedExportItems As HashSet(Of ExportItem) = New HashSet(Of ExportItem)()

Private Sub sankeyDiagramControl1_SelectedItemsChanged(ByVal sender As Object, ByVal e As SankeySelectedItemsChangedEventArgs)
    selectedExportItems.Clear()
    For Each link As SankeyLink In e.SelectedLinks
        selectedExportItems.Add(CType(link.Tags(0), ExportItem))
    Next
End Sub
'...
Public Class ExportItem
    Public Property Exporter As String
    Public Property Importer As String
    Public Property Sum As Double

    Public Sub New(ByVal from As String, ByVal [to] As String, ByVal weight As Double)
        Exporter = from
        Importer = [to]
        Sum = weight
    End Sub
End Class

To select Sankey nodes and links programmatically, add them to the SelectedItems collection. The following example selects the France node and the related links:

csharp
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 }
};
sankeyDiagramControl1.DataSource = data;
sankeyDiagramControl1.SourceDataMember = "Source";
sankeyDiagramControl1.TargetDataMember = "Target";
sankeyDiagramControl1.WeightDataMember = "Value";
sankeyDiagramControl1.SelectedItems.Add("France");
sankeyDiagramControl1.SelectedItems.Add(data[0]);
sankeyDiagramControl1.SelectedItems.Add(data[2]);
sankeyDiagramControl1.SelectedItems.Add(data[5]);
vb
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
        }
    }
    sankeyDiagramControl1.DataSource = data
    sankeyDiagramControl1.SourceDataMember = "Source"
    sankeyDiagramControl1.TargetDataMember = "Target"
    sankeyDiagramControl1.WeightDataMember = "Value"
    sankeyDiagramControl1.SelectedItems.Add("France")
    sankeyDiagramControl1.SelectedItems.Add(data(0))
    sankeyDiagramControl1.SelectedItems.Add(data(2))
    sankeyDiagramControl1.SelectedItems.Add(data(5))
}

The Sankey diagram control uses the SankeyPaletteColorizer to color nodes. A new color from the palette is applied to each node with a unique label. Colors repeat if the number of unique labels exceeds the number of palette colors. To apply a gradient fill to links, the control utilizes the source and target node colors. Specify the SankeyPaletteColorizer.Palette property to change colors that are used to paint a Sankey diagram. You can select one of predefined palettes.

csharp
sankeyDiagramControl1.Colorizer = new SankeyPaletteColorizer { Palette = Palettes.NorthernLights };
vb
sankeyDiagramControl1.Colorizer = New SankeyPaletteColorizer With {
            .Palette = Palettes.NorthernLights
        }

You can also create a new palette as follows:

csharp
using System.Drawing;
//...
    Palette palette = new Palette("Custom") {
        Color.Red,
        Color.Green,
        Color.Blue
    };
    sankeyDiagramControl1.Colorizer = new SankeyPaletteColorizer { Palette = palette };
vb
Imports System.Drawing
'...
    Dim palette As Palette = New Palette("Custom") From {
        Color.Red,
        Color.Green,
        Color.Blue
    }
    sankeyDiagramControl1.Colorizer = New SankeyPaletteColorizer With {
        palette
    }

Custom Colorizer

To paint links and nodes based on a custom algorithm, create a class that implements the ISankeyColorizer interface. Then, assign an object of this class to the Colorizer property.

The following code implements a colorizer that applies random colors to nodes and specifies colors used to apply a gradient fill to links:

csharp
private void Form1_Load(object sender, EventArgs e) {
    sankeyDiagramControl1.Colorizer = new MyColorizer {
        LinkSourceColor = Color.Red,
        LinkTargetColor = Color.Yellow
    };

public class MyColorizer : ISankeyColorizer {
    public event EventHandler ColorizerChanged;
    Random rand = new Random();
    Dictionary<string, Color> KeyColorPairs = new Dictionary<string, Color>();
    public Color LinkSourceColor { get; set; }
    public Color LinkTargetColor { get; set; }
    public Color GetLinkSourceColor(SankeyLink link) {
        return LinkSourceColor;
    }
    public Color GetLinkTargetColor(SankeyLink link) {
        return LinkTargetColor;
    }
    public Color GetNodeColor(SankeyNode info) {
        if (!KeyColorPairs.TryGetValue((string)info.Tag, out Color nodeColor)) {
            nodeColor = GenerateColor();
            KeyColorPairs.Add((string)info.Tag, nodeColor);
        }
        return nodeColor;
    }
    private Color GenerateColor() {
        return Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256));
    }
}
vb
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
    sankeyDiagramControl1.Colorizer = New MyColorizer With {
        .LinkSourceColor = Color.Red,
        .LinkTargetColor = Color.Yellow
    }
End Sub

Public Class MyColorizer
    Inherits ISankeyColorizer

    Public Event ColorizerChanged As EventHandler
    Private rand As Random = New Random()
    Private KeyColorPairs As Dictionary(Of String, Color) = New Dictionary(Of String, Color)()
    Public Property LinkSourceColor As Color
    Public Property LinkTargetColor As Color

    Public Function GetLinkSourceColor(ByVal link As SankeyLink) As Color
        Return LinkSourceColor
    End Function

    Public Function GetLinkTargetColor(ByVal link As SankeyLink) As Color
        Return LinkTargetColor
    End Function

    Public Function GetNodeColor(ByVal info As SankeyNode) As Color
        Dim nodeColor As Color = Nothing

        If Not KeyColorPairs.TryGetValue(CStr(info.Tag), nodeColor) Then
            nodeColor = GenerateColor()
            KeyColorPairs.Add(CStr(info.Tag), nodeColor)
        End If

        Return nodeColor
    End Function

    Private Function GenerateColor() As Color
        Return Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256))
    End Function
End Class

Customize Tooltips

Tooltips are shown when the mouse pointer hovers over a node or link. Use the SankeyToolTipOptions.NodeToolTipEnabled, SankeyToolTipOptions.LinkToolTipEnabled and ToolTipController properties to disable/enable tooltips and customize their appearance. To format tooltip text, handle the CustomizeNodeToolTip and CustomizeLinkToolTip events and specify the CustomizeSankeyToolTipEventArgs.Title and CustomizeSankeyToolTipEventArgs.Content properties in event handlers.

The following code formats text used in tooltips. The e.Node.Tag , e.Link.SourceNode.Tag , and e.Link.TargetNode.Tag properties store values that are shown in default tooltip titles. To obtain node and link weights, use the e.Node.TotalWeight and e.Link.TotalWeight properties.

csharp
private void Form1_Load(object sender, EventArgs e) {
    sankeyDiagramControl1.ToolTipOptions.LinkToolTipEnabled = DevExpress.Utils.DefaultBoolean.True;
    sankeyDiagramControl1.ToolTipOptions.NodeToolTipEnabled = DevExpress.Utils.DefaultBoolean.True;
    sankeyDiagramControl1.ToolTipController = new DevExpress.Utils.ToolTipController { 
        ToolTipType = DevExpress.Utils.ToolTipType.Flyout, 
        AllowHtmlText = true 
    };
    sankeyDiagramControl1.CustomizeNodeToolTip += OnCustomizeNodeToolTip;
    sankeyDiagramControl1.CustomizeLinkToolTip += OnCustomizeLinkToolTip;
}
private void OnCustomizeNodeToolTip(object sender, CustomizeSankeyNodeToolTipEventArgs e) {
    e.Title = $"Country: <u>{e.Node.Tag}</u>";
    e.Content = $"{e.Node.TotalWeight:f1}";
}

private void OnCustomizeLinkToolTip(object sender, CustomizeSankeyLinkToolTipEventArgs e) {
    e.Title = $"{e.Link.SourceNode.Tag}{e.Link.TargetNode.Tag}";
    e.Content = $"{e.Link.TotalWeight}";
}
vb
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
    sankeyDiagramControl1.ToolTipOptions.LinkToolTipEnabled = DevExpress.Utils.DefaultBoolean.[True]
    sankeyDiagramControl1.ToolTipOptions.NodeToolTipEnabled = DevExpress.Utils.DefaultBoolean.[True]
    sankeyDiagramControl1.ToolTipController = New DevExpress.Utils.ToolTipController With {
        .ToolTipType = DevExpress.Utils.ToolTipType.Flyout,
        .AllowHtmlText = True
    }
    sankeyDiagramControl1.CustomizeNodeToolTip += AddressOf OnCustomizeNodeToolTip
    sankeyDiagramControl1.CustomizeLinkToolTip += AddressOf OnCustomizeLinkToolTip
End Sub

Private Sub OnCustomizeNodeToolTip(ByVal sender As Object, ByVal e As CustomizeSankeyNodeToolTipEventArgs)
    e.Title = $"Country: <u>{e.Node.Tag}</u>"
    e.Content = $"{e.Node.TotalWeight:f1}"
End Sub

Private Sub OnCustomizeLinkToolTip(ByVal sender As Object, ByVal e As CustomizeSankeyLinkToolTipEventArgs)
    e.Title = $"{e.Link.SourceNode.Tag} → {e.Link.TargetNode.Tag}"
    e.Content = $"{e.Link.TotalWeight}"
End Sub

To print the control, use one of the methods below:

The following methods allow you to export the control to various formats:

The code below sets the resulting Sankey diagram image’s width to the document width and exports a Sankey diagram to a PDF file:

csharp
sankeyDiagramControl1.OptionsPrint.SizeMode = DevExpress.XtraCharts.Printing.PrintSizeMode.Zoom;
sankeyDiagramControl1.ExportToPdf("D://sankey.pdf");
vb
sankeyDiagramControl1.OptionsPrint.SizeMode = DevExpress.XtraCharts.Printing.PrintSizeMode.Zoom
sankeyDiagramControl1.ExportToPdf("D://sankey.pdf")

Implements

IPrintable

Inheritance

Object MarshalByRefObject Component Control SankeyDiagramControl

See Also

SankeyDiagramControl Members

Sankey Diagram Control

DevExpress.XtraCharts.Sankey Namespace