There are numerous articles out there about how to change the system master page on SharePoint. The master that all pages in the _layouts aka LAYOUTS folder use. In most cases they want to do this to enable a different data source for the top navigation aka global navigation. The average rookie who is in charge of creating a site in Microsoft Office SharePoint Sever using the publishing feature will stumble across this problem and start hacking away at either the original application.master, or even go and implement a custom http handler to exchange the application.master with a custom one on the fly. None of these approaches are best practice nor are they supported by Microsoft. So is it possible to fix the navigation on the SharePoint layouts master page without breaking the system? Sure thing!
The magic trick is called Delegates. On the default.master and application.master you will find elements called <SharePoint:Delegate...
These elements have one sole purpose. to enable you, the developer to exchange the contents of the delegate with something else.
One Delegate is called the TopNavigationDataSource. That one can be found directly in the default.master or in the TopNavBar.ascx which is used in the application.master.
So how do you load your own control into it? Through Features.
You specify a <Control>lement in your elements file which specifies the new control to load on behalf of a delegate. Some delegate accept only one substitution, some allow multiple controls to appear. The TopNav one only allows for one out of obvious reasons. The Control element also has an attribute called Sequence. This attribute is used to determine which control gets loaded. the smaller number wins. So be careful what you specify here. try to stay above 20 to allow for some flexibility.
The following example shows the default data source declaration in default.master, which specifies TopNavigationDataSource as the ControlId value.
<SharePoint:DelegateControl runat="server" ControlId="TopNavigationDataSource">
<Template_Controls>
<asp:SiteMapDataSource
ShowStartingNode="False"
SiteMapProvider="SPNavigationProvider"
id="topSiteMap"
runat="server"
StartingNodeUrl="sid:1002"/>
</Template_Controls>
</SharePoint:DelegateControl>
You can create a feature that overrides the default delegate, adding a folder to the \TEMPLATE\FEATURES directory that specifies an alternate data source.
First create a Feature.xml file that references the assembly to use as the data source and points to another XML file (NavigationSiteSettings.xml) that specifies elements that are part of the feature.
<Feature Id="541F5F57-C847-4e16-B59A-B31E90E6F9EA"
Title="Portal Left Navigation"
Description="Enable global navigation for the top link bar."
Version="12.0.0.0"
Scope="Web"
ReceiverAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
ReceiverClass="Microsoft.SharePoint.Publishing.NavigationFeatureHandler"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="NavigationSiteSettings.xml"/>
</ElementManifests>
</Feature>
The following NavigationSiteSettings.xml file redefines the site map provider to use as the TopNavigationDataSource control, setting 50 as the Sequence attribute value, which specifies that the custom control be used instead for top navigation nodes.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control Id="TopNavigationDataSource" Sequence="50"
ControlClass="System.Web.UI.WebControls.SiteMapDataSource"
ControlAssembly="System.Web, version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Property Name="SiteMapProvider">CombinedNavSiteMapProvider</Property>
<Property Name="EnableViewState">true</Property>
<Property Name="StartFromCurrentNode">true</Property>
<Property Name="StartingNodeOffset">0</Property>
<Property Name="ShowStartingNode">false</Property>
<Property Name="ID">topSiteMap</Property>
</Control>
</Elements>
As you can see, it is pretty simple to swap the underlying navigation. In some cases though you will need to do more than just swap the datasource. Maybe modify the aspnet:menu control or even add some controls to the page. No worries. You can specify your own class as substitute instead of specifying the sitemapdataprovider class and add the controls you want. Just make sure to call the EnsureChildControls in the Init of your class, otherwise your code will run too late and you will get error messages about missing topSiteMap objects on your page.
I've added two samples using WspBuilder, a basic and a more complex one to this article for you to download and try out. The code is pretty simplistic and by no means completed, but should get you started.