DEV Community

Devanshi Akbari
Devanshi Akbari

Posted on

Creating a Visual Diagram Using Syncfusion in a C# Windows Forms Application

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
Enter fullscreen mode Exit fullscreen mode

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
    }
}

Enter fullscreen mode Exit fullscreen mode

Breakdown of Key Components

  1. 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.

  2. 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.

  3. 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.

  4. 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)