Bypassing the Interprise 5.6.22 Menu’s RootNode

by JasonRShaver 7. September 2011 18:53

The default method to handing the asp:Menu is to have a single RootNode (default is ‘Home’) and use hover-over menus to drill down.  In my case, I wanted to have a row of items going across the top of my webpage like so:

image

instead of the default layout that looks like this:

image

Understanding App_Code\ISESiteMapProvider.cs

So, software such as Interprise is special.  It can’t just use a normal SiteMap stuck in a file somewhere.  They need to be dynamic.  The Categories and Manufacturers are obvious, but what about showing something like the nearest store?  Or how about if you are a “Woot” style company and want to have a “Quantity Remaining: 481” link on your menu?  To allow all this functionality, Interprise turned to XmlPackages. 

XmlPackages, and how they work, are outside the scope of this post, but they allow you to take a group of data sources (Query strings, SQL Servers, RSS Feeds, Locale information, etc.) and combine them using XML/XSLT to render content (usually HTML or XML). 

Interprise uses a two step system for its SiteMap.  First the SkinBase gets the contents of XmlPackages\page.menu.xml.config via App_Code\ISESiteMapFactory.cs.  Then it takes that information, localizes some strings (everything with the (!menu.YourAccount!) style syntax) via your string tables, and finally it pulls all your categories, section, and/or manufacturers.  This is all done with magic in App_Code\ISESiteMapProvider.cs.

Now, to reach our goal, you would be thinking that all we need to do is change the starting node used in ISESiteMapProvider, but doing so creates an invalid sitemap (more on this in a bit).  So, we leave this alone for now…

Setting Up the New SiteMap

First thing we need to do is edit the default SiteMap to move ‘Home’ alongside the other categories and create a new empty RootNode.  We need a RootNode because Microsoft’s standard requires one, and only one, RootNode.  While I have linked to the docs, it is good to point out that Urls must be unique, you cannot have two links pointing to the same page in a single SiteMap.

As I mentioned above, the required file is located at XmlPackages\page.menu.xml.config.  Here is an example after moving Home to be along the rest of my nodes and adding a new RootNode to be ignored:

  1. <xsl:template match="/">
  2.     <siteMap>
  3.         <siteMapNode title="this gets ignored">
  4.             <siteMapNode title="Home"  url="default.aspx" />
  5.             <!-- Titles are predefined entities, the contents are not explicitly set here but runtime they are dynamically populated per entity-->
  6.             <siteMapNode title="(!menu.Categories!)" />
  7.             <siteMapNode title="(!menu.Manufacturers!)" url="manufacturers.aspx" />
  8.             <!-- These are static links -->
  9.             <siteMapNode title="(!menu.CustomerService!)" url="t-service.aspx" >
  10.                 <siteMapNode title="(!menu.YourAccount!)" url="account.aspx" />
  11.                 <siteMapNode title="(!menu.FAQs!)" url="t-faq.aspx" />
  12.                 <siteMapNode title="(!menu.PolicyReturns!)" url="t-returns.aspx" />
  13.                 <siteMapNode title="(!menu.Shipping!)" url="t-shipping.aspx" />
  14.                 <siteMapNode title="(!menu.Contact!)" url="t-contact.aspx" />
  15.                 <siteMapNode title="(!menu.PolicyPrivacy!)" url="t-privacy.aspx" />
  16.                 <siteMapNode title="(!menu.PolicySecurity!)" url="t-security.aspx" />
  17.                 <siteMapNode title="(!menu.LeadForm!)" url="t-Leadform.aspx" />
  18.             </siteMapNode>
  19.         </siteMapNode>
  20.     </siteMap>
  21. </xsl:template>

Changing App_Code\SkinBase.cs

Now that we have our new and improve SiteMap, we need to tell our SkinBase class to handle the SiteMapNode information explicitly instead of relying on data binding.  Again, this is required because even if we wanted to replace the ISESiteMapProvider, we still would be stuck with a single root node.

Edit the SetupTelerikMenu() method inside App_Code\SkinBase.cs replacing the commented lines with this block:

  1. // Explicit Menu setup inside SetupTelerikMenu()
  2. SiteMapDataSource ds = new SiteMapDataSource();
  3. ds.Provider = ISESiteMapProviderFactory.Instance.GetProvider(ThisCustomer.LocaleSetting);
  4. //telerikMenu.DataSource = ds;
  5. //telerikMenu.DataBind();
  6. SetupRadMenu(telerikMenu, ds.Provider as SiteMapProvider);

If you are trying to use the asp:Menu control instead of the Telerik control you would want to edit SetupMenu() instead.

Now we just have to add the SetupRadMenu method and its related code.  Again, if you are using the asp:Menu instead, just change instances of RadMenu to System.Web.UI.WebControls.Menu.  Add these methods to your App_Code\SkinBase.cs:

  1. private void SetupRadMenu(RadMenu menu, SiteMapProvider provider)
  2. {
  3.     // Use this one for a single root
  4.     //BuildNodes(menu, null, provider.RootNode);
  5.  
  6.     // Use this one for multiple roots
  7.     foreach (SiteMapNode node in provider.RootNode.ChildNodes)
  8.         BuildNodes(menu, null, node);
  9. }
  10. public void BuildNodes(RadMenu menu, RadMenuItem item, SiteMapNode node)
  11. {
  12.     var CurrentItem = AddNodeToMenu(menu, item, node);
  13.  
  14.     foreach (SiteMapNode CurrentChildNode in node.ChildNodes)
  15.     {
  16.         BuildNodes(menu, CurrentItem, CurrentChildNode);
  17.     }
  18. }
  19.  
  20. private RadMenuItem AddNodeToMenu(RadMenu menu, RadMenuItem item, SiteMapNode node)
  21. {
  22.     var ThisItem = new RadMenuItem();
  23.     ThisItem.Text = node.Title;
  24.     ThisItem.NavigateUrl = node.Url;
  25.  
  26.     if (item == null)
  27.         menu.Items.Add(ThisItem);
  28.     else
  29.         item.Items.Add(ThisItem);
  30.  
  31.     return ThisItem;
  32. }

and that’s it for SkinBase.  Notice that I left the ability to revert back to a single root node just by uncommenting a section of SetupRadMenu and commenting out the other.

Skin Template.ascx

And just for completeness, here is what your Templace.ascx might look like.  Again, I am using a Telerik menu instead of the ASP.NET Menu, but you get the idea I am sure:

  1. <telerik:RadMenu runat="server" ID="aspnetMenu" Skin="TTX" EnableEmbeddedSkins="false" />

Notice that there is nothing ‘special’ here, everything is handled in SkinBase.

Next Steps

If you are following my series here, you will notice that we have a Telerik menu, and now we have complete control over what we are displaying on it, while still using the dynamic items from the ISESiteMapProvider.cs.  But where can we take this?  Here is my next step:

image

But that will have to wait until I get a bit more time to write everything down.  But trust me, it might be awesome.

Tags: , , , , ,

Blog

About the author

I am a software developer working for Microsoft in Redmond, WA.  In addition, my wife and I own TTXOnline, what is likely the 3rd largest table tennis store in the US.

Month List

Page List