This is part two in a three part series for implementing Mega Drop Down navigation in SharePoint 2007. The topics covered are:
Part 1 – Tables, Unordered Lists, and SharePoint
Part 2 – Custom Control Rendering
Part 3 – Hooking it all up with jQuery
Since the out-of-the-box SharePoint menu control renders the HTML in <TABLE> elements,skinning and customizations are limited. The rendering for my MegaDropDown is based on the <DIV> and <UL> elements and provides many more customization options. Note: In SharePoint 2010, the AspMenu control has an option to render with <UL> based elements. I haven’t ported the MegaDropDown to SP2010 yet, but will post my results when I get a chance to convert it.
Once you bind to the navigation’s data source, you can easily walk through the collection of items and render HTML on the fly. I was doing this in my first iteration of the MDD, but things got a little complicated as I needed to parse all the available menu options, and then apply some custom jQuery logic once I had everything calculated. That said, I opted for a two pass approach:
Pass 1 – Iterate through the navigation data source and build my own customized collection that stored some extended properties that were specific to the MDD.
Pass 2 – Iterate through the custom collection and render the HTML
While there is obviously an extra step involved (and another copy of the navigation), it really simplified the code and the overall process.
The custom collection essentially consists of several container classes that keep track of the navigation items (Title, Url, Type). The first two properties are pretty obvious but Type is tailored for the MDD. Type determines if the item is a standard menu item or is an image. A benefit of the MDD is that is has the ability to display a site “image” within the navigation drop down itself (more on this later).
- [Serializable]
- public class MenuItem
- {
- public string Name;
- public string Html;
- public string Url;
- public MenuItem() { }
- public MenuItem(string name, string html, string url)
- {
- Name = name;
- Html = html;
- Url = url;
- }
- public MenuItemType Type;
- }
Displaying and grouping items
There are two formats for displaying menu items: standard and grouped. Standard is just what you would expect and menu items are rendered in the order defined in SharePoint. Grouped items however, allow you to organize links into categories which can significantly simplify the interface when needing to display dozens of links. A major requirement of the MDD project was to keep the customization as transparent and compatible with SharePoint as possible. The grouping logic is one place where some customizations are visible. In order to group items there needs to be a way to define the group name (i.e. category) as well as the links are associated with each category.
MegaDropDown with uncategorized items
MegaDropDown with categorized items
The client I was working with was very adamant about using the standard SharePoint "Modify Navigation” UI so I am not entirely happy with the solution for grouping, but it does solve the problem without building any new web forms or pages. A special syntax was created so an admin simply has to enter it within the standard UI. When you want to specify that a link should fall within a specific group, you append the link title with $GroupName. For example, if you wanted to create a group named Offices and add the Chicago office, you would enter:
Chicago$Offices
That’s it. The parsing code will detect this and separate out the names automatically. Here’s how it would look in the Navigation Link dialog:
As I said, I don’t exactly like the approach, but it is a pretty low-impact solution and took about 20 minutes to write the parsing code—and no UI changes were needed!
I keep track of each MenuItem in a generic List. Since there are two types of dropdowns (standard and grouped), I keep track of each link type in a separate List.
- [Serializable]
- public class MenuNode : MenuItem
- {
- public List<MenuGroup> Groups = new List<MenuGroup>();
- public List<MenuItem> Items = new List<MenuItem>();
- public List<MenuNode> Nodes = new List<MenuNode>();
- public MenuNode() { }
- public MenuNode(string name, string html, string url)
- : base(name, html, url)
- {
- }
- public MenuState State;
- }
Keeping track of all of the navigation types is a little tricky, so hopefully the following diagram helps illustrate how the collections relate:
Once all the parsing is done and I am ready to render the items, I simply iterate through each list and generate the HTML: For brevity, I’ve removed some of the housekeeping logic, but the RenderMenus() method walks the main List<> and calls RenderGroupItems or RenderMenuItems() accordingly:
- private bool RenderMenus()
- {
- int menuCount = 0;
- string _menuItem = "<li class=’menuitem{0}’><a href=’{1}’><span class=’menuitem{2}’>{3}</span></a></li>";
- string _parentMenuItem = "<li class=’nav_sites{0} menuitem{1}’><a href=’{2}’><span class=’groupmenuitem{3}’>{4}</span></a>";
- writer.Write("<ul id=’navigation’>");
- // renders the top level menu (horizontal navigation bar)
- foreach (MenuNode node in _menuNodes)
- {
- string current = string.Empty;
- // if this site (or any sub-site within) is the current site, add a CSS style so we can hightlight it
- if (node.State == MenuState.MenuSelected)
- current = " current";
- // just a single-level menu item (no kids)
- if ((node.Items.Count == 0) && (node.Groups.Count == 0))
- {
- output = string.Format(_menuItem, current, node.Url, current, node.Name);
- writer.Write(output);
- }
- else
- {
- if (node.Groups.Count > 0) // traverse all groups
- {
- output = string.Format(_parentMenuItem, menuCount, current, node.Url, current, node.Name);
- writer.Write(output);
- RenderGroupItems(writer, node, menuCount);
- writer.Write("</li>");
- }
- else if (node.Items.Count > 0)
- {
- output = string.Format(_parentMenuItem, menuCount, current, node.Url, current, node.Name);
- writer.Write(output);
- scriptBlock = RenderMenuItems(writer, node.Items, menuCount);
- scripts.Add(scriptBlock);
- writer.Write("</li>");
- }
- menuCount++;
- }
- }
- }
Rendering Menu Items
For the most part, displaying each of the menu items involves the same logic except you iterate through each of the top level items menus. As you read each item, you just emit individual <li> elements that represent each of the menus.
The end result of the HTML might look something like this:
- <div id="navcontainer">
- <ul id=’navigation’>
- <li class=’menuitem current’>
- <a href=’/Pages/Default.aspx’>
- <span class=’menuitem current’>Home</span>
- </a>
- </li>
- <li class=’menuitem’>
- <a href=’/news’>
- <span class=’menuitem’>News</span>
- </a>
- </li>
- <li class=’nav_sites0 menuitem’>
- <a href=’/locations’>
- <span class=’groupmenuitem’>Locations</span>
- </a>
- <div id=’menu0′ class=’menu’>
- <div class=’menunavitems’ id=’menunavitems_1′>
- <table cellpadding=’0′ cellspacing=’0′ class=’navlinklayout’ width=’100%’ border=’0′>
- <tr>
- <td class=’grouplayout’ valign=’top’>
- <h3 class=’cat_header’>Offices</h3>
- <ul class=’sub_nav’ id=’1′>
- <li>
- <a href=’/offices/chicago’><span>Chcago</span></a>
- </li>
- <li><a href=’/offices/newyork’><span>New York</span></a></li>
- </ul>
- <td>
- </tr>
- </table>
- </div>
- </div>
- </li>
- <li class=’menuitem’>
- <a href=’/reports’>
- <span class=’menuitem’>Reports</span>
- </a>
- </li>
- </ul>
- </div>
The code is fairly straightforward and I tried to make the HTML as simple as possible. The entire navigation, including the mega drop down sits inside the main <div> container ‘navcontainer’. The ‘navigation’ <UL> handles the horizontal top ‘tabbed’ navigation menu. Since it is <UL> based, I style the layout and appearance with CSS. Each <LI> represents a tab defined in SharePoint’s navigation page.
When sub-navigation items exists (e.g. sub-sites), a new <DIV> (menu) is defines the drop down itself. Although I tried to follow a flexible <DIV> based layout, the sub-items list is tabular and fits nicely in a <TABLE>. If grouping exists, each group (or category) is displayed with a styled <H3>. All the sub-items are then displayed with an embedded <UL> (sub-nav).
In my next and final article, I will explain how I hook it all up with jQuery. (I promise that it won’t be nearly as long between postings).
As always, comment or email me if you have any questions.




Thanks a lot for write this down!
I was following the first post for like a month to see if second part was available.
After that, i finally implement a solution to parse an XML file and render with JQuery+CSS the mainnav to acquire the same result as you. Obviously, my client doesn’t have the need to update the menu several times. Otherwise it will be a headache
This really helps to figure some problems that i had!
Simple and maybe dummy question:
The code snippets that you have post here are to build a control (or something) in VS? Or better, how do you implement that code?
Thanks again!