In the world of data visualization, representing hierarchical structures clearly and effectively can be challenging. However, with tools like Syncfusion's Diagram control, creating visually appealing and highly functional diagrams becomes much easier. In this blog post, I'll walk you through the process of creating a visual diagram using Syncfusion's HierarchicLayoutManager in a Windows Forms application.
We'll dive into the step-by-step process of initializing the diagram, populating it with nodes representing data objects, and customizing the layout to meet your specific needs.
Setting Up the Windows Forms Application
Before we begin, ensure you have Syncfusion's Diagram control installed in your project. If you don't have it yet, you can add it via NuGet:
Install-Package Syncfusion.Windows.Forms.Diagram
The Code Implementation
The following code demonstrates how to set up a diagram in a Windows Forms application using Syncfusion's HierarchicLayoutManager. This example uses static data for simplicity.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Syncfusion.Windows.Forms.Diagram;
namespace TADA.EndAround.DataOrganizer.UI.General
{
public partial class WorkspaceSnapshot : Form
{
#region Members
HierarchicLayoutManager hierarchicalLayout;
#endregion
public WorkspaceSnapshot()
{
InitializeComponent();
dataSourceDiagram.BeginUpdate();
this.dataSourceDiagram.Model.BoundaryConstraintsEnabled = false;
comboLytDirection.SelectedIndex = 0;
PopulateFields();
this.dataSourceDiagram.Model.RenderingStyle.SmoothingMode = SmoothingMode.HighQuality;
this.dataSourceDiagram.View.BackgroundColor = Color.Transparent;
this.dataSourceDiagram.Model.BoundaryConstraintsEnabled = false;
this.dataSourceDiagram.View.HandleRenderer.HandleColor = Color.AliceBlue;
this.dataSourceDiagram.View.HandleRenderer.HandleOutlineColor = Color.SkyBlue;
this.dataSourceDiagram.View.SelectionList.Clear();
hierarchicalLayout = new HierarchicLayoutManager(this.dataSourceDiagram.Model, 0, 55, 10);
hierarchicalLayout.LeftMargin = 25;
hierarchicalLayout.TopMargin = 25;
hierarchicalLayout.Model.RightMargin = 25;
hierarchicalLayout.Model.BottomMargin = 25;
this.dataSourceDiagram.LayoutManager = hierarchicalLayout;
this.dataSourceDiagram.View.Magnification = 55;
this.dataSourceDiagram.LayoutManager.UpdateLayout(null);
txtHSpacing.Text = hierarchicalLayout.HorizontalSpacing.ToString();
txtVSpacing.Text = hierarchicalLayout.VerticalSpacing.ToString();
txtLMarigin.Text = hierarchicalLayout.LeftMargin.ToString();
txtTMarigin.Text = hierarchicalLayout.TopMargin.ToString();
txtBMarigin.Text = hierarchicalLayout.Model.BottomMargin.ToString();
txtRMarigin.Text = hierarchicalLayout.Model.RightMargin.ToString();
chkAutoLayout.Checked = hierarchicalLayout.AutoLayout;
dataSourceDiagram.EndUpdate();
}
#region InitializeDiagram
/// <summary>
/// Initialize the nodes and links in the diagram based on static data.
/// </summary>
private void PopulateFields()
{
// Static Architecture Node
string architectureName = "System Architecture";
Syncfusion.Windows.Forms.Diagram.RoundRect architectureNode = new Syncfusion.Windows.Forms.Diagram.RoundRect(0, 0, 120, 75, MeasureUnits.Pixel);
architectureNode.FillStyle.Color = Color.FromArgb(255, 86, 4);
architectureNode.FillStyle.ForeColor = Color.FromArgb(255, 165, 74);
architectureNode.LineStyle.LineColor = Color.White;
architectureNode.FillStyle.Type = FillStyleType.LinearGradient;
architectureNode.Name = architectureName;
Syncfusion.Windows.Forms.Diagram.Label architectureLabel = new Syncfusion.Windows.Forms.Diagram.Label(architectureNode, architectureName);
architectureLabel.FontStyle.Family = "Segoe UI";
architectureLabel.FontStyle.Size = 10;
architectureLabel.FontColorStyle.Color = Color.Black;
architectureNode.Labels.Add(architectureLabel);
dataSourceDiagram.Model.AppendChild(architectureNode);
// Static Data Source and Tables
int dataSourceX = 150;
int dataSourceY = 150;
int tableX = 300;
int tableY = 150;
int clauseX = 450;
int clauseY = 150;
int columnOffset = 70;
string[] dataSources = { "Database A", "Database B" };
string[][] tables = {
new string[] { "Table 1", "Table 2" },
new string[] { "Table 3", "Table 4" }
};
string[][][] dataClauses = {
new string[][] { new string[] { "Clause 1A", "Clause 1B" }, new string[] { "Clause 2A", "Clause 2B" } },
new string[][] { new string[] { "Clause 3A", "Clause 3B" }, new string[] { "Clause 4A", "Clause 4B" } }
};
for (int i = 0; i < dataSources.Length; i++)
{
Syncfusion.Windows.Forms.Diagram.RoundRect dataSourceNode = new Syncfusion.Windows.Forms.Diagram.RoundRect(dataSourceX, dataSourceY, 100, 60, MeasureUnits.Pixel);
dataSourceNode.FillStyle.Color = Color.FromArgb(123, 104, 238);
dataSourceNode.LineStyle.LineColor = Color.White;
dataSourceNode.Name = dataSources[i];
Syncfusion.Windows.Forms.Diagram.Label dataSourceLabel = new Syncfusion.Windows.Forms.Diagram.Label(dataSourceNode, dataSources[i]);
dataSourceLabel.FontStyle.Family = "Segoe UI";
dataSourceLabel.FontStyle.Size = 10;
dataSourceLabel.FontColorStyle.Color = Color.Black;
dataSourceNode.Labels.Add(dataSourceLabel);
dataSourceDiagram.Model.AppendChild(dataSourceNode);
ConnectNodes(architectureNode, dataSourceNode);
dataSourceY += 100;
for (int j = 0; j < tables[i].Length; j++)
{
Syncfusion.Windows.Forms.Diagram.RoundRect tableNode = new Syncfusion.Windows.Forms.Diagram.RoundRect(tableX, tableY, 100, 60, MeasureUnits.Pixel);
tableNode.FillStyle.Color = Color.FromArgb(72, 209, 204);
tableNode.LineStyle.LineColor = Color.White;
tableNode.Name = tables[i][j];
Syncfusion.Windows.Forms.Diagram.Label tableLabel = new Syncfusion.Windows.Forms.Diagram.Label(tableNode, tables[i][j]);
tableLabel.FontStyle.Family = "Segoe UI";
tableLabel.FontStyle.Size = 10;
tableLabel.FontColorStyle.Color = Color.Black;
tableNode.Labels.Add(tableLabel);
dataSourceDiagram.Model.AppendChild(tableNode);
ConnectNodes(dataSourceNode, tableNode);
tableY += 100;
for (int k = 0; k < dataClauses[i][j].Length; k++)
{
Syncfusion.Windows.Forms.Diagram.RoundRect clauseNode = new Syncfusion.Windows.Forms.Diagram.RoundRect(clauseX, clauseY, 100, 60, MeasureUnits.Pixel);
clauseNode.FillStyle.Color = Color.FromArgb(144, 238, 144);
clauseNode.LineStyle.LineColor = Color.White;
clauseNode.Name = dataClauses[i][j][k];
Syncfusion.Windows.Forms.Diagram.Label clauseLabel = new Syncfusion.Windows.Forms.Diagram.Label(clauseNode, dataClauses[i][j][k]);
clauseLabel.FontStyle.Family = "Segoe UI";
clauseLabel.FontStyle.Size = 10;
clauseLabel.FontColorStyle.Color = Color.Black;
clauseNode.Labels.Add(clauseLabel);
dataSourceDiagram.Model.AppendChild(clauseNode);
ConnectNodes(tableNode, clauseNode);
clauseX += columnOffset;
}
clauseX = 450;
}
dataSourceY = 150;
}
}
/// <summary>
/// Connects the given nodes
/// </summary>
/// <param name="parentNode">Parent Node</param>
/// <param name="childNode">Child node</param>
private void ConnectNodes(Node parentNode, Node childNode)
{
if (parentNode != null && childNode != null)
{
OrgLineConnector conn = new OrgLineConnector(PointF.Empty, new PointF(0, 1));
conn.VerticalDistance = 35;
conn.LineStyle.LineColor = Color.Gray;
Decorator decor = conn.HeadDecorator;
decor.DecoratorShape = DecoratorShape.Filled45Arrow;
decor.FillStyle.Color = Color.White;
decor.LineStyle.LineColor = Color.Gray;
this.dataSourceDiagram.Model.AppendChild(conn);
parentNode.CentralPort.TryConnect(conn.TailEndPoint);
childNode.CentralPort.TryConnect(conn.HeadEndPoint);
this.dataSourceDiagram.Model.SendToBack(conn);
}
}
#endregion
#region Event Handlers
private void apply_Click(object sender, EventArgs e)
{
float rotationAngle = 0;
float parseVal;
if (txtHSpacing.Text != string.Empty && float.TryParse(txtHSpacing.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).HorizontalSpacing = parseVal;
else
txtHSpacing.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).HorizontalSpacing.ToString();
if (txtVSpacing.Text != string.Empty && float.TryParse(txtVSpacing.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).VerticalSpacing = parseVal;
else
txtVSpacing.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).VerticalSpacing.ToString();
if (txtRMarigin.Text != string.Empty && float.TryParse(txtRMarigin.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).Model.RightMargin = parseVal;
else
txtRMarigin.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).Model.RightMargin.ToString();
if (txtBMarigin.Text != string.Empty && float.TryParse(txtBMarigin.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).Model.BottomMargin = parseVal;
else
txtBMarigin.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).Model.BottomMargin.ToString();
if (txtLMarigin.Text != string.Empty && float.TryParse(txtLMarigin.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).LeftMargin = parseVal;
else
txtLMarigin.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).LeftMargin.ToString();
if (txtTMarigin.Text != string.Empty && float.TryParse(txtTMarigin.Text.ToString(), out parseVal))
((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).TopMargin = parseVal;
else
txtTMarigin.Text = ((HierarchicLayoutManager)this.dataSourceDiagram.LayoutManager).TopMargin.ToString();
hierarchicalLayout.Model.DocumentSize = new SizeF((hierarchicalLayout.Model.BoundingRect.Size.Width + hierarchicalLayout.Model.RightMargin),
(hierarchicalLayout.Model.BoundingRect.Size.Height + hierarchicalLayout.Model.BottomMargin));
}
#endregion
}
}
Breakdown of Key Components
Diagram Initialization:
The diagram is initialized with specific settings, such as boundary constraints, rendering styles, and handle colors, to ensure the diagram's layout is smooth and visually appealing.PopulateFields Method:
This method creates the nodes representing the system architecture, data sources, tables, and clauses. The nodes are connected using connectors (OrgLineConnector) to establish relationships between them.HierarchicLayoutManager:
The HierarchicLayoutManager is configured to manage the layout of nodes within the diagram, including spacing and margins. It automatically adjusts the positions of nodes to create a hierarchical layout.Event Handlers:
The apply_Click event handler allows users to adjust spacing and margins dynamically, which can be useful for fine-tuning the layout in real-time.
Conclusion
With Syncfusion's powerful diagramming tools, creating complex hierarchical diagrams becomes straightforward. By leveraging the HierarchicLayoutManager, you can ensure that your diagram is not only functional but also aesthetically pleasing. This approach provides a solid foundation for visualizing complex data structures in a Windows Forms application.
Feel free to modify the code to suit your specific requirements, whether it involves more complex data structures or different visual styles. The flexibility of Syncfusion's controls allows for endless possibilities in data visualization.
Top comments (0)