Templates-based menu control for SharePoint

, ,

Sometimes doing the simplest things turns out to be unnecessary difficult. This is true with SharePoint development in particular. For example: have you ever tried creating a menu control based on a standard Site Map Provider which would render a nested unordered list (UL) and nothing else? I have, and finally have a solution that I’m happy about: a templates-based menu control.

SharePoint and menus

If you ever looked at the out-of-the-box available Master Pages provided with MOSS 2007 you know that they use the standard ASP.NET menu control to render the navigation. While it definitely does the job, have you ever looked at the rendered HTML?

Screenshot of a home page of a default SharePoint Publishing Portal with rendered HTML source highlighted

Compare how much markup is being used for a couple of menu items which could be rendered like:

<ul>
  <li><a href="/PressReleases/Pages/default.aspx">Press Releases</a></li>
  <li><a href="/Search">Search</a></li>
</ul>

See the difference? A couple of issues should become clearly visible at this point.

ASP.NET menus are poorly accessible

Have you ever took a look at HTML as seen by screen readers or search bots? Because the links are buried under a bunch of tables it’s difficult for such applications to read them correctly. All the tables around the links make the visitors of your site loose the track of what they are “looking” at and just navigate away from your site. Additionally many search bots can’t properly deal with too complex markup. Making the HTML of your pages too complex can result in lower rating of your content.

How much markup do I have to download?

The markup of a standard ASP.NET menu I have showed you in the image above is 1308 bytes. Rendering the same menu using an unordered list is 185 bytes only! The standard ASP.NET menu produces 7 times more markup than you would want it to. Also consider the fact that the menu above consists of only two items. What if we were talking about a medium size website with 20 links? How much markup would you have to download only to see a couple of links?

Branding costs way too much time

Have you ever met a designer who would produce HTML that would match the markup of the ASP.NET menu in 100%? I’m glad I haven’t. Somehow designers have more experience with writing correct markup than most ASP.NET developers. Good designers seem to know what is going to work and what wouldn’t. If a designer gives you branding implemented as static HTML pages, CSS, JavaScript and the only thing you would have to do is to copy & paste all the different pieces in MOSS 2007, you would still spend hours on getting it all working with the CSS you received. There is no chance that branding a table would work with CSS designed for a list. You would have to change one or the other and that costs time – a lot of it.

ASP.NET menu anatomy

ASP.NET menus are not that bad. There are situations when you could use them and experience no problems at all. There are still many applications being developed for environments where performance, bandwidth and accessibility are not an issue. And although I would totally agree that it’s a bad practice to deliver poor markup, people do it, and it works.

ASP.NET menus have one great concept implemented in them: the templates. Using a template you can define placeholder for your data which is then filled using data binding. Although this concept could theoretically give you full control of the rendered HTML markup and separate the logic of the menu control from the rendered HTML, the ASP.NET menu fails to do the job correctly. Yes: you can define your own template but on the output everything you defined is still being rendered in a cells of a nested table.

We should know better

By know many people found their way around the issue of ASP.NET menus on MOSS 2007 Web Content Management solutions. So have I. In the last few years I have worked on a few Web Content Management solutions built on top of MOSS 2007: all of them being Internet-facing sites. In all that time me and my colleagues were trying to find a way to get rid of all these tables and get some more control over the rendered HTML to make it match the branding we had to implement.

At some point we stumbled upon the CSS-friendly Control Adapters. And while they definitely do the job I wasn’t particularly happy about writing HTML using .NET code. Mixing the logic of the Menu with a Control Adapter and the HTML rendering didn’t make it easier for us to maintain and modify the code when needed. Somehow it wasn’t the grail we were looking for.

In the meantime we tried some other approaches including inheriting from the standard ASP.NET menu controls, overriding stuff and creating our own controls from scratch. After all how difficult it can be to create a menu control that gets the data from a Site Map Provider and renders them as an unordered list? My answer? Much more difficult than it should be.

A solution (candidate)

Recently I decided to give it another chance. I still wanted to get the minimal amount of custom code, full control of the rendered HTML and preferably not use .NET to write HTML. I was still charmed by the idea of having these templates you could define in your Master Page/Page Layout.

Here are the results of my work:

Clipping of a SharePoint home page with a menu rendered as an unordered list

Doesn’t look that awesome, does it? Now let’s have a look at the rendered HTML:

