Sunday, November 01, 2009

Silverlight Organization Chart – Part 2 – Calculations

Now we have the nodes list ready from the previous post, the second step is to start calculating where each node will be displayed. The below points will be called while loading the first time and then when the user resize the chart or open and close any node. For sure it will be better to just draw the chart with the new scale on resizing. And I’ll work on it to enhance its performance isA.


Point 1: Prepare the data


In this point we will start with the root node, and go to the child nodes then the sub child and so on. We need to set the level for each node, and the number of opened and closed sub nodes, also we will set the sub nodes order and the default allowed width for the second level. Also the total height will be calculated.

private void SetLevel(Person parent, int level)

        {

            // Set the node level

            parent.Level = level;

 

            // Calculate the total height based on the number of levels

            if (totalHight < levelHight * (level + 2))

            {

                totalHight = levelHight * (level + 2) ;

                MyCanvas.Height = totalHight;

            }

 

            // Select the closed items under this parent

            var resultN = from n in persons

                          where n.ManagerID == parent.ID && n.Opened == false

                         select n;

 

            Person[] nodesN = resultN.ToArray();

 

            // Get the closed nodes number

            parent.HiddenSubNodes = nodesN.Length;

 

            // Select the opend nodes under this parent

            var result = from n in persons

                         where n.ManagerID == parent.ID && n.Opened == true

                         select n;

 

            Person[] nodes = result.ToArray();

 

            // Get the Opend nodes number

            parent.SubNodes = nodes.Length;

 

            // Call the child nodes

            for (int i = 0; i < nodes.Length; i++)

            {

                nodes[i].NodeOrder = i + 1;

                nodes[i].MinChildWidth = buttonWidth + minHorizontalSpace;

                SetLevel(nodes[i], parent.Level + 1);

            }           

        }



Point 2: Calculations


In this point we will need to:
1. Calculate the width for each node based on the opened sub nodes. As you can see in the image, the minChildWidth for the blue node is not the same as the default.
2. Calculate the StartX for each parent node.
3. Calculate the X coordinate for each node based on the StartX of the parent and the node order.
4. Realign the parent node to be centered in the space allowed for its sub nodes.

private void CalculateWidth(Person parent)

        {

            if (parent.SubNodes > 0)

            {

                var result = from n in persons

                             where n.ManagerID == parent.ID && n.Opened == true

                             orderby n.NodeOrder

                             select n;

 

                Person[] nodes = result.ToArray();

                double minWidth = 0;

                for (int i = 0; i < nodes.Length; i++)

                {

                    CalculateWidth(nodes[i]);

                    minWidth = minWidth + nodes[i].MinChildWidth;

                }

 

                if (minWidth > parent.MinChildWidth)

                {

                    parent.MinChildWidth = minWidth;

                    if (MyCanvas.Width < minWidth)

                    {

                        MyCanvas.Width = minWidth;

                        totalWidth = minWidth;

                    }

                }

 

                // Calculate the startX for each node

                double start = parent.StartX;

                for (int i = 0; i < nodes.Length; i++)

                {

                    nodes[i].StartX = start;

                    nodes[i].X = nodes[i].StartX + nodes[i].MinChildWidth / 2;

                    CalculateWidth(nodes[i]);

                    start = start + nodes[i].MinChildWidth;

                }

 

                // realign the parent node to the middle of the child nodes

                if (nodes.Length > 1)

                {

                    parent.X = (nodes[0].X + nodes[nodes.Length - 1].X) / 2;

                }

                else // root element

                {

                    parent.X = nodes[0].X;

                }

            }

        }



After that we will just need to draw the nodes, we have the X coordinate and we have the Y Coordinate with can be calculated from the level multiplied by the level hight.

3 comments:

ஜெகதீஸ்வரன் said...

Hi Mourad,

Your post is interesting and not able to get the idea about drawing lines for each nodes.

Would you be able to post the source code of this application ?

Thanks & Regards,
Jagadhees

yuva said...

Hi Mourad,

Your post is very useful. And i feel i need some extra information to understand. could you please provide us with some more information like GetPerson(), Start() method in XMLFileLoaded and the MyCanvas XAML and how to load the nodebox in that control.

it will be very helpful, if you help us with this.

Awaiting for your response,
Mozhi.S

Manoj Kumar Jain said...

Sir Share your entire source code. It will like the food of starvation victims. Can you Please tell me if its presentation will be left to right rather Top Down.

kindly get back to me @ mca2004.manoj@gmail.com
I wish all the best to you dude, May god enrich you Day by Day. You given me Hope