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

  • What's New in Visual Studio 2019 v16.5 Preview 2

    The second preview of Visual Studio 2019 v16.5 has arrived with improvements across the flagship IDE, including the core experience and different development areas such as C++, Python, web, mobile and so on.

  • C# Shows Strong in Tech Skills Reports

    Microsoft's C# programming language continues to show strong in tech industry skills reports, with the most recent examples coming from a skills testing company and a training company.

  • Color Shards

    Sharing Data and Splitting Components in Blazor

    ASP.NET Core Version 3.1 has at least two major changes that you'll want to take advantage of. Well, Peter thinks you will. Depending on your background, your response to one of them may be a resounding “meh.”

  • Architecture Small Graphic

    Microsoft Ships Preview SDK, Guidance for New Dual-Screen Mobile Era

    Microsoft announced a new SDK and developer guidance for dealing with the new dual-screen mobile era, ushered in by the advent of ultra-portable devices such as the Surface Duo.

  • How to Create a Machine Learning Decision Tree Classifier Using C#

    After earlier explaining how to compute disorder and split data in his exploration of machine learning decision tree classifiers, resident data scientist Dr. James McCaffrey of Microsoft Research now shows how to use the splitting and disorder code to create a working decision tree classifier.

.NET Insight

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.

Upcoming Events