Clipping showing the HTML of the SharePoint menu rendered as an unordered list

And this is how it did it:

Code snippet for the menu from the Master Page

To keep a long story short: I have developed a custom control that allows you to define custom templates and using data binding renders the menu items using your own templates!

Using the TemplatedMenu you can add a menu control to your page and use your own rendering without writing a line of code! Comparing to using a Control Adapter, the deployment of the TemplatedMenu is fully supported using the standard SharePoint WSP. If you want to use a Control Adapter in your solution, you have to deploy not only the assembly but also the .browser file what gets challenging in environments with multiple web front ends.

The TemplatedMenu is available on CodePlex. Feel free to experiment with it. I’m curious to hear if it works for you and if there is anything else you find challenging.

To help you understand what I’ve built and how it works I’ve included XML comments in the code. If you’re using DevExpress you can easily view it using the Documentor plugin.

Viewing the XML comments in code using the Documentor plugin for DevExpress

The TemplatedMenu is provided as-is. Although it would be really cool if it could help you in your solution, I give you no guarantee it will. You use it at your own risk.

Download: Imtech SharePoint TemplatedMenu (ZIP, 12KB)

Possibly related posts

35 Responses to “Templates-based menu control for SharePoint”

  1. Jaap Vossers Says:

    Hey Waldek,

    how about storing the templates in SharePoint – in web property bag with custom application page to manage them (per web)?

  2. Waldek Mastykarz Says:

    @Jaap: That would absolutely give the content editors some extra flexibility. I'm not sure however it could be easily done because of the data binding that happens in the templates. I think that it would add extra complexity at costs of a bit of flexibility: after all, how often will you change the markup of a menu?

  3. Jaap Vossers Says:

    It would provide a way to have different menus per site, without having to customise the masterpage / page layout for that.

  4. Waldek Mastykarz Says:

    @Jaap: Definitely agree that it would be cool to have such functionality. I only doubt that templating and data binding is the right approach. Perhaps XML + XSLT would be a better choice?

  5. Tyler Holmes Says:

    Nice moves Waldek! I've had writing this control on my todo list forever now. I expect it will help a LOT when it comes to producing specific data driven markup for jQuery plug-ins.

    Thanks!

  6. Mark Beddis Says:

    Hi Waldek. Your control looks great but I am getting compiler errors – static member cannot be marked as virtual. Do I need to set anything to compile? Thank you so much – I really would like to play with this

  7. Waldek Mastykarz Says:

    @Mark Beddis: Hi Mark, I was quite sure I've put the right source on-line. I will check it out and let you know tomorrow.

  8. Mark Beddis Says:

    Hi Walkdek – Thank you so much for the control.

    I managed to compile it in the end and get it working. Just in case anyone else has had problems compiling and deploying I have noted the steps I took.

    I am using Visual Studio 2008 and WSPBuilder

    The steps I took were as follows:

    I removed the STATIC clause from the 3 lines that made the compiler complain.

    I signed the dll in the visual studio environment (2008) and created a psk (in project properties)

    I removed the post compile copy commands (in the project properties).

    I built the DLL and then used WSPBuilder to deploy
    the application.

    One thing I noticed is that in the example master file you are referring to asp:SimpleMenu – I changed this to TemplatedMenu

    The relevant bits from my custom.master with my changes are:

    Registration Part:

    Usage Part:

    <asp:HyperLink runat="server" ID="MenuItem" Text='' NavigateUrl=" />

    <asp:HyperLink runat="server" ID="MenuItem" Text='' NavigateUrl=" CssClass="selected"
    />

    I didnt think it was working – but found out it was when I created a subsite – it suddenly sprung into life and displayed the subsite name in the menu.

    Thank you again for publicising your work. I am now going to have some fun playing with it.

  9. cactor Says:

    @Mark Beddis,
    Thanks for the comments, can you tell me (or give some link to see) how to deploy that dll.
    Im stuck there! :)

    Also, when i create WPSBuilder Project, and right-click in the project or solution, i don\'t see the \"WSPBuilder\" menu… it could be posible that there is something wrong with the installer?

    Thank\'s again and really great contribution Waldek!!
    Thanks both!

  10. Waldek Mastykarz Says:

    @cactor: it might happen that WSPBuilder has been disabled for some reason. To check if it's there and enabled go in Visual Studio to Tools > Add-In Manager. WSPBuilder should be listed and enabled.

  11. cactork Says:

    You're right Waldek..
    I'll check that, thanks!

  12. Mark Beddis Says:

    Cactor – sorry – I was away for a couple of days – if you can still use any help I still have everthing to hand

  13. Andries Says:

    I really liked to control and templatability :-)

    We used in a branding solution. It worked fine on a single server, but apparently the control has some issues in a farm environment. We put the control into a custom application.master where it worked fine for all types of sites (single server and farm), except when clicking from a mysite to e.g. "Memberships". It results in the following error (we changed the namespace btw as it was part of a larger solution):

    Object reference not set to an instance of an object. at .TemplatedMenu.RenderNode(SiteMapNode node, Int32 level, Int32 index)
    at .TemplatedMenu.CreateChildControls()
    at System.Web.UI.Control.EnsureChildControls()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

    Any thoughts on this?

    The setup for the farm was 3 WFE's and 1 App server.

  14. Andries Says:

    And for some reason the typicial OOTB topmenu items (e.g. myprofile, but also targeted links) do not appear in the menu control. Any thoughts on this?

    Cheers,

    And

  15. Waldek Mastykarz Says:

    @Andries: I can't imagine it depending on something single-server specific. Have you tried debugging the control to get some more details on the error?

  16. Andries Says:

    Did the debugging. Apparently the controls has the 0 reference error when no items/nodes are available (this was the case when applying it to MySite).

    A fix was done in Templatedmenu.cs to avoid this 0 reference error (a.o. check if (currentNode != null)).

    Regards,

    Andries

  17. Waldek Mastykarz Says:

    @Andries: Great to hear the fix was that easy. Thanks for getting back.

  18. Chris Parker Says:

    Great control – we're going to use it in our "learning gateway" project for a local government in the uk. One slight request though:

    If you use an ASP:Menu control, you are able to specify a sitemap datasource which importantly for us allows you to specify a starting node url. I can't seem to provide the same functionality via a custom site map provider.

    This is useful for us because we have subsites below the main site and we do not wish to display the entire site navigation – just the navigation items below the current site.

    Any way we can accomplish this using your templated menu?

  19. Chris Parker Says:

    Apologies for the last comment – just came across the StartingNode variable in your code comments. Must have missed that before :)

  20. Richard Walsh Says:

    I really like this control – it helps meet accessibility guidelines by rendering the menus correctly as lists. However I have a question. Can this menu be also used for the left hand nav in SharePoint? And can the navigation items be managed in MOSS using the admin screen (Site settings…Manage Navigation)? As long as they use the PortalSitemapProvider data source then I presume this is the case, however I am unable to get any items showing for the 'Current Navigation' when using the TemplatedMenu as a left hand nav

    Thanks,

  21. Waldek Mastykarz Says:

    @Richard: theoretically it should be possible. I don't recall using it as the left hand nav, but as long as you're using a Navigation Provider it should work fine.

  22. Sandip Patil Says:

    Hi Waldek

    I m very much frustrated while building custom navigation in publishing site. My issues below
    I m developing on corporate site like this http://www.softsolutions.com.pk/
    I have created pages Home,About and submenu pages of this menu, Services and submenu pages of this menu and so on. This pages i have created in Site–> All Site Content–>Pages where pages are on same level here.
    Now how to create navigation for this pages. I m confused because how can i group subpages of About or Services and create a menu Or i m going in wrong direction. Is there any way to group pages?

    Please help…

  23. Waldek Mastykarz Says:

    @Sandip: the only way to display levels in the navigation (group pages) is to create subsites and create those pages there. Otherwise, if you want to keep using a single Pages list, you would have to create your own Navigation Provider which would translate a flat Pages list to a hierarchical navigation.

  24. Martin Hansen Says:

    Hi there, still hanging arround :)

    @Sandip
    By default, sharepoint create pages in /pages/ folders in each Publishing Sites.
    A way to face this is to manually move pages with SPDesigner into your own folders.

    Another way is to make your own Navigation like Waldek says in your masters. Maybe consuming an XML and there place your navigation tree. This will be really painful if your pages will be updating often.
    Remember that you have the Navigation config. Enter to the site you want to modify, under Site Actions -> Modify All Site Settings. There you can customize the navigation for each site.

    Hope that help!
    cheers,

  25. Sandip Patil Says:

    Thanks Waldek and Martin for ur last reply i need some more help on below.
    I have used ur navigation and successfully deployed on my site. My site having two variation labels named English and French. As per ur instruction i have created subsite for About and Services in English so it automatically created in French. Now i have put navigation control in English Master page Where it showing hierarchy like this

    English French Press Release Home
    |
    |About
    | |–XYZ
    | |–PQR
    |Services
    | |–XYZ
    | |–PQR

    Same tree like For French like in English.But i need like this.
    Home About Services
    |–XYZ |–XYZ
    |–PQR |–PQR
    Also in subsite About and Services navigation should be same.

    Please help, This is major task in my site.

  26. Martin Hansen Says:

    if you need to change the orientation or position, say hello to CSS Styles! :)
    I see the above images that the control renders HMTL ULs tags, so you can define styles to change that.

    Have a look at this site:
    http://css.maxdesign.com.au/listamatic/

  27. Sandip Patil Says:

    Something i done here but not accomplished my work, MasterPage is in the root site collection and Navigation control is in it. Presently i have done like i want by setting English as Starting Node but due to this French site have same navigation like English(Page links points to English Site pages).
    English and French using same master page here. Same navigation need on English and French but page links should be points to their own pages.

  28. Martin Hansen Says:

    Hi there Sandip,
    Surfing around there are tons of articles talking about this, found this few resources that may help you (i hope :) ).

    About Variations
    http://technet.microsoft.com/en-us/library/cc262404.aspx
    http://technet.microsoft.com/en-us/library/cc262951.aspx
    http://office.microsoft.com/en-us/sharepointserver/HA101207311033.aspx
    http://office.microsoft.com/en-us/sharepointserver/HA101212991033.aspx
    http://office.microsoft.com/en-us/sharepointserver/HA101213141033.aspx

    Some tips on Navigations
    http://msdn.microsoft.com/en-us/library/aa830817.aspx#Office2007SSBrandingWCMPart3_CreatingSiteVariations
    http://aspnetcoe.wordpress.com/2006/09/28/
    http://technet.microsoft.com/en-us/library/cc262404.aspx
    http://blogs.microsoft.co.il/blogs/egady/archive/2009/01/21/sharepoint-site-variations.aspx
    http://aspnetcoe.wordpress.com/2007/02/09/customize-variation-root-landing-logic/

  29. Sandip Patil Says:

    Thanks Waldek for ur great effort on this article. I have solved above point just by changing provider node. Previously that was root node now i set to the current node.

    Old: provider.RootNode;
    New: provider.CurrentNode;

    Change is here
    public virtual void SetStartingNode()
    {
    if (!String.IsNullOrEmpty(StartingNode))
    {
    startingNode = provider.FindSiteMapNode(GetServerRelativeUrlFromPrefixedUrl(StartingNode));
    if (startingNode == null)
    {
    startingNode = provider.CurrentNode;
    }
    }
    else
    {
    startingNode = provider.CurrentNode;
    }
    }
    Hats off genious….!

  30. Sandip Patil Says:

    Hi Waldek
    I want to display starting node too in the navigation. Currently code only render nodes under the starting node.
    Please help…

  31. Raf Says:

    Hi There,

    I converted you nav app into VB.net. This problem that I am getting is that the EVAL portion is not rendering the Title and the URL. I am however able to see the UL-> LI tags. any suggestions?

    Thanks,
    Raf

    P.S Good Article

  32. Waldek Mastykarz Says:

    @Raf: no, not really. There are two things that might be wrong. Either something went wrong while translating the code from C# to VB.NET or there are some minor differences between the languages that would make you use some different syntax.

  33. Raf Says:

    Hey Waldek,

    Thanks for you reply. There is something wrong with the eval. I replace it will regular text and it works fine. If I put the code up will you have a look at it?

    thanks,
    Raf

  34. Raf Says:

    Hi Waldek,

    I have successfully migrated this Menu into VB. I have also incorporated the latest Jquery. There was a minor code error that I did :)

    Thanks,
    Raf

  35. Waldek Mastykarz Says:

    @Raf: Cool! Great to hear that :D

Leave a Reply

Security Code:

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS
Copyright © 2007 - 2010 Waldek Mastykarz

Creative Commons License