Custom Navigation in SharePoint - The Full Monty
The out of the box navigation controls work for many scenarios, but what happens if they don't? What are the options available to have custom navigation in SharePoint (WSS or MOSS)? I am not just talking about look and feel, in fact I am mostly talking about functionality and controlling the HTML output that the navigation renders e.g. you want HTML DIV tags instead of tables with rows and cells, or what about UL and LI tags?
This is something that I have been meaning to write about for some time now, but have never had the chance. I have had so many folk ask how to have more control over the navigation output and so at long last I have decided to document several options. Besides the HTML output control, I have also had scenarios like "the navigation works okay, but in certain scenarios I want it to also wash the dishes, but only if the navigation node is a dishwasher". In this post you will find information on how to solve both of these types of scenarios, and therefore I cover the following options:
- Out of the box controls with custom stylesheets (I won't cover this in much details as this has been done before)
- 3rd party navigation controls
- Use CSS Control Toolkit with out of the box navigation controls
- Develop a custom navigation control which inherits from another
- Develop a custom navigation control from scratch
- Develop a custom navigation control which consumes a datasource control (this one I have most detail on)
Option 1: Out of the box controls with custom stylesheets
This scenario doesn't solve either the issues described earlier, but I just wanted to make sure you were up to speed on navigation in SharePoint before you head down any of the other options. In other words....read this option for background info!
The navigation you see below was done using the out of the box ASP Tree View control with custom CSS styles. Not bad result for no code! In other words, no code development, just standard styling "stuff" in SharePoint.
I am not going to cover how this was achieved and all the various configuration options, but do recommend you read the following posts which do
Out of the box configuration options and how navigation works:
Branding the out of the box navigation:
Here you provide CSS styles for the SharePoint ASP menu control, the ASP .NET menu or ASP .NET tree controls, depending on which one of those you are using. (as a side note, the SharePoint ASP menu control inherits from the ASP .NET menu control to change some small behaviour).
Option 2: 3rd party navigation controls
You may find that purchasing a 3rd party navigation control will be a better option for you if you are looking for a "fancy" or WOW user experience, without having to worry about developing or supporting it yourself. What you are really looking for here is a control which can be used to bind to an ASP .NET navigation data source provider, like the ASP .NET menu, ASP .NET tree and SharePoint ASP menu controls do. In other words, you want it to work just like they do, and simply replace one of the out of the box controls with a 3rd party one.
One such company that provides rich controls is Telerik.
The above screen shot is their RadMenu control (showing various style options). They even have documentation on how to get this control working in SharePoint.
Option 3: Use CSS Control Toolkit with out of the box navigation controls
The out of the box navigation controls emit HTML which is full of tables, rows and cells. In some scenarios customers have requested that instead of HTML tables, they would like to have DIV tags (or any other set of HTML tags). One way of achieving this is through the CSS Control Toolkit. The toolkit consists of control adapters, which are little pieces of logic that you add to your web site to effectively "adapt" an ASP.NET control to render the HTML you prefer. The ASP.NET 2.0 CSS Friendly Control Adapters kit provides pre-built control adapters that you can easily use to generate CSS friendly markup from some of the more commonly used ASP.NET controls.
This will work with ASP navigation and SharePoint navigation controls. Instead of me documenting the steps to get this working in a SharePoint environment, let me rather point you to a blog entry by the Mossman titled "CSS Friendly Control Adapters in SharePoint 2007 (A Walk-Through)", which has the steps documented for you already.
NOTE: when I tried this for demonstration purposes it worked well, except for the fact that it also changed the navigation on the internal SharePoint application pages e.g. site settings etc, and this caused weird behaviour on those pages. Therefore, you might want to modify the source code of the CSS Control Adapter for the menu control to only work on specific scenarios so that it doesn't effect your application pages. The source code for the toolkit is available on CodePlex.
Option 4: Develop a custom navigation control which inherits from another
You develop a custom ASP .NET navigation control which inherits from an existing control i.e. ASP .NET menu, ASP .NET tree or SharePoint ASP menu control. You then have the option of overriding specific functionality to get what you want (for formatting and perhaps functionality reasons). This is exactly what the SharePoint product team did when developing the SharePoint ASP menu, it inherits from ASP .NET menu and changes certain functionality. A good example of how to do this, is found by downloading the source code for the SharePoint ASP menu which the SharePoint team have released here.
NOTE: you may not be able to override all functionality, you will need to play with this to see if you can achieve your goals, otherwise you will need to use one of the other custom navigation options.
Option 5: Develop a custom navigation control from scratch
The next 2 options cover the scenario where you want to control the HTML output whether it be DIV, Table or any another HTML tags. It also covers the dishwasher scenario I mentioned at the top of this article. In the dishwasher scenario you want custom logic in deciding which nodes to render in your navigation, beyond the standard SharePoint logic and rules e.g. only render navigation nodes where the 1st letter of the node, is the same as the first letter of the name of the person viewing the page. Okay, not a great real world example, but hopefully you get the idea.
In this option (option 5), you develop a navigation control in the same way as you develop any other ASP .Net server control, but you call the SharePoint API to get back the navigation nodes required to render your navigation.
You may have some type of recursive function which starts from the current node (current site e.g. SPWeb object) and loops through all child and sibling nodes to render the navigation. Those developers who come from a Microsoft Content Management Server (MCMS) 2002 background will understand this model, because this is essentially the same as looping through channel and posting objects to render the navigation required.
Note: The out of the box navigation controls in SharePoint do not work in this way, they are true ASP .NET navigation controls which get there data (navigation nodes) from a data source control.
Developing a control which does not need a data source control has some draw backs. You need to code various configuration options into your control if you want SharePoint configuration to still be used to define which nodes are rendered e.g. show pages or not, start from the current node, ignore headings, ignore links. I.e. the options which are available through the SharePoint UI to change the navigation.
So where do I start?
- Firstly, you need to be an ASP .Net web control developer who knows how to; develop composite controls by overriding the CreateChildControls method or rendering your own HTML in the render method of the control. If you don't have a clue what I am talking about here.....find a web control developer.
- Get familiar which the SharePoint API, specifically the SPWeb and PortalSiteMapNode and class.
- Read and digest "Increased performance for MOSS apps using the PortalSiteMapProvider" - this will give you an idea of how you access the nodes in the hierarchy so that you can render them.
Sample:
Once again, instead of doing all the hard work here, I am going to point you to a sample developed by Stephen Huen. Download it and take a look at the good sample.
Option 6: Develop a custom navigation control which consumes a datasource control
Like option 5, this allows for complete HTML output control and covers the dishwasher scenario too. However, the implementation is different to option 5.
The difference in this model is that the data source (navigation nodes) are "given" to you by the data source control. You don't need code to get the navigation items and you don't need to worry about showing or hidding navigation items based on navigation configured through the SharePoint UI. The data source will only give you the nodes which you are supposed to render based on the SharePoint navigation configured through the UI. Of course you could still add your custom logic beyond the standard SharePoint logic, which would be one of the reasons for developing this control in the first place.
At a high-level you would do the following to develop this control:
- Create a class which Inherits from HierarchicalDataBoundControl
- Override the PerformDataBinding method
- Override Render method
- Deploy Control into GAC or into local \BIN folder of web application (if local bin, you MUST change trust level in web.config to WSS_Medium or Full)
Instead of walking through every line of code which would make up such a control, I have put together a sample control, which has loads of comments included to help you understand the logic. However, let me give you a broad overview anyway.
Overview of the Sample
The requirement is to have complete control of the rendered HTML for the navigation e.g. nodes represented as a LI tags inside a UL tag, and CSS styles would be used to show navigation level 1, 2, 3 etc. The sample takes the navigation nodes from the data source control and creates an XML document which represents the navigation (this is done in the PerformDataBinding method). Each navigation node in the XML document is represented as a <node> element with attributes e.g. title, url and the navigation hierachy is represented by the node element hierachy i.e. a <node> element can have sibling and child <node> elements. All that is left to do is render your HTML in the Render method, and it is here where you can do what you like.
If a node is current, then we set the attribute "selected" in the XML document to true.
Three important points to be aware of:
- When a page with the navigation control on it performs a postback, the PerformDataBinding method is NOT called. Therefore, on postback you will get an empty navigation control. This is "by design" and I didn't figure out a way to force PerformDataBinding to execute. Instead, I saved the XML document which is created in the PerformDataBinding method into viewstate, so that when a postback is performed, we still have the XML from which to work. I also looked at the size of the viewstate produced by navigation control compared to the out of the box ASP Menu, ASP Tree and SharePoint menu controls and it was smaller (I didn't test all cases, but tried various with a fair amount of navigation items).
- If you use this sample and get no navigation nodes shown / returned, it is probably because your navigation assembly is not in the GAC. If it is in the local bin folder for the web application you need to change the default trust level in web.config from WSS_Minimal to WSS_Medium or Full. I am not going into code access security in this post, which is what this is all about.
- In the sample, I have left the render method "empty", so this is where you need to put in your own rendering logic. At the moment it will just render the XML inside the XML document (see screen shot below). You could use XSLT transformation, or iterate through the XML document and render as appropriate. Your call.
If you add the sample control “as is” to a SharePoint site, you get the following:
Standard Control | Sample Control (render raw XML) |
Download the sample control here:
The sample is based on the HierarchicalDataBoundControl Class Sample found on MSDN, which does things slightly differently, and perhaps in your scenario could be better.
Some ideas for your control:
- Could have a custom property on the control which will insert the CSS style to be used on each level in navigation hierachy e.g. level 1 is a CSS style called "mystyle1", level 2 is a different called "mystyle2", where "mystyle" is set as propety on control.
- Could have a debug flag property on the control which can be used to output the raw XML for debugging purposes.
Disclaimer: This is sample code only, and therefore treat it as such. I have not tested all scenarios!! It works with the ASP Data Source provider control and the SharePoint data source provider, but I did not test it with XML data source provider i.e. I assume you are using it for SharePoint.
Comments
Anonymous
June 17, 2008
Workflow und SharePoint Delivering Modular SharePoint Workflow Functionality (Part 1 of 2) DeliveringAnonymous
June 17, 2008
Workflow und SharePoint Delivering Modular SharePoint Workflow Functionality (Part 1 of 2) DeliveringAnonymous
October 14, 2008
I have created a Site Navigation Control using CurrentNavSiteMapProvider. I am able to display all the site nodes according to he navigation settings defined through modify navigation interface. The only point when it fails is when the user adds a new link to the current navigation and selects open in a new windows option. The link gets displayed but it opens it in the same window. I am unable to find the property which could reaveal that a particular link should be opened in a new window. Any kind of help will be deeply appreciated, ThanksAnonymous
October 16, 2008
Hi Michael, I am a newbie to SharePoint and was trying to understand your sample in option 6. How do you run that sample? Where is the XMLDocument located to add links and where should this class be deployed? Please adviceAnonymous
October 16, 2008
Hitesh, Can you please help me in creating this custom navigation control. Whats the process? Do we create a class library and deploy in GAC and then? ThanksAnonymous
October 20, 2008
This is mostly about page navigations. If you are looking for a category navigation that shows tagged items / documents per taxonomy-based tree-style category, then take a look at: http://www.sharepartxxl.com/products/taxonomy/default.aspx Regards, FrankAnonymous
October 23, 2008
Hitesh - yes, the code doesnt cater for new windows. You will need to look at the class properties to see if the nav item has newwindows flag set and if it has then add that to the XML DOM. Then in your rendering code check this and take appripriate action. When I get a chance I will make the adjustment to the sample here too. Newbie - this sample assumes that you understand .Net server control development. You can either deploy to GAC or to local IIS site bin folder. If you deploy to bin folder you may need to change web.config trust setting to FULL to test it correctly. I find GAC a bit painful for development because I need to always re-add to GAC after compile and also worried about cached copy. If this all sounds confusing, then I suggest looking on MSDN on how to create an ASP .Net server control first. MichaelAnonymous
January 05, 2009
Hi Michael, Can you let me know how did you achieve the first scenario with custom CSS? Are you using a TREEVIEW control, as I want to achieve a similar Expand/Collapse functionality. Can you help me with that. thanksAnonymous
January 05, 2009
yes, the treeview control was used.Anonymous
January 15, 2009
I am trying to use your code for generating horizontal navigation instead of vertical navigation. I was able to implement till the point you have shown here. Do you think this code will work in the above scenario ?Anonymous
March 07, 2009
Hi :) the link to http://odie.sts.winisp.net/Shared%20Documents/CustomNavigationClass.txt dosent work :( can you redirect me to that file?Anonymous
March 09, 2009
Hi, thanks for pointing out the issue in the URL for that file. My source server is down and I am busy relocating my files. Should be done in next day or two.Anonymous
May 20, 2009
Have you tried your control in Shared Mode (Edit Page)? The PerformDataBinding method never gets called and leaves the navigation empty.Anonymous
May 23, 2009
What is the best way to just change the OnClick events in the asp:tree to OnMouseOver ? I have a request that the tree expands on the first mouse click, but then subsequent children use the hover to expand. Same issue the asp:menu control where you change the hover on the first node to a mouseclick, then subsequent children use mouseover.Anonymous
September 09, 2010
To good navigation is here too. www.worldofsharepoint.comAnonymous
November 25, 2010
I tried to use your sample as described in option 6, i created the control then i applied to master page but im getting this error: Object reference not to set an instance of object under NavControl..PerformDataBinding() I guess this error is because the datasource is null, im very confused can you plz tel me how to use this control in masterpage, do am i missing any other controls needs to be created. Your help will bereally apprecaited. Waiting for your reply. Thanks, AffiAnonymous
August 10, 2011
I compiled the dll for 3.5 (runningMOSS 2007 - this should work right). I'm having trouble using the control. This is what I have: <uc1:NavControl id="NavControl1" runat="server" datatextfield="title" datasourceid="XmlDataSource1" /> <asp:xmldatasource id="XmlDataSource1" datafile="navigationTree.xml" runat="server" /> Is this correct? ThanksAnonymous
September 01, 2012
I have set up a SharePoint website (SPWeb) with 3 ASPX site pages. The top navigation bar contains links to the [Home page], [ASPX Page1], [ASPX Page 2] and [ASPX Page3]. Depending on where I am in the site, that is any one of these pages, the corresponding tab is highlighted - showing me where I am. This works fine for the most part but with one exception. The second ASPX page is a web part page containing multiple list view web parts. When I browse to that page, the correct tab is highlighted in the top navigation bar. But when I click on one of the lists in the ASPX page, it goes into the actual list's AllForms.aspx application page and in the top navigation bar, the first tab is highlighted probably because it cannot map any of the tabs in the top navigation bar to the URL of the page that is being viewed. My requirement is that if the user is browsing to any list's application page in the site, the second tab in the top navigation bar is highlighted. How do I implement this?Anonymous
October 21, 2013
Dude Gr8 article, but your links doesn't work :(. Can you please update the links Thank you, Ricky