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:
- Navigation in MOSS explained Part 1
- Navigation in MOSS explained Part 2
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)
 |