views:

374

answers:

2

I have a question similar to the one described here:
ASP.NET URL Routing with WebForms - Using the SiteMap

My ASP.Net WebForms webapplication has a Sitemap, similar to this:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode title="Root">
    <siteMapNode url="Home" title="Home" />
      <siteMapNode url="Home?" title="Home" />
      <siteMapNode url="Faq" title="FAQ" />
    </siteMapNode>
    <siteMapNode url="Reports" title="Your reports" />
      <siteMapNode url="Reports?" title="Your reports" />
      <siteMapNode url="ExtraReports" title="Extra reports" />
    </siteMapNode>
    <siteMapNode url="Presentations" title="Your presentations" />
      <siteMapNode url="Presentations?" title="Your presentations" />
      <siteMapNode url="ExtraPresentations" title="Extra presentations" />
    </siteMapNode>
 </siteMapNode>

I'd like to have the url in the following format:
://host/projects/{company}/{projectno}/Home
://host/projects/microsoft/10/Home
://host/projects/microsoft/11/Reports
://host/projects/apple/10/ExtraReports

The Url routing routes /projects/{company}/{projectno}/{pagename} to /Pages/{pagename}.aspx.

My navigation consists of a TabStrip on top which should contain Home, Reports and Presentations and a Menu on the left with their subitems (e.g. with Home selected it should be: Home, Faq).

My questions:

  1. What is the proper way to handle the duplicate SiteMapNode urls?

  2. How do I prevent the TabStrip from generating urls like: ://host/Home and ://host/ExtraReports?
    I need to maintain the current company and projectno selection, and the sitemap ignores relative urls

  3. The TabStrip and Menu controls need to recognize the selected items to show the active selection. What is the proper solution to all of this?

+1  A: 

Zyphax,

It's most likely that you are going to have to re-write the TabStrip and Menu controls to take account of your routes. This would also mean that you could remove the duplicate SiteMapNodes.

Instead you would need to write some code to traverse the SiteMap tree to get the furthest ancestor which is a child of the Home page. This is an extension method which might help.

public SiteMapNode FurthestAncestor(this SiteMapNode node)
{
    while (node.Key !=  this.RootNode.Key && node.ParentNode.Key != this.RootNode.Key)
    {
        node = node.ParentNode;
    }

    return node;
}

Also using relative URLs will help ../Presentations rather than /projects/apple/10/Presentations

Phil Peace
I've been working on it in the office today. I think I've found a nice solution, which is indeed related to the TabStrip and Menu controls. I'll post it tomorrow. Thnx.
Zyphrax
Look for the solution below..
Zyphrax
A: 

After spending a few hours on this, I think I've come to a good (enough :)) solution.

SiteMap file
Notice the nodes without url will point to the parent

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"&gt;
    <siteMapNode>
        <siteMapNode title="Home" url="Home">
            <siteMapNode title="Home" />
            <siteMapNode title="xxx" url="Cycle"/>
            <siteMapNode title="yyy" url="Survey" />
            <siteMapNode title="zzzteam" url="Team" />
        </siteMapNode>
        ...

Utility class to generate the Url
CurrentContext is a static reference to the RequestContext

public static string GetPageUrl(SiteMapNode node) {
   RouteValueDictionary values = new RouteValueDictionary(CurrentContext.RouteData.DataTokens);
   values["page"] = node.Url.Trim('/');

   return CurrentContext.RouteData.Route.GetVirtualPath(CurrentContext, values).VirtualPath;
}

Databound events in the MasterPage
Navigation is a Telerik Tabstrip, subnavigation is the ASP.Net Menu

private void navigation_TabDataBound(object sender, Telerik.Web.UI.RadTabStripEventArgs e) {
   if (!String.IsNullOrEmpty(e.Tab.NavigateUrl)) {              
          SiteMapNode node = (SiteMapNode) e.Tab.DataItem;

   // Is this tab a parent of the current navigation node? Then select it
   if (SiteMap.CurrentNode.IsDescendantOf(node)) e.Tab.Selected = true;
          e.Tab.NavigateUrl = XRouteHandler.GetPageUrl(node);
   }
}

private void subNavigation_MenuItemDataBound(object sender, MenuEventArgs e) {
    SiteMapNode itemNode = (SiteMapNode) e.Item.DataItem;
    SiteMapNode currentNode = SiteMap.CurrentNode;

    // SiteMapNodes without url will point to the parent node
    if (String.IsNullOrEmpty(itemNode.Url)) {
        itemNode = itemNode.ParentNode;
        e.Item.Selectable = true;
    }

    // Is this menu item the node itself or one of it's parents? Then select it
    if (currentNode == itemNode || currentNode.IsDescendantOf(itemNode))
        e.Item.Selected = true;
        e.Item.NavigateUrl = XRouteHandler.GetPageUrl(itemNode);
    }
}

XmlSiteMapProvider implementation
This solution is good enough for now, will look into it later to prettify it :)

public override SiteMapNode FindSiteMapNode(HttpContext context) {
        string pageName = context.Request.Url.Segments.Last().Trim('/');
        foreach (SiteMapNode node in SiteMap.RootNode.GetAllNodes())
                if (node.Url.EndsWith(pageName, StringComparison.InvariantCultureIgnoreCase)) return node;
        return SiteMap.RootNode;
}
Zyphrax