Click here to Skip to main content
Click here to Skip to main content

Tree/Organization structure

, 3 Jul 2013
Rate this:
Please Sign up or sign in to vote.
Create HTML tree or organization structure for web applications.

Introduction

A tree structure is a way of representing the hierarchical nature of a structure in a graphical form. ASP.NET provides tree control to represent hierarchy structure, but this is very basic control and do not have capability to show in graphical manner. It’s very easy to understand the relationships between nodes at one glance.

This article provides detail on how to create tree structure. I have created sample code that you can either use as-is or customized as per your need.

Background

The tree structure is created with basic HTML Table and Div controls with basic styles, hence supported by all the browsers. HTML table cells hold the node name and the lines to show node relationship. These lines are basically created with DIV tags. Line width is calculated on the bases of its child node.

Code Detail

Basic Data requirements

My data reside in SQL server and used SQL queries to fetch the data. Below table has sample data to create tree structure. In this example column ID has unique values and column PID (Parent ID ) has relationship with ID column. Column Name is self explanatory that is node name in tree.

Below is table structure used to create Hierarchical structure.

ID Name PID
1 Commissioner 0
2 Chief Account Officer 1
4 Additional Commissioner 1
6 Administrative Officer 1
7 Estate Officer 1
8 Account Officer 2
9 Account Head 2
10 Deputy Commissioner1 4
11 Deputy Commissioner 2 4
12 Drug Division 10
13 Project Division 10
14 Capital Project 10
16 Korba project 11
17 Admin project 11
18 Officer1 9
19 Officer 2 9

Column Descriptions:

ID: Represent unique id for each node in tree.

Name: Name of the node

PID: Parent ID of Node ( This is relation with ID column).

Every Tree has only one parent node and in my example parent node has PID value as zero.

Code Detail

How to Use

Step 1: Add below style and DIV tag you aspx page . These style are used while rendering the tree in browser.

Style used for hierarchy structure in ASPX page

<style type="text/css">

.vertical
{   position:relative;
    width: 2px;
    height: 25px;
    left:50%; 
    background-color: #000000;
}


table 
{
   vertical-align:top;
   text-align:center;
    border-collapse: collapse;
}
table td { border:0;   vertical-align:top;   text-align:center; word-wrap: break-word;
}

</style>//

ASPX page code - Div will hold the tree structure.

//
<body>
    <form id="form1" runat="server">
    <div id="abc" runat="server"></div>
    </form>
</body> 

Step 2: Add below code in ASPX page cs file. The first line of Page_load function create the instance of the “OrgTree” class and the second line of the code call the “CreateTreeHtml()” function to generate the application.

protected void Page_Load(object sender, EventArgs e)
{
    OrgTree orgTree = new OrgTree();
    //Assing output of the CreateTreeHtml function to Div
    abc.InnerHtml = orgTree.CreateTreeHtml();
}

Step 3: Add Class “OrgTree” in your project and modify the connection string to point it to your database. Change the table name if required. You will find more detail about this class in next section.

Run your application and you will find the tree structure on your screen.

How it works Core Functionality

The class “OrgTree” contains the core logic to create a tree structure. This class contain create two mail function and rest four function are helper to create tree. I will explain each function in detail in subsequent sections.

1. CreateTreeHtml: This is public function and expose to outer world to create tree structure. This function internally calls another function CreateLevelNode which take the input as ID of parent node in tree. In this example I have passes value as 1 because in my example parent node id is one.

public string CreateTreeHtml()
{
    // CreateLevelNode funtion accept the Root node id. In this example the root node id is 1.
    return CreateLevelNode(1);
} 

2. CreateLevelNode : This is core function that return the HTML string of hierarchy structure. This function is called recursively to create entire tree structure. In the first step it create parent node and then create child node. Each child node again calls this function to create its child node. This process goes continuously until all the leaf nodes are get created. Basic logic is creating table with number of columns equal to child nodes. One extra table row is created in each table to draw horizontal line. This horizontal line is nothing but a DIV tag in HTML having height as 1 pixel, when this DIV is rendered in the browser it appears just as a single line.

Same way for each node creates a vertical line down to each parent node and again this vertical line is DIV tag having width 1 pixel.

