Practical ASP.NET

Managing Menus

Peter considers two solutions for keeping items in the sitemap off of your Menu or TreeView controls. But he's also wondering if there are more solutions out there.

I was recently asked by one of the participants taking an ASP.NET course with me if there was a way to prevent an item in the sitemap from appearing on the menu but still have it appear on SiteMapPath (the control that gives you a "breadcrumbs-like" description of where a page fits into the site's hierarchy). I never ask why my course participants why they want to do these things... I just assume there's a good reason and try to be helpful as I can. Then I send them my bill.

I could think of at least two solutions to the problem. I'll cover one in this column (working with the menu control) and one in the next column (creating my own menu provider). However, I'm suspicious that there are other, probably simpler, solutions out there that I haven't considered. If you've got one, post it!

A quick review: To add a menu to a page, you first add a Web.sitemap file to your project with an XML description of the menus and submenus you want. Here's the start of a typical sitemap that defines the root for the menu ("Home"), a main menu item ("Management") and the first submenu item on that menu item ("List Customers"):

?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns=
   "http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
 <siteMapNode url="~/Default.aspx" 
              title="Home"  
              description="Northwind CRM home">
    <siteMapNode title="Management" 
                description="Customer Support">
       <siteMapNode url="~/CustomerList.aspx" 
                    title="List Customers"                
                    description="Customers for region"/>

You then drag the control of your choice (a Menu or a TreeView) onto your page and, from its SmartTag, create a SiteMapDataSource that ties your menu control to the sitemap. Typically, you'll put your menu on the Master Page for your site.

To interfere with what goes onto the menu you want to add code to the menu's MenuItemDataBound event. That event's e parameter passes a ton of information that you can use to control what actually gets displayed. The e parameter's Item property, for instance, gives you access to the MenuItem itself which, in term, gives you access to the MenuItem's parent. Once you have a MenuItem's parent, you can remove the item from the parent's ChildItems collection with the collection's Remove method.

To remove each menu item as it's added, for instance, you could use this code which checks to see if a menu item has a parent before trying to remove it:

If e.Item.Parent IsNot Nothing Then
  e.Item.Parent.ChildItems.Remove(e.Item)
End If

This leaves the root menu item ("Home" in my example) still on the screen. So, to complete this process, the final step is to check to see if the menu has any children under its root and set the menu's Visible property to False if it does not. This code, in the Menu's PreRender event will do the job:

If Me.Menu1.Items(0).ChildItems.Count = 0 Then
  Me.Menu1.Visible = False
End If

While a neat trick, this didn't solve my participant's problem: He only wanted some of the items deleted. There are a couple of ways that could be handled. For instance, if we wanted to check an attribute of the SiteMapNode that generated the menu item, I could convert the MenuItem's DataItem property to a SiteMapNode and check its Url property. This example, for instance, eliminates any MenuItem with a URL that contains "Customer":

Dim smn As SiteMapNode
smn = CType(e.Item.DataItem, SiteMapNode)
If smn.Url.Contains("Customer") Then
  e.Item.Parent.ChildItems.Remove(e.Item)
End If

But, for maximum flexibility, I'd like to control this process through some custom attribute that I add to the SiteMapNode in the sitemap. I'd like to be able to add something like this displayInMenu attribute:

<siteMapNode url="~/CustomerList.aspx"
             title="List Customers"
             description="Customers for region"
             displayInMenu="false"/>

Of course, you could also add some security-related attribute or, perhaps, an attribute related to the user's level of expertise (e.g. "novice", "expert").

Fortunately, the SiteMapNode object has an Item collection that is used for nothing else than retrieving custom attributes (you can't use it to retrieve the standard attributes like url, for instance). If the attribute you ask for doesn't exist on the SiteMapNode no error is raised so this code does the job:

Dim smn As SiteMapNode
smn = CType(e.Item.DataItem, SiteMapNode)
If smn.Item("displayInMenu") = "false" Then
  e.Item.Parent.ChildItems.Remove(e.Item)
End If

I only wish I'd thought of this much detail when the question was asked.

Next column: Building your own menu provider.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events