Are you a member? sign in or take a minute to sign up

Cancel
User Id: Password:
SyrinxLogo-beulah.png

ASP.NET and ASP.NET MVC Menu Control


This article outlines the usage of the standalone Syrinx ASP.NET menu control that can be used in any ASP.NET or ASP.NET MVC project. This is the same menu control used in our Syrinx CS open source system.

Background of the menu

After working with a variety of web menu controls over the years, I still never felt 100% happy with any one of them. In my previous projects I was using a combination of unordered lists (The UL and LI html elements) and divs, and used jquery to create sub menus. I would just create the raw html of UL and LI elements each time I wanted a menu, but that was getting out of hand. I've had a hard time finding a nice menu element that would actually work in IE 6 through 8, Fire Fox 3+, Chrome and Safari as well as being ASP.NET and open source. So I felt it was time to make an ASP.NET control that would be a simple to use server side control that generated powerful html and jquery script that would work in all the browsers. The one thing I didnt worry about is people who shut off javascript in their browser. When javascript is off, the top level menu would work, but the popups would not function.

My new ASP.NET control meets all my needs. Its easy to use and its very flexible with how it looks, and best of all it works well in IE 6 through 8, Fire Fox, Safari, and Chrome. It has no limitation to the depth of submenus, but this version doesnt attempt to deal with the submenus getting too close to the edge of the browser window. My next level of effort will reposition the submenu if it would be drawn off the edge of the page view. The control also fully supports ASP.NET's theme and skinning features, so you can skin the control in your sites skin files.

It was important for me to build this control to support classes inheriting from it to override and extend its base behavior. We use this in our Syrinx Community Server (SCS). The base menu control is completely stand along and doesnt require anything other than jquery when jquery effects will be used (like fading in/out submenus). We have updated SCS to use this menu and we built an SCS specific control that inherits from this one that extends the menu to have deeper knowledge of SCS's security and content management. You can easily do the same for other project environments to make the menu more integrated with the environment's functionality.

Flexibility in menu markup

The menu is able to create UI and LI style menu items as well as the table based menu system outlined above.  This is used by many web frameworks like Twitter Bootstrap, jQuery UI, and jQuery Mobile just to name a few.  If you're site is not using a framework that provides a menu system you need to use, you should use the original table based menu system and the JavaScript that comes with it to make the menu function.

If you are using a framework with its own menu system and JavaScript to drive it, you can omit the Syrinx Menu JavaScript from the page and use their menu in the browser.  In cases where you are not using the Syrinx Menu JavaScript, you're still using the ASP.NET code to generate the menu items.

Using the Control - Standard ASP.NET

1. Add the control reference to your ASP.NET page:

<%@ Register Assembly="SyrinxMenuV3" Namespace="Syrinx2" TagPrefix="syx" %>

2. Add the control where you want the menu to display:

<syx:Menu runat="server" id="Menu1" Orientation="Horizontal" MenuShowEffect="fade" 
          CssClass="TestMMenu" NumLayers="3" ItemCssClass="TestMMenuItem" ItemNumLayers="2" 
          SubMenuCssClass="TestMSubMenu" SubMenuNumLayers="3" SubMenuItemCssClass="TestMSubMenuItem" SubMenuItemNumLayers="2">
    <syx:MenuItem Text="Opportunities" NavigateUrl="/articles/Opportunities.aspx" />
    <syx:MenuItem Text="Industry News" NavigateUrl="/articles/News.aspx">
        <syx:MenuItem Text="Local News" NavigateUrl="/articles/LocalNews.aspx" />
        <syx:MenuItem Text="International News" NavigateUrl="/articles/IntNews.aspx" />
    </syx:MenuItem> 
    <syx:MenuItem Text="Safety" NavigateUrl="/articles/Safety.aspx" /> 
    <syx:MenuItem Text="Contacts" NavigateUrl="/articles/Contacts.aspx" /> 
</syx:Menu>

 

If you've ever used the ASP.NET control you may notice that this menu's server side html looks very similar to the ASP.NET one. I tried to make the server side html as similar as possible to the standard ASP.NET menu so that it was easy to move over to this one. I've noticed that the standard ASP.NET menu wont work properly in most browsers now. Even IE8 will have difficulties with the standard menu that was working fine in IE7. Both Safari and Chrome dont react well to the standard ASP.NET menu either. So, if you're using the standard menu and are frustrated by compatibility problems you should be able to move over to this control quickly.

Using the Control - ASP.NET MVC

1. At the top of the page, create the menu control and setup its properties:

@{
    Syrinx2.MvcMenu menu = new Syrinx2.MvcMenu("defaultMainMenu", (val) => Url.Content(val))
    {
        PrimaryCssClass = "syrinx-menu",
        CssClass = "MainMenu",
        SubMenuCssClass = "MainMenuSubMenu",
        ItemCssClass = "MainMenuItem",
        SubMenuItemCssClass = "MainMenuSubMenuItem"
    };
}
 

2. Render the menu in the html where needed:

@Html.Raw(menu.Html)

3. Add the syrinx menu JavaScript to a bundle in the sites BundleConfig, which you should place within your Scripts directory:

            bundles.Add(new ScriptBundle("~/bundles/syrinxmenu").Include("~/Scripts/jquery.syrinxmenu.js"));


4. Ensure the bundle is included on your layout template:

        @Scripts.Render("~/bundles/jquery", "~/bundles/syrinxmenu")

 

Menu Item Details

While there is some similarity with the standard menu, my menu has some different options. One are that is substantially different is the 4 different NumLayer attributes shown in the example. The main menu, its menu items, the sub menu and the sub menu items are all wrapped with at least one DIV element. How many div elements wrap the various menu elements is controlled by its corresponding NumLayers attribute. This is an important aspect to stylizing the control's look. With multiple DIV elements you can use css background images to box up the element while supporting various sizes of menu elements. I'll discuss the "DIV layering" approach and how to apply it later in this article.

The standard ASP.NET web form user control can use menu item markup within its body as shown in the getting started example above.  Both the ASP.NET user control and the MVC class can be populated using an XML file that contains the same type of markup used in the body of the ASP.NET user control (minus the namespace prefix).  The following is an example of the external xml file defining a menu:

<?xml version="1.0" encoding="utf-8" ?>
<Menu id="exampleMenu" Orientation="Horizontal" MenuShowEffect="fade"
          CssClass="TestMMenu" NumLayers="3" ItemCssClass="TestMMenuItem" ItemNumLayers="2"
          SubMenuCssClass="TestMSubMenu" SubMenuNumLayers="3" SubMenuItemCssClass="TestMSubMenuItem" SubMenuItemNumLayers="2">
    <MenuItem Text="Opportunities" NavigateUrl="/articles/Opportunities.aspx" />
    <MenuItem Text="Industry News" NavigateUrl="/articles/News.aspx">
        <MenuItem Text="Local News" NavigateUrl="/articles/LocalNews.aspx" />
        <MenuItem Text="International News" NavigateUrl="/articles/IntNews.aspx" />
    </MenuItem>
    <MenuItem Text="Safety" NavigateUrl="/articles/Safety.aspx" />
    <MenuItem Text="Contacts" NavigateUrl="/articles/Contacts.aspx" />
</Menu>

To build up the navigation elements within the menu, you embed MenuItem elements within the menu element. As shown in the example above, the MenuItem supports menu items defined within it, and just like the standard ASP.NET control, this is how you define sub menus. There is no limit to the depth, so a menu item can have menu items within it, that have menu items within them and so on.  The syx:MenuItem used within the standard ASP.NET user control is the same as that used in the external file and thus they have the same properties and abilities.

The base MenuItem class has the following properties:
 

Text This is the actual display value to use for the menu item and can contain rich html as needed.
ImageUrl This will place an image to the side of the text. The image will not be resized in any way, but will have styling applied to get rid of the image border. If you want all text to align properly you should use the same size image for all menu items in a given submenu. Menu items that dont have an ImageUrl will have a span to the side of the Text with a class name of "nimg", which you can use to set a padding-left equal to the size of the Image and padding so that all menu items in a given submenu will align properly. This is discussed more below in the section on properly setting up the menu css.
NavigateUrl This the location the menu item will navigate to when the user clicks the item. The menu uses real html anchor elements for the menu items, so this value is used for the anchor's href. This can contain javascript via the javascript: url.
Target This controls which browser window will be used when the user navigates to a new location by clicking the menu item. This value goes directly into the html anchor's target attribute.
Visible This controls if the menu item will actually be rendered in browser html. By setting this value to false, the html for the menu item will not be rendered and not sent to the browser.

 All of these properties are marked as virtual, so you can create new classes that inherit from the MenuItem class to extend the behavior of menu items.

The syx:Menu class has a variety of properties that help control its behavior.
 

Orientation Horizontal or Vertical for the top level menu.
MenuShowEffect The value can be display, fade, slide or toggle. The display value will just change the css display attribute, where as the face, slide and toggle values use the corresponding JQuery effect to show/hide submenus.
ExternalLinkDefaultTarget When menu items point to external sites, you can set the target attribute for all external menu item links with this one value. If a menu item has its own Target property set it will override this property. This property can be a big help in managing a bunch of external links in a menu without having to ensure each menu item's target property is setup properly.

Controlling the Menu's Style with Div Layers

As mentioned earlier, the menu exposes "NumLayer" properties for the top level menu, its menu items, the sub menus and the sub menu items. The following diagram show the overall html generated for both top level menus and submenus.

I've read many articles about building menus with UL/LI html elements in order to have flexibility in overall layout style via CSS, but after using that approach for a few years I find that the hassle of getting the css correct to work in all browsers just isnt worth it. This menu control uses tables to ensure a general proper layout for horizontal and vertical menu types. When you're starting with an ASP.NET control that is generating html based on server side control properties, there is just as much flexibility in layout options but with significantly less hassles with it looking proper in ie6 and all the modern browsers. There is still a significant enough users of older browsers out there to keep core things like menus working well in them. IE6 just works better with tables than floating LI elements or LI elements with their display css set to inline (you cannot even use the more modern inline-block display type because IE6 doesnt recognize it). The use of tables also allows the div layer mechanism to shine with menus.

In the above diagram, you'll notice that the menu table is contained within the "menu div layers", which is one or more divs contained within themselves. The only reason to have more than one div wrap the menu table is when you want to have the menu contained within an image. The follow menu example shows a typical usage of div layers:

This menu is used 3 DIV elements layered within themselves. Each of the DIV elements is setup with the css background-image as shown below:

 

.TestMenu {background: url(images/MenuBg.jpg) repeat-x top left;} 
.TestMenua {background: url(images/MenuLeft.jpg) no-repeat top left;} 
.TestMenub {background: url(images/MenuRight.jpg) no-repeat top right;}

 

The css for this menu allows the table for the menu items to grow in width without running out of background image. In this example, the table items must stay within the height of the graphic. If the text gets to tall, it can spill out of the image containing it. The 3 layer DIV wrapper shown in this example works well for horizontal menus only. When wrapping a virtual submenu, both the width and the height need to be expandable within the image box, and either 5 or 9 layers of DIV elements would be needed to make a image box that can expand in width and height properly.

When you specify the NumLayers property of the syx:Menu, it will use the CssClass name as a part of all the DIV elements wrapping the table. The outer most div will use only the value used in CssClass (such as "TestMenu" used in the example above), while the inner divs will be be the CssClass value with the letter a through z appended to it for each successive inner layer. In the example above, you can see there is a TestMenu, TestMenua and TestMenub class being used for the 3 layers wrapping the menu table.

Menu Items can also be wrapped in some number of div layers so that each menu item can also be wrapped. This helps with a tabbed look, as shown in this example below:


 

Each of the menu items in this example are wrapped with three layers of DIV elements similarly to how the first example above was wrapping the entire menu. This allows each menu item width to be as big as needed without worrying about overflowing the "tab". Layering the menu items like this is controlled via the syx:Menu's ItemNumLayers and ItemCssClass properties. The CSS names used follow the same pattern as discussed earlier for the first example, so if the ItemCssClass property is set to "MainMenuItem" and ItemNumLayers is set to "3", then MainMenuItem, MainMenuItema, and MainMenuItemb would be used for the css class names.

The popup submenus and their menu items can be wrapped in div layers by using the SubMenuNumLayers, SubMenuCssClass, SubMenuItemNumLayers and SubMenuItemCssClass properties of the syx:Menu.

The DIV layering approach provides an incredible about of flexibility in how the menus can look, and it will look and work properly in IE6 and all the major browsers too.

Overall Menu CSS

CSS attributes that are needed for the menu to work properly is specified within embedded style attributes on the various html elements generated by the syx:Menu. Each menu item is actually an html anchor element with content within it. In order for the menu item to be clickable along the entire width of a vertial menu (versus just the content within the menu), the anchor needs to have display:block;white-space:nowrap; css on it. The following example shows a verticle menu to help explain this:


 

The popup submenu with its first menu item highlighted is clickable along the entire width of the submenu. If the anchor didnt have the display:block style applied to it, it would only be clickable on the text area. Because the syx:Menu puts this type of css directly in the style attribute of the anchor element, you dont need to worry about it in your css file for the overall look of the menu. This makes it much easier to just focus on the way the menu looks without worrying about missing CSS needed just for the basic menu to operate properly. I've found that when a menu control makes the developer put all the control css directly in their own css file, it takes longer to make the menu work properly as you can easily forget to put the type of styling needed just to make the menu function properly.

Because the syx:Menu control embeds the controlling css directly within the html, your css files only need to contain real styling info. The CSS for the black menu in the image above, which doesnt need any div layering is as follows:
 

 

.TestMMenu {
background-color:Black;
border-top:solid 1px gray;
border-bottom:solid 1px gray;
}

.TestMMenuItem a {
 padding: 10px 12px 10px 12px;
    font-size:20px;
    text-decoration:none;
    color:White;
}
.TestMMenuItem a:hover {
 padding-bottom: 6px;
 border-bottom:solid 4px #F3B900;
    color:#F3B900;
}
.TestMSubMenu {
 background-color:black;
 padding:5px 0px 5px 0px;
}
.TestMSubMenuItem a{
 color:white;
 padding: 3px 10px 3px 10px;
 text-decoration:none;
 font-size:14px;
    font-weight:bold;
}
.TestMSubMenuItem a:hover{background-color:#F3B900;}

The above CSS covers the top level menu and the submenus.

Using ASP.NET Theme and Skin Options

At the beginning of this article, the syx:Menu is shown with all of its attributes embedded in it. This works fine if you're only going to be using one look for a given site, but if you want to take full advantage of the ASP.NET theme and skin feature, the menu will support the SkinID as the following example shows:

<syx:Menu runat="server" id="Menu1" SkinID="MainMenu"> 
    <syx:MenuItem Text="Opportunities" NavigateUrl="/articles/Opportunities.aspx" /> 
    <syx:MenuItem Text="Industry News" NavigateUrl="/articles/News.aspx">
        <syx:MenuItem Text="Local News" NavigateUrl="/articles/LocalNews.aspx" /> 
        <syx:MenuItem Text="International News" NavigateUrl="/articles/IntNews.aspx" /> 
    </syx:MenuItem> 
    <syx:MenuItem Text="Safety" NavigateUrl="/articles/Safety.aspx" />
    <syx:MenuItem Text="Contacts" NavigateUrl="/articles/Contacts.aspx" />
</syx:Menu>

Once the skinID attribute is specified, all the other appearance attributes of the menu should be specified in the skin file for the ASP.NET theme. For this last menu example, the skin file would contain the following:

 

<syx:Menu runat="server" SkinID="MainMenu" Orientation="Horizontal" MenuShowEffect="fade"
       CssClass="TestMMenu" ItemCssClass="TestMMenuItem" 
       SubMenuCssClass="TestMSubMenu" SubMenuItemCssClass="TestMSubMenuItem" />

 

All of these options work together to make the syx:Menu ASP.NET control very flexible yet easy to use.