1. Original Entry + Comments2. Write a Comment3. Preview Comment

February 01, 2012  |  Horizontal menu as a Razor helper  |  41251 hit(s)

On the forums, someone was asking about creating a horizontal menu using Razor code in ASP.NET Web Pages. The technique of combining an unordered list (<ul> element) with CSS to create a horizontal layout, using <a> elements as the menu items, is a pretty well-known one; you can find examples here and here, not to mention in the Site.css file that comes with a lot of the Visual Studio web project templates.

The actual Razor part is likewise not hard. Someone showed this code as a way to dynamically build a list (to which you'd then apply the CSS):

@foreach (var item in collection)
The interesting part was a comment that the original poster made:
It sounds like if I have lots of logic inside the loop, I might be best served moving that to the back end somewhere and just calling a method that returns the text to display within each list item.
What this sounded like to me was a fun excuse to see about creating a custom helper. Helpers are, in effect, chunks of code and markup; when you invoke the helper, it runs the code and emits the markup. For example, by creating a helper to render a horizontal menu, you could do something like this in a page:

<h1>My Page</h1>
<p>Hello, horizontal menu.</p>
And get something like this:

Well, anyway, in the App_Code folder of an ASP.NET Web Pages site (it must be in that folder), I created a file named MyHelpers.cshtml. In the new file I replaced the default contents with the following:

@helper HorizontalMenu(params string[] menuListArray) {

string[] menuItem;
string menulink = "";
string menuLIitems = "";

/* This should all be in a .css somewhere */
font-family:"Segoe UI";font-size:11pt;font-weight:bold;
#horizontalMenu ul {margin:0px; padding:0px;}
#horizontalMenu ul li{
border-right:2px solid white;padding:6px;
#horizontalMenu li a {color:#fff; text-decoration:none;}
#horizontalMenu li a:hover {color:red;}

<div id="horizontalMenu">
@for(int i = 0; i < menuListArray.Length; i++){
menulink = "";
menuItem = menuListArray[i].Split( new Char[]{'|'});
menulink =
String.Format("<a href=\"{1}\">{0}</a>",
menulink = menuListArray[i];
menuLIitems += "<li>" + menulink + "</li>";

Update 7 Feb 2012 Fixed a typo in the code (I'd named the variable menuListArrary instead of menuListArray). I'd used the misspelled variable name consistently, so everything worked ok, but I corrected the name anyway to avoid potential confusion.

Some things of note, I guess:
  • The name of the file (MyHelpers.cshtml) acts as the namespace for any helpers in that file; the name of the helper itself (first list of the previous example) is the method. That's why the earlier example invokes the helper as MyHelpers.HorizontalMenu.

  • The CSS for the menu layout probably should not be in this file; it probably should be in a separate .css file.

  • And/or at least parts of the CSS should probably be parameterized so that you can pass values to it when you invoke the menu. Maybe.

  • You can pass as many items to this as you like (hence the use of the params string[] syntax in the helper method signature).

  • The original poster had noted that in some browsers, if the <ul> and <li> elements are on separate lines, the silly browser puts blank spaces between them. So a goal here was to smush everything together onto one line.

  • The menu text and menu target/link are separated by a pipe character ("|"). This was a quick-and-dirty thing. It might be more elegant to pass, say, 2-element arrays to the helper or something, but I generally think that invoking helpers in a page should be as straightforward as possible.

  • As coded, the links for the menu items have to be absolute. However, they really should be expressed, or at least expressable, using the ASP.NET ~ operator, which returns the virtual root, but which requires the ASP.NET Href method to resolve. I didn't bother.

  • For some reason I thought it was important to have logic in case a menu item was passed to the helper that didn't have a target in it. I can't offhand think of when this would ever be useful, tho.

I'm sure if I gave this another 5 minutes of thought I could improve it. And others could probably give it 10 seconds of thought, ditto. But it was fun while it lasted. :-)

Interesting Finds: February 2, 2012