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)
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).
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.
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.
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.
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.
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:
||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.
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.