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?
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:
Doesn’t look that awesome, does it? Now let’s have a look at the rendered HTML:
And this is how it did it:
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.
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)
















June 9th, 2009 at 5:08 pm
Hey Waldek,
how about storing the templates in SharePoint – in web property bag with custom application page to manage them (per web)?
June 10th, 2009 at 7:10 am
@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?
June 11th, 2009 at 10:10 am
It would provide a way to have different menus per site, without having to customise the masterpage / page layout for that.
June 11th, 2009 at 10:30 pm
@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?
June 19th, 2009 at 10:06 pm
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!
August 3rd, 2009 at 2:57 pm
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
August 3rd, 2009 at 8:24 pm
@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.
August 3rd, 2009 at 9:18 pm
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.
September 4th, 2009 at 9:44 pm
@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!
September 5th, 2009 at 9:46 pm
@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.
September 6th, 2009 at 8:14 pm
You're right Waldek..
I'll check that, thanks!
September 6th, 2009 at 11:00 pm
Cactor – sorry – I was away for a couple of days – if you can still use any help I still have everthing to hand
September 17th, 2009 at 1:05 pm
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.
September 17th, 2009 at 1:28 pm
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
September 17th, 2009 at 9:38 pm
@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?
September 21st, 2009 at 10:26 am
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
September 21st, 2009 at 5:38 pm
@Andries: Great to hear the fix was that easy. Thanks for getting back.
September 30th, 2009 at 5:19 pm
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?
September 30th, 2009 at 5:22 pm
Apologies for the last comment – just came across the StartingNode variable in your code comments. Must have missed that before
December 9th, 2009 at 12:43 pm
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,
December 9th, 2009 at 1:35 pm
@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.
February 2nd, 2010 at 11:59 am
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…
February 3rd, 2010 at 11:46 am
@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.
February 3rd, 2010 at 8:18 pm
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,
February 4th, 2010 at 11:30 am
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.
February 4th, 2010 at 1:25 pm
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/
February 4th, 2010 at 4:52 pm
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.
February 5th, 2010 at 2:15 am
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/
February 5th, 2010 at 1:29 pm
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….!
February 12th, 2010 at 5:29 pm
Hi Waldek
I want to display starting node too in the navigation. Currently code only render nodes under the starting node.
Please help…
February 26th, 2010 at 10:22 pm
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
February 28th, 2010 at 1:47 pm
@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.
February 28th, 2010 at 10:54 pm
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
March 1st, 2010 at 3:20 am
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
March 1st, 2010 at 6:26 am
@Raf: Cool! Great to hear that