My Lord, it's been a while! 2009 has gotten off to a tumultuous start for me: water damage in my condo, two home improvement projects, two failed mortgage refinances, two internal projects at work, and two public facing SharePoint web sites. With all that going on, when could I have had to philosophize about SharePoint?
When it comes to public facing SharePoint, we get into all kinds of things beyond what you need to do for a standard intranet. These include cross-browser support, an unwavering standard for look & feel, and more AJAX than I care to think about.
In the two aforementioned SharePoint projects, both called for heavily customized navigation. And I don't just mean changing the order of the top bar's links. We're talking gradient images, precise CSS-based layout, and, of course, the ability to traverse sites, sub sites, and pages.
The major problem is (and this will be no surprise) that SharePoint comes up with some...interesting...HTML for it's navigation menus. Basically, the default is that each menu item is a table nested in an outer table. You do have the opportunity to set some CSS class names and set some properties on the data sources, but, especially when we're talking about custom branding or public facing sites, this isn't generally going to be enough to implement the client's designs.
I've come up with a really quick way to hook into the SharePoint navigation subsystem and output your HTML from scratch, allowing for the most flexibility in your customization requirements. But first, just a bit of high-ish level overview of how navigation works in SharePoint.
It all starts in the web.config, where there are a number of providers defined in the system.net section. They look like this:
If you are using WSS only, then the navigation, breadcrumb, and menu controls defined in default.master are directly wired up to the providers. In MOSS, there is another layer of indirection: data sources. In these master pages (and, with MOSS, layout pages as well) you see these navigation controls wired to a PortalSiteMapDataSource, which is then itself connected to one of the aforementioned providers.
This provides the interface to tie into all of the addition navigation customization you can do in MOSS. If you really want to dig to the deepest level here and completely circumvent all menu rendering, then you can directly code against static methods in PortalSiteMapDataSource, as well as several other goodies in the Microsoft.SharePoint.Publishing.Navigation namespace. You can also tie into the publishing infrastructure for additional tweeking. Here are some examples...
Directly iterate the current site's child navigation nodes, completely from scratch:
Code against the custom links and ordering set up in a MOSS site's navigation settings page:
Finally, let's go back to the quick-and-easy way when assuming that the current navigation is correct, and we just want to control the HTML more explicitly. First, create a new user control designed to live directly on the master page. To do this, follow these steps:
Now that everything's wired up, we can finally get to this code! My technique is to pass the data source from the master page directly the user control. The control then creates a temporary menu control, binds it to the data source, then iterates it (and it's children) to build whatever UI is deemed best-suited for the look & feel.
Here's the shell of the user control, with some practical customization ideas / examples:
And what the mater page's code behind will look like:
One last thing: what about WSS? All this master page stuff and publishing stuff and custom navigation stuff (and basically cool stuff) is MOSS-only. That's not to say that there's nothing we can do in WSS to customize these aspects of your portal.
You can hack up default.master (on the file system at "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\GLOBAL\default.master") to add your own functionality beyond what you can do with themes and web parts. Also, the trusty SPWeb object does have some hooks into WSS navigation. Check out the Navigation property of an instantiated web, and you'll see access to the top, global, and quick launch nav, and more!
That's it! Have fun navigating!