I’ve been following a few forum posts about modifying the built-in navigation providers in SharePoint and thought I’d knock up a quick post explaining, step-by-step how this can be done.
Unfortunately, the documentation at MSDN is a bit cryptic (Although you’ll see by the end of this article that it makes perfect sense!)
As usual, I won’t get into details about creating and deploying features. WSPBuilder is your man. (Although I’m finding SPVisualDev to be a handy wee tool as well these days)
So what’s the plan then?
On the default master page, you’ll see code that looks a bit like this:
You’ll notice that the datasource for the QuickLaunchMenu (and all the other menus if you dig around the file a bit), is a delegate control. Good stuff, I hear you say, that means we can simply override it with our custom implementation and we’re done!
So here’s how you implement the delegate:
Create a new feature that looks a bit like this (We’ll use the XmlDataSource control for simplicity, you could use anything or build your own control that implements IHierarchicalDataSource) :
<?xml version="1.0" encoding="utf-8"?>
<Feature
Id="D6238DAF-A017-4465-8261-1E136EE38D24"
Title="NavTestFeature"
Description=""
Version="1.0.0.0"
Scope="Web"
Hidden="False" xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="NavigationDelegate.xml" />
<ElementManifest Location="Sitemap.xml" />
</ElementManifests>
</Feature>
You’ll see that I’m referring to two manifest files:
This is sitemap.xml, this copies a file to use as the sitemap. I’m using books.xml from MSDN:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Module" Path="Module" Url="_catalogs/masterpage" xmlns="http://schemas.microsoft.com/sharepoint/">
<File Url="books.xml" Type="GhostableInLibrary" />
</Module>
</Elements>
This is NavigationDelegate.xml:
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="QuickLaunchDataSource" Sequence="50"
ControlClass="System.Web.UI.WebControls.XmlDataSource"
ControlAssembly="System.Web, version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Property Name="ID">QuickLaunchSiteMap</Property>
<Property Name="DataFile">books.xml</Property>
<Property Name="XPath">books/computerbooks</Property>
</Control>
</Elements>
The key things to note about the delegate file are:
- The Sequence number – the lower the number the higher the priority. If you had a nother delegate with a lower number it would be shown instead of this one.
- The ControlClass – This is your iHierarchicalDataSource implementation.
- The Property elements – These are used to set the properties on your data source.
- The value of the ID property – This must match the navigation control on the master page. If you look at the excerpt from default.master you’ll see that the DataSourceID on the AspMenu control is QuickLaunchSiteMap.
Once you have these two files, create a folder called Module with an xml file called books.xml in it.
Books.xml
<?xml version="1.0" encoding="utf-8" ?>
<books>
<computerbooks>
<book title="Secrets of Silicon Valley" author="Sheryl Hunter"/>
<book title="Straight Talk About Computers" author="Dean Straight"/>
<book title="You Can Combat Computer Stress!" author="Marjorie Green"/>
</computerbooks>
<cookbooks>
<book title="Silicon Valley Gastronomic Treats" author="Innes del Castill"/>
</cookbooks>
</books>
Your Feature should look a bit like this:
That’s pretty much it, install your feature and take a look at your customised menu.
It’s never that simple though!
When you open your page you’ll get an error “The DataSourceID of 'QuickLaunchMenu' must be the ID of a control of type IHierarchicalDataSource. A control with ID 'QuickLaunchSiteMap' could not be found”
What the error doesn’t really tell you though is the reason why the datasource couldn’t be found. By default the XmlDataSource (and all the other standard asp.net SiteMapDataSources) are disabled in web.confg.
You can fix this by changing:
<SafeControl Assembly="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="System.Web.UI.WebControls"
TypeName="XmlDataSource" Safe="False"
AllowRemoteDesigner="False" />
To this:
<SafeControl Assembly="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="System.Web.UI.WebControls"
TypeName="XmlDataSource" Safe="True"
AllowRemoteDesigner="False" />
Got there in the end
Not exactly impressive, but I hope it’s a bit more coherent that the MSDN help (which I’m sure you’ll agree, when looking at it now, makes perfect sense)