I've been spending some time with ASP.NET 2.0 lately. There are many interesting and very useful new features (Master Pages for example being one of my favourites). Some features however require some more effort to make them work as you'd like (as everything, I guess). Two of those are the Menu and the SiteMapDataSource controls.
Basically, what I was trying to achieve was a very basic top-level, horizontal navigation bar for the site I was working on. The first hurdle was getting the Menu control to generate even remotely useful HTML code, as by default it will generates a mess of nested HTML tables somewhat awkward to style using CSS. Fortunately, I had read ScottGu's entry on the CSS control adapters toolkit, so I downloaded and took a look at the code and was able to use that to instead generate
- and
- HTML tags that were be more workable. I did make a few modifications to the default adapter included in the tooklit to wrap my horizontal menu in tags instead of
tags, which allowed me to put some other stuff next to the menu I needed.
With that out of the way, I was running into a little bit of a snag. SiteMaps can only have a single root, which was ok by me and suited the site just fine. The problem was that I wanted the navigation bar to include both the top-level node in the SiteMap (i.e. "Home") as well as all the entries in the second level of the SiteMap (i.e. the real content pages). Having the SiteMapDataSource control driving the menu ignore the root of the SiteMap was not an option, and leaving it as is didn't fit the bill, either.
I could've just used an external XML data source instead, but the SiteMap really worked for my purposes (and was used to drive a SiteMapPath control as well), and well, I'm a sucker for punishement and wanted to check out if I could beat this thing into submition. As it turns out, I could :-)
What I did probably can be called an ugly hack: I created a SiteMapDataSource-derived control that overrides the GetHierarchicalView() method. In it, I grab the SiteMap, clone the root, remove the children and then create a new SiteMapNodeCollection that contains both the cloned root as well as the original second-level items, then return a new SiteMapHierarchicalDataSourceView created from that. Strangely enough it works. Here's what the code looks like (sort of):
public class CustomSiteMapDataSource : SiteMapDataSource
{
protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
SiteMapNode root = Provider.RootNode;
SiteMapNode newRoot = root.Clone();
newRoot.ChildNodes = new SiteMapNodeCollection();
SiteMapNodeCollection collection = new SiteMapNodeCollection();
collection.Add(newRoot);
collection.AddRange(root.ChildNodes);
return new SiteMapHierarchicalDataSourceView(collection);
}
}
I then just had the Menu control get it's data from a CustomSiteMapDataSource object instead of a normal SiteMapDataSource. Note the code above doesn't have any decent error checking and most certainly will always return just the two top levels of the menu, but it worked for my purposes.