NerdyHearn
Home
Blog

Contact
Mailing List

Software

Active Directory Products
Object Compare
Permission Compare

IPhone Products
Calls To Calendar
SMS To CSV
SMS To Gmail
Voicemail To Gmail

Sites
DocuTerminal
How Long For Me
My Music To Me
SaveMySerials
TypeCount

Blog
Twitter

NerdyHearn - Blog


<< Back To All Blogs

Creating an Organizational Chart using C#: Part 2

Thursday, June 18th, 2009

In part 1 of this series, I went over how to actually establish the tree layout that would be used to create an organizational chart using C#.

Now that we have established we have a loaded tree structure, we simply need to build the chart itself.

To simplify the explanation of this process, I have broken the drawing out into two sections.

The first section, BuildBlock, simply builds the actual block which will have each Employee's name and title information, with a background color and a small black border around the outside. By breaking this out of the process, it makes the code much easier to follow.

I will comment inline in the method, so here is the code for BuildBlock:

// We want to return a bitmap of each Employee class
private Bitmap BuildBlock(Employee employee)
{
int width = 250;
int height = 80;

// Create a new Bitmap which is is specified by the height and width above
Bitmap canvas = new Bitmap(width, height);

// Create a drawable Graphics object from the bitmap
Graphics gfx = Graphics.FromImage(canvas);

// Fill the rectangle with a background of gray
gfx.FillRegion(Brushes.Gray, new Region(new Rectangle(0, 0, width, height)));

// Draw the border around the outside
gfx.DrawRectangle(new Pen(Brushes.Black, 1), new Rectangle(0, 0, width - 1, height - 1));

// Establish our two font objects, for the name and title respectively
Font fontname = new Font("Helvetica", 14, FontStyle.Bold, GraphicsUnit.Pixel);
Font fonttitle = new Font("Helvetica", 10, FontStyle.Italic, GraphicsUnit.Pixel);

int namewidth = 0;
int nameheight = 0;
int titlewidth = 0;
int titleheight = 0;

// Get the height and widths of each text blob, with their fonts, so we can center them in the block
namewidth = (int)gfx.MeasureString(employee.Name, fontname).Width;
nameheight = (int)gfx.MeasureString(employee.Name, fontname).Height;
titlewidth = (int)gfx.MeasureString(employee.Title, fonttitle).Width;
titleheight = (int)gfx.MeasureString(employee.Title, fonttitle).Height;

int namex = 0;
int namey = 0;
int titlex = 0;
int titley = 0;

// Now determine the center for names and titles with the widths and heights of the title and name font sections
namex = (width / 2) - (namewidth / 2);
namey = (height / 2) - ((nameheight + titleheight) / 2);
titlex = (width / 2) - (titlewidth / 2);
titley = (height / 2) - ((nameheight + titleheight) / 2) + nameheight;

// Draw the actual text
gfx.DrawString(Name, fontname, Brushes.DarkBlue, namex, namey);
gfx.DrawString(Title, fonttitle, Brushes.Black, titlex, titley);

// Flush the output to the bitmap
gfx.Flush();

// Establish a padding bitmap, which will have 10 pixels around the entire image
// Padding makes it look much better so they are not cramping and allows for easier drawing of lines
Bitmap padding = new Bitmap(width + 20, height + 20);

// Make a graphics object from the padding bitmap
Graphics padGfx = Graphics.FromImage(padding);

// Center the image in the padding canvas
padGfx.DrawImage(canvas, new Point(10, 10));

// Flush the output to the bitmap
padGfx.Flush();
return padding;
}

The second aspect of drawing the tree, is drawing the entire tree itself. If you look at the structure of a tree, each tree is really a branch, even the tree itself, from the larger aspect.

To make the drawing logical and much easier to understand, if you break each grouping into a branch, you can then apply logic to each branch and traverse upwards until your tree is complete.

The aptly named BuildBranch is the real "meat" of our organizationl chart builder, so without further ado, here is the recursive code:

private Bitmap BuildBranch(Employee Manager)
{
// In each case of BuildBranch, Manager is really just the parent of all the children

// Get the block of the manager
Bitmap manager = BuildBlock(Manager);

// Now do the same for each child, obtaining a list of all Bitmap children
List<Bitmap> children = new List<Bitmap>();
foreach (Employee reportee in Manager.Children)
{
children.Add(BuildBlock(reportee));
}

int canvasWidth = 0;
int canvasHeight = 0;

// Figure out your final heights and widths
// Do not forget that a branch could also just be a single employee without any children,
// essentially rendering a branch the same as a block
if (children.Count > 0)
{
foreach (Bitmap child in children)
{
canvasHeight += child.Height;
canvasWidth = manager.Width;
}
// Always increase the height by the manager
canvasHeight += manager.Height;
}
else
{
canvasWidth = manager.Width;
canvasHeight = manager.Height;
}

// Create the bitmap to the correct size
Bitmap result = new Bitmap(canvasWidth, canvasHeight);

// Now create a Graphics object from that bitmap
Graphics gfx = Graphics.FromImage(result);

// Center the manager in the entire branch image
int managerx = (canvasWidth / 2) - (manager.Width / 2);

// If the manager has children, then you want to draw a line down to connect the branches below
if (children.Count > 0)
{
Graphics liner = Graphics.FromImage(manager);
liner.DrawLine(new Pen(Brushes.Black, 2), new Point(manager.Width / 2, 90), new Point(manager.Width / 2, 100));
liner.Flush();
}

// Draw a line from the center of the manager up (to connect to the next block)
Graphics liner = Graphics.FromImage(manager);
liner.DrawLine(new Pen(Brushes.Black, 2), new Point(manager.Width / 2, 0), new Point(manager.Width / 2, 10));
liner.Flush();

gfx.DrawImage(manager, new Point(managerx, 0));

// Draw lines in between org levels to connect each child to the manager
int childx = 0;
foreach (Bitmap child in children)
{
gfx.DrawImage(child, new Point(childx, manager.Height));
gfx.DrawLine(new Pen(Brushes.Black, 2), new Point(childx + (child.Width / 2), manager.Height), new Point(managerx + (manager.Width / 2), manager.Height));
childx += child.Width;
}

// Dump the graphics object to the bitmap
gfx.Flush();

// Give us the full Org Chart!
return result;
}

That's it, now you have your organizational chart!

OrgChartin' Tom Out.

Tags

CSharp

Related Blogs

Autostarting a Windows Service directly after install in C#
Enumerating all attributes of an element and adding them to a dictionary using LINQ with Lambda Expressions
Generic Method for Loading Interfaces in C# (For a Plugin System)
Code Analysis with NDepend V3
Copy a DLL from the GAC

Comments

Tom said on Friday, December 28th, 2012 @ 5:56 AM

Hi Fahim,

Yes it is a recursive function so it will go as deep as the hierarchy needs to.

fahim said on Friday, December 28th, 2012 @ 4:58 AM

Can this be used for more than 2 level hierarchy??
Thanks.

Tom said on Monday, June 29th, 2009 @ 6:22 PM

Thanks for letting me know Wim, I had copied the code inline as I was using a different last-leaf-layout for the last nodes. Thanks for pointing it out!

Wim said on Monday, June 29th, 2009 @ 2:27 AM

You're doing something wrong in the canvas calculation of the whole picture.
This should be:

if (children.Count > 0)
{
// Always increase the height by the manager
canvasHeight = manager.Height*2;
canvasWidth = manager.Width*(children.Count);
}
else
{
canvasWidth = manager.Width;
canvasHeight = manager.Height;
}

BR

Add A Comment

Name:


URL:


Email Address: (not public, used to send notifications on further comments)


Comments:



Enter the text above, except for the 1st and last character:


NerdyHearn - Latest tech news relating to C#, ASP.NET, SharePoint, PHP, general development, and more. DocuTerminal - Online Filing Cabinet solution. Scan, search and archive your paper documents. SaveMySerials - Protect yourself from theft, fire, natural disasters and more by recording your serial numbers My Music To Me - Stream your subsonic music collection to your Sonos wireless system TypeCount - Count how often you type across different computers! ServrTrackr - Monitor your SSL certificates, website uptime, and DNS configurations