Passing Data Between Pages Using the ASP.NET MenuItem
Sometimes, when the user clicks on a menu choice, you want to pass some data based on the page's content. Here's how to dynamically alter a menu control based on the data on the page.
Just when, after the last three columns, I thought I was done with the ASP.NET menu controls, I got another question: "How can I add a querystring to a menu item?" Unlike the question that triggered the first column in the series
("How to have items in the sitemap file appear in the SiteMapPath but not appear on the menu?"), this time I understood the problem.
By default, menus aren't driven by the current content on the page: When a user clicks on a menu item, they get the same URL regardless of the page's content. However, if a user is looking at a page displaying customer "ALFKI" and the user clicks on the Sales Orders menu choice, the user might reasonably expect the Sales Order page -- when it appears -- to be displaying the Sales Orders for customer "ALFKI". One way to make that happen is to pass information in the querystring of the URL associated with the menu item.
Specifically, my reader was asking how to update the Sales Order menu choice so that, on the Customer page, the menu would include the Id of the customer current being displayed (or whatever other information the reader wanted to pass to the Sales Order page). Before looking at the solution, I should mention that there are other options than changing the URL associated with a menu, but they're all less attractive.
For instance, when the user arrives at the Sales Order page, you could display all the Sales Orders in the database... but that's a lot of data to retrieve. Alternatively, you could display no sales orders when the page first displays and require the user to pick a customer from a drop down list at the top of the page. However, the odds are that the user does want to see the sales orders for the last customer viewed so, rather than do nothing at all, why not display those sales orders by default? You could also add a Hyperlink control to the Customer page that links to the Sales Order page and load the Hyperlink's NavigateUrl property with a URL that included querystring with the customer's Id... but that further clutters up the Customer page.
I've also seen some programmers handle this issue by storing the Customer's name in the Session object (typically under a name like "CurrentCustomer").In the Sales Order page's Load event these developers check for a value in the CurrentCustomer Session object and use it to drive the Sales Orders selected when the page is first displayed. When this works, it can be a very elegant solution but, often, making sure that the CurrentCustomer value is correct can be a programming nightmare.
The simplest solution is to alter the menu item. The code for this should go in the page itself since the functionality is specific to the page. To do this, I'll go back to the technique that I showed in my first menu column and update the MenuItem as it's added to the menu in the Menu's MenuItemDataBound event. That event is passed, through the e parameter's Item parameter, to the MenuItem being added. The MenuItem's DataItem property gives you access to the SiteMapNode that defined the MenuItem.
To implement this solution, I begin by converting the object in DataItem property to a SiteMapNode to get at the original URL for the MenuItem. I then check to see if that SiteMapNode points to the SalesOrders.aspx page. If so, I alter the URL used by the MenuItem (accessible through the Item's NavigateUrl property) by adding the CustomerID from a textbox on the page to the end of the string along with the name "CustID". It's conceivable that the CustomerID might contain characters that aren't acceptable in a querystring so I pass the CustomerID through the Server object's MapPath method to get those unacceptable characters converted to acceptable ones. The resulting code looks like this:
Protected Sub Menu1_MenuItemDataBound( _
ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.MenuEventArgs) _
Dim smn As SiteMapNode
smn = CType(e.Item.DataItem, SiteMapNode)
If smn.Url.Contains("SalesOrders.aspx") Then
e.Item.NavigateUrl = smn.Url & "?CustID=" & _
In the SalesOrder.aspx page's Load event, I check to see if the querystring contains a value for CustID and, if so, I use it to retrieve the records for that customer:
Dim custid As String
custid = Me.Request.QueryString("CustID")
If custid IsNot Nothing Then
... .retrieve records for customer...
I really am done now (or, at least, for awhile) with working with menus. I've packaged up all the code from the last four columns into the download for this month (WorkingWithMenus.zip). In my next column, I'm going to take a look at a security issue in ASP.NET.
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/.