private string CreateLevelNode(int PID)
{
    
    string finalHdiv;
    StringBuilder sbmainTbl = new StringBuilder();
    DataTable dtparent = GetParentNodeDetails(PID);
    
    //Caclulate with of tabele cell.     
    int leafNodeCount = GetLeafNodeCount(PID);
    if (leafNodeCount == 0)
        leafNodeCount = 1;//avoid divide by 0 error
    int tdWidth = 100 / leafNodeCount;

    // Get the difference in width to create blank node.
    int tdWidthDiff = 100 - leafNodeCount * tdWidth;

    sbmainTbl.AppendFormat("{0}", tabletag);

    // Add parent Node Text .
    sbmainTbl.AppendFormat("<tr><td>{0}</td></tr>", 
          CellText(dtparent.Rows[0]["Name"].ToString()));

    // Get the child node Name
    DataTable dt = GetChildNodeDetails(int.Parse(dtparent.Rows[0]["ID"].ToString()));
    int childNodecount = dt.Rows.Count;

    //Draw Horizontal and Vertical line if child nore are more than one.
    if (childNodecount > 1)
    {
        if (childNodecount > 0)
            sbmainTbl.AppendFormat("<tr><td>{0}</td></tr>", VDiv);

        if (childNodecount == 0)
            finalHdiv = string.Format(HDiv, 0, 0);
        else
            finalHdiv = HLineWidth(PID);// string.Format(HDiv, 100 - 100 / 
              childNodecount, 100 / (2 * childNodecount));
        sbmainTbl.AppendFormat("<tr><td>{0}</td></tr>", finalHdiv);
    }

    sbmainTbl.AppendFormat("<tr><td>");

    //Create Vertical line below parent Node.
    sbmainTbl.AppendFormat("{0}<tr>", tabletag);
    for (int i = 0; i < childNodecount; i++)
    {
        int leafNodeCountchild = GetLeafNodeCount(int.Parse(dt.Rows[i]["ID"].ToString()));
        if (leafNodeCountchild > 0)
            sbmainTbl.AppendFormat("<td style=\"width:{0}%\" >{1}</td>", 
                                   leafNodeCountchild * tdWidth, VDiv);
        else
            sbmainTbl.AppendFormat("<td>{1}</td>", VDiv);
        // sbmainTbl.AppendFormat("<td style=\"width:{0}%\" >{1}</td>", tdWidth, VDiv);
    }
    //Create empty Cell.
    if (tdWidthDiff > 0)
    {
        sbmainTbl.AppendFormat("<td style=\"width:{0}%\" ></td>", tdWidthDiff);
    }
    sbmainTbl.Append("</tr>");

    sbmainTbl.Append("<tr>");
    for (int i = 0; i < childNodecount; i++)
    {
        //Create Child Node Table.
        sbmainTbl.AppendFormat("<td>{0}</td>", 
          CreateLevelNode(int.Parse(dt.Rows[i]["ID"].ToString())));
    }
    //Create empty Cell.
    if (tdWidthDiff > 0)
    {
        sbmainTbl.AppendFormat("<td style=\"width:{0}%\" ></td>", tdWidthDiff);
    }
    sbmainTbl.Append("</tr></table>");
    sbmainTbl.AppendFormat("</td></tr></table>", tabletag);
    return sbmainTbl.ToString();
}

3. HLineWidth: This function calculate the width of horizontal line and set the left offset point to draw this line. As number of child node in tree are not fixed so width of line is calculated based upon the number of child nodes. If child node is one than no need to draw horizontal line and if Node do not have any child then do nothing. Let’s consider child nodes are three than horizontal line width would be half width of first node + width of second node + half width of last node. Offset point would be half width of first child node.

private string HLineWidth(int PID)
{
    //This function calculate the width of Horizontal line 


    float HlineWidth = 0;

    int leafNodeCount = GetLeafNodeCount(PID);
    if (leafNodeCount == 0)
        leafNodeCount = 1;//avoid divide by 0 error
    float tdWidth = 100 / leafNodeCount;
    float tdWidthDiff = 100 - tdWidth * leafNodeCount;
    DataTable dt = GetChildNodeDetails(PID);
    int childNodecount = dt.Rows.Count;

    float offset = 0;
               
    for (int i = 0; i < childNodecount; i++)
    {
        int leafNodeCountchild = GetLeafNodeCount(
            int.Parse(dt.Rows[i]["ID"].ToString()));
        if (i == 0)
        {
            offset = leafNodeCountchild * tdWidth / 2;
            HlineWidth = HlineWidth + offset;                   
        }
        else if (i == (childNodecount - 1))
        {
            HlineWidth = HlineWidth + leafNodeCountchild * tdWidth / 2;                   
        }
        else
        {
            HlineWidth = HlineWidth + leafNodeCountchild * tdWidth;                   
        }
    }

    return string.Format(HDiv, HlineWidth , offset);
}

4. CellText : This is very simple function and just add the parent node name inside the DIV tag.This function is intentionally created to provide colors and background color to node. You can also customize this to create hyperlink or add custom functionality on this node.

private string CellText(string Celltxt)
{
    // Set the Node text here. You can customize this as per your requirement.
    return string.Format("<div style=\"display:block; " + 
      "word-wrap: break-word; width: 99%;\">{0}</div>", Celltxt);
}

5. GetChildNodeDetails : This function just connect to data base and get the child nodes detail for next level only. You must change the connection string to point to your DB to get the required data.

private DataTable GetChildNodeDetails(int parentId)
{
    // Get the Current level child node details (ID , Name, PID)
    SqlConnection con = new SqlConnection();
    con.ConnectionString = ConnectionString;
    SqlCommand cmd = new SqlCommand(
      "select * from tree where pid= " + parentId.ToString(), con);
    //create the DataAdapter & DataSet
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    con.Close();
    return ds.Tables[0];
}

6. GetParentNodeDetails : This function just get the current node name and its ID. This ID would be used to get their child node in further calls. This function return the HTML Hierarchy table.

private DataTable GetParentNodeDetails(int ID)
{
    // Get the Parent Node Name and ID
    SqlConnection con = new SqlConnection();
    con.ConnectionString = ConnectionString;
    SqlCommand cmd = new SqlCommand("select * from tree where id= " + ID.ToString(), con);
    //create the DataAdapter & DataSet
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    con.Close();
    return ds.Tables[0];
} 

7. GetLeafNodeCount: This is last function and just return the leaf level nodes count for parent node.

private int GetLeafNodeCount(int ID)
{
    // This function return the Leaf node count.
    string query = string.Format(@"WITH Node (ID, name,PID)
                                AS  (
                                    SELECT     tree.ID, tree.name , tree.PID
                                    FROM       tree
                                    WHERE      ID ={0}
                                    UNION ALL
                                    SELECT     tree.ID, tree.name, tree.PID
                                    FROM       tree
                                    INNER JOIN Node
                                    ON         tree.PID = Node.ID
                                    )
                                SELECT  ID, name,PID FROM   Node
                                where  ID not in (SELECT  PID FROM   Node)
                                ", ID);

    SqlConnection con = new SqlConnection();
    con.ConnectionString = ConnectionString;
    SqlCommand cmd = new SqlCommand(query, con);
    //create the DataAdapter & DataSet
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    con.Close();
    return ds.Tables[0].Rows.Count;

} 

Class level variables: Below are variables used by above functions:

//tableTag variable define the table with style for the Hierarchy structure.
string tabletag = "<table class =\"tbl\" border=\"0\" " + 
  "cellpadding=\"0\" cellspacing=\"0\" style=\" " + 
  "table-layout: fixed;   width:100% ;vertical-align:top; text-align:center\" >";
//HDiv variable Show horizontal  line .
string HDiv = " <div style=\"position:relative; background-color:" + 
  " #000000;width: {0}%; left:{1}%;  height: 2px;\"></div>";
//HDiv variable Show horizontal  line .
string VDiv = " <div class=\"vertical\"></div>";

String ConnectionString = @"Data Source=.\;Initial Catalog=TestDB;Integrated Security=True";

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Anil kumar Bhardwaj

United States United States
No Biography provided

Comments and Discussions

 
Questionwant to add new functionality on the node PinprofessionalAmit Kumar14326-Dec-13 22:31 
QuestionCan you make this generic, so it can be used as a structure in ASP MVC menu's as well Pinmemberlongnights14-Sep-13 18:31 
GeneralMy vote of 5 Pinmemberlongnights14-Sep-13 18:30 
QuestionNot bad. Could take the structure further though for best performance Pinmemberrobvon11-Jul-13 21:10 
GeneralMy vote of 3 PinmemberMBSMBS7-Jul-13 21:11 
GeneralRe: My vote of 3 PinmemberAnil kumar Bhardwaj7-Jul-13 21:47 
GeneralMy vote of 4 PinmemberAshutosh Phoujdar4-Jul-13 1:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 3 Jul 2013
Article Copyright 2013 by Anil kumar Bhardwaj
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid