Click here to Skip to main content
15,860,972 members
Articles / Web Development / ASP.NET
Article

Tree Chart Generator

Rate me:
Please Sign up or sign in to vote.
4.66/5 (42 votes)
18 Nov 2007CPOL5 min read 354.9K   16.4K   119   117
An article on the development of a Tree Chart Generator
TreeGenerator

Introduction

There is, in organizations, a constant need to display tree structures. This can be done for file systems, Organization charts, Bill of Material, etc. This article discusses a component which can generate an image of the required tree, based on the data in a DataTable. The DLL can be called from Windows Forms or ASP.NET. Most of the parameters (sizes, colors) can be controlled by the developer using the DLL; they are exposed as properties.

This first version of this DLL was called OrgChart Generator and was published here. During development of a BOM display application, it became obvious that the Org chart Generator lacks a few very important issues:

  1. It was too specific: the DLL is a Tree generator and should be named as such.
  2. A node in the Tree could not appear twice. An Employee may work for more than one department.
  3. The data structure used (both an XmlDocument and a SortedDictionary) was not good enough.

So, it became necessary to rewrite the whole application and give it a new name. Hence, this article.

Using the Code

The download ZIP file contains the C# solution, which has two projects:

  1. TreeGenerator, which contains the code for the DLL that generates the Tree image
  2. TestTreeGenerator, which contains a Windows Forms application that demonstrates the abilities of TreeGenerator and its usage.

Constructor

The TreeGenerator accepts a Typed DataTable in the constructor, with the node list, in order to generate the tree. The TreeData.TreeDataTableDataTable is the DataTable that the class will accept in the constructor. It can contain the whole tree structure or just parts of it. If it contains the whole tree, multiple miniTrees can be created from the same instance of the object. The structure of the DataTable is as follows:

  • string nodeID - Primary Key - the ID of the node - employee number, item code or anything else that uniquely identifies the node
  • string parentNodeID - Primary Key - The ID of the node parent. This defines the tree structure. It may be String.Empty
  • string nodeDescription - A short description for the node
  • string nodeNote - A note for the node

Methods

There is just one main method, GenerateTree, which is overloaded. The difference between the overloads is that one method accepts as parameters the required size of the resulting image and resizes it before returning it. The other returns it in the size as determined by calculation - the number of boxes (both x & y) times the number of nodes.
This method returns a System.IO.Stream , which can be used to display an image, save to file, or any other activity. Another important parameter for this method is the StartFromNodeID. This parameter determines which node will be at the top of the tree.

Another method exists mainly as a helper method. getRectangleFromNode is used internally, but can also be used externally to return the Rectangle of the node coordinates, based on the XmlNode. This class/DLL can be used from a Windows Forms application in the following way:

C#
//First - create the DataTable. In real life it would be extracted from a DB
//or created some other way. 
private void Form1_Load(object sender, EventArgs e) {
            TreeGenerator.TreeData.TreeDataTableDataTable dtTree = 
			new TreeData.TreeDataTableDataTable(); 
            dtTree.AddTreeDataTableRow("1000", "","1000",""); 
            dtTree.AddTreeDataTableRow("2000", "1000","2000",""); 
            dtTree.AddTreeDataTableRow("2001", "1000","2001",""); 
            dtTree.AddTreeDataTableRow("3000","2000", "3000",""); 
            dtTree.AddTreeDataTableRow("3001", "2000", "3001",""); 
            dtTree.AddTreeDataTableRow("3000", "2001", "3000",""); 
            dtTree.AddTreeDataTableRow("4000", "3000", "4000",""); 
            dtTree.AddTreeDataTableRow("4001", "3000", "4001", ""); 
          //instantiate the object 
          myTree = new TreeBuilder(dtTree);    
        }
        //Now - Generate the tree itself
        picTree.Image = Image.FromStream(
            myTree.GenerateTree("1000", 
            System.Drawing.Imaging.ImageFormat.Bmp));

Properties

The image that is returned has default properties. In order to control the sizes and colors of the Tree, these properties can be changed (all sizes are in pixels):

  • BoxFillColor - Which color will fill the boxes
  • BoxWidth - The width of each box
  • BoxHeight - The height of each box
  • Margin - The margin from the edges of the image to the boxes
  • HorizontalSpace - The space between the 2 employee boxes on the same level
  • VerticalSpace - The space between 2 levels of employees
  • FontSize - The font size for the node details
  • LineColor - The color of both the box line and the connecting line
  • LineWidth - The width of both the box line and the connecting line
  • BGColor - The color in the Background of the image
  • FontColor - The font color
  • xmlTree - An XmlDocument containing all the tree data, including the position of each node on the chart. This can be used for checking if a specific node was clicked.

Changing these properties could generate weird results, so care should be taken by the user. For example, it would not look good if both the BoxFillColor and the FontColor were the same.

Trapping MouseClick Events

Using the xmlTree property, it can be determined where the user clicked. Below is an example for a Windows Forms application. This will not work for ASP.NET.

C#
private void picOrgChart_MouseClick(object sender, MouseEventArgs e)
        {
            Rectangle currentRect;
            //determine if the mouse clicked on a box, 
            //if so, show the node description.
            string SelectedNode = "No Node";
            //find the node
            foreach (XmlNode oNode in myTree.xmlTree.SelectNodes("//Node"))
            { 
                //iterate through all employees until found.
                currentRect = myTree.getRectangleFromNode(oNode);
                if (e.X >= currentRect.Left &&
                    e.X <= currentRect.Right &&
                    e.Y >= currentRect.Top &&
                    e.Y <= currentRect.Bottom)
                {
                    SelectedNode = 
                    oNode.Attributes["nodeDescription"].InnerText;
                    break;
                }             
            }
            
            MessageBox.Show(SelectedNode);
        }

ASP.NET Usage

Using this code in ASP.NET requires two pages (see attached ZIP file):

  1. ShowTree.aspx - Accepts the user input and shows the image
  2. OrgChartImage.aspx - Generates the image and returns it to the calling page.

ShowTree.aspx

The page contains a textbox that accepts any input. Whatever the user typed will become the top node of the tree. In real life, the textbox (or DDL, or any other control) will accept only employee ID, Item Code, etc. Clicking on the "Show Tree" button will show the tree for the typed in name. In this case, it will be a randomly generated tree. In real life, the tree data should be selected from a database.

Clicking on any of the tree nodes will show the child nodes of that node, in a new tree. It is possible, instead of showing a new tree each time, to enlarge the tree all the time, by keeping the tree dataTable as a static object.

The main function in this page is the one that generates the image. The image can be shown as an imageMap, or any other image tag. In this case, the imageMap.ImageUrl is linked to the page which reads the image from the cache and returns it as a stream.
This function also gets the XmlDocument from the TreeGenerator object and defines the hotspots for the imageMap.

C#
private void GenerateChart(string mItem) 
    { 
        //create the chart object 
        TreeGenerator.TreeBuilder currentBOM       
            = new TreeGenerator.TreeBuilder(GetDT(mItem));
        //specify visual properties
        currentBOM.HorizontalSpace = 15;
        currentBOM.VerticalSpace = 15;
        currentBOM.FontSize = 7;
        currentBOM.BoxHeight = 80;
        currentBOM.BoxWidth = 80;
        currentBOM.LineColor = 
          System.Drawing.ColorTranslator.FromHtml("#BFBFC9");
        currentBOM.FontColor = 
         System.Drawing.ColorTranslator.FromHtml("#3B4164");
           System.Drawing.Image OC = 
           System.Drawing.Image.FromStream(
             currentBOM.GenerateTree(
              mItem, System.Drawing.Imaging.ImageFormat.Bmp)); 
           string nameOfImage =
        Session.SessionID + "CurrentImage"; 
        //save in the cache, to be used by the page creating the image 
        Cache.Insert(nameOfImage, OC, null, 
           DateTime.MaxValue,TimeSpan.FromMinutes(1), 
           CacheItemPriority.NotRemovable, null); 
        //update the item image map 
        //supply the image name by querystring 
        impBOM.ImageUrl = "~/OrgChartImage.aspx?ID=" + nameOfImage;
        //remove all hotspots 
        impBOM.HotSpots.Clear();
        //regenerate the hotspots. 
        foreach (XmlNode oNode in currentBOM.xmlTree.SelectNodes("//Node")) 
        { 
        RectangleHotSpot RectHS
            = new RectangleHotSpot();
            RectHS.HotSpotMode = HotSpotMode.PostBack; Rectangle
            currentRect = currentBOM.getRectangleFromNode(oNode);
            RectHS.Top = currentRect.Top;
            RectHS.Bottom = currentRect.Bottom;
            RectHS.Left = currentRect.Left;
            RectHS.Right = currentRect.Right;
            RectHS.PostBackValue = oNode.Attributes["nodeID"].InnerText;
            RectHS.AlternateText
              = oNode.Attributes["nodeDescription"].InnerText; 
           impBOM.HotSpots.Add(RectHS);
        }
    }

OrgChartImage.aspx

This page loads the cache item based on the passed parameter, converts it into an image and writes it into the output stream. There is only one function here:

C#
protected void Page_Load(object sender, EventArgs e)
    {
        // Change the response headers to output a JPEG image.
        this.Response.Clear();
        this.Response.ContentType = "image/jpeg";
        System.Drawing.Image OC = null;
        //Build the image 
        string imageName = Request.QueryString["ID"];
        if (Cache[imageName] != null)
        {
            OC = (System.Drawing.Image)Cache[imageName];
            // Write the image to the response stream in JPEG format.
            OC.Save(this.Response.OutputStream, ImageFormat.Jpeg);
            OC.Dispose();
        }      
    }

History

  • 16th September, 2007 - Initial release
  • 23rd September, 2007 - Added ASP.NET usage sample

License

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


Written By
Web Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Added Images url to the dataset to draw person's image in the tree Pin
Rotem Sapir15-Apr-09 21:35
Rotem Sapir15-Apr-09 21:35 
QuestionCreating Image with N-Level of tree and huge data crashes the application [modified] Pin
Sameer Kulkarni25-Feb-09 23:54
Sameer Kulkarni25-Feb-09 23:54 
AnswerRe: Creating Image with N-Level of tree and huge data crashes the application Pin
Rotem Sapir26-Feb-09 1:00
Rotem Sapir26-Feb-09 1:00 
GeneralRe: Creating Image with N-Level of tree and huge data crashes the application Pin
Sameer Kulkarni26-Feb-09 22:47
Sameer Kulkarni26-Feb-09 22:47 
GeneralRe: Creating Image with N-Level of tree and huge data crashes the application Pin
Rotem Sapir3-Mar-09 1:15
Rotem Sapir3-Mar-09 1:15 
GeneralRe: Creating Image with N-Level of tree and huge data crashes the application Pin
sfghk9-Mar-09 1:37
sfghk9-Mar-09 1:37 
GeneralRe: Creating Image with N-Level of tree and huge data crashes the application Pin
Rotem Sapir10-Mar-09 20:01
Rotem Sapir10-Mar-09 20:01 
GeneralTreeGenerator.dll Pin
Rokid18-Nov-08 10:28
Rokid18-Nov-08 10:28 
Hello,

Great tool! Would it be possible to get the source code for TreeGenerator.dll ?

Thank you very much in advance!
GeneralRe: TreeGenerator.dll Pin
Rotem Sapir22-Nov-08 21:48
Rotem Sapir22-Nov-08 21:48 
Generalcounting and coloring the node Pin
santosh jha"Don"16-Sep-08 20:39
santosh jha"Don"16-Sep-08 20:39 
GeneralRe: counting and coloring the node Pin
Rotem Sapir17-Sep-08 1:19
Rotem Sapir17-Sep-08 1:19 
GeneralRe: counting and coloring the node Pin
santosh jha"Don"17-Sep-08 1:27
santosh jha"Don"17-Sep-08 1:27 
GeneralRe: counting and coloring the node Pin
Rotem Sapir17-Sep-08 1:30
Rotem Sapir17-Sep-08 1:30 
GeneralRe: counting and coloring the node Pin
santosh jha"Don"17-Sep-08 1:36
santosh jha"Don"17-Sep-08 1:36 
QuestionColoring the boxes? Pin
fromheaven8215-Sep-08 10:37
fromheaven8215-Sep-08 10:37 
AnswerRe: Coloring the boxes? Pin
Rotem Sapir17-Sep-08 1:18
Rotem Sapir17-Sep-08 1:18 
AnswerRe: Coloring the boxes? Pin
WarrenSchwartz17-Mar-09 19:30
WarrenSchwartz17-Mar-09 19:30 
GeneralRe: Coloring the boxes? Pin
Rotem Sapir18-Mar-09 1:22
Rotem Sapir18-Mar-09 1:22 
GeneralError running the ASP.NET example Pin
fromheaven8211-Sep-08 9:22
fromheaven8211-Sep-08 9:22 
GeneralRe: Error running the ASP.NET example Pin
fromheaven8215-Sep-08 10:35
fromheaven8215-Sep-08 10:35 
Generalfunction that connects to Database Pin
kieran540522-Jul-08 0:28
kieran540522-Jul-08 0:28 
QuestionJava version? Pin
Nawaz Ijaz2-Jun-08 22:38
Nawaz Ijaz2-Jun-08 22:38 
AnswerRe: Java version? Pin
Rotem Sapir3-Jun-08 2:01
Rotem Sapir3-Jun-08 2:01 
GeneralPrinting Issue Pin
Johnnybamber16-May-08 4:04
Johnnybamber16-May-08 4:04 
GeneralRe: Printing Issue Pin
Rotem Sapir17-May-08 18:56
Rotem Sapir17-May-08 18:56 

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

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