Introduction
AMenu is a simple .Net wrapper for a CSS based "flyout" vertical menu. There are many good articles on the Web describing the workings of CSS menus, e.g.: http://www.seoconsultants.com/css/menus/tutorial. Basically, the menu is implemented using nested UL elements, where each LI contains a hyperlink.
The primary function of the CSS is to hide/show the submenus, and to implement the desired layout. Hiding is done by using either {disaply:none} on the UL element, or moving the element off the screen {left:-5000px}. The submenus are shown by specifying their position in the hover pseudo class using a child selector: li:hover > ul {left:100%}.
The following is a working sample demonstrating the concept. Please note, the IE6 specific code has been ommited for clarity; This sample will work on all browser but IE6.
File test.htm:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>AMenu test</title>
<link href="amenu.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="Menu3" class="amenu">
<ul class="top">
<li class="vpadding_top" />
<li><a href="#">Parts Catalog ...</a>
<ul class="sub">
<li class="vpadding_top">
<li><a href="#">Batteries</a></li>
<li><a href="#">Alternators</a></li>
<li class="vpadding_bottom" />
</ul>
</li>
<li class="vpadding_bottom" />
</ul>
</div>
</body>
</html>
Implementation
The menu is implemented as three WebControl-derived componenets: AMenu, AMenuSub, MenuLink, and the associated style sheet amenu.css.
The AMenu class is responsible for rendering the DIV container and the top-level UL. The MenuLink class renders all LI elements, including the child A element. The AMenuSub renders the UL element for the sub-menus.
The picture bellow shows the final DOM tree. The screenshot was taken from the included demo application running on Firefox.
Internet Explorer 6
Since IE6 doesn't support CSS2, the CSS child selectors used to show the sub-menus won't work. The workaround was to wrap each sub-menu in a TABLE, and making it a child element of the A element (instead of the LI element). This is done by generating conditional comments for each A element. I'm not sure who to credit for this clever solution, I first saw it in the work done by Stu Nicholls (http://www.cssplay.co.uk).
The picture bellow shows the DOM tree as rendered on IE6
Using the Code
The menu components expose the following public properties:
AMenu:
- ArrowImage
- URL of the arrow image. Should be 10x10. Default: none
- BackColor
- Menu background color. Default: #F7F7F7
- BackHover
- Hover background color. Default: #DAE0E4
- BorderColor
- Border color. Default: #C6C6CC
- Font-Bold
- Use bold font? Default: false.
- Font-Names
- Menu font names. Default: Arial.
- Font-Size
- Menu font size. Default: 12px.
- ForeColor
- Menu foreground color. Default: #336666.
- ForeHover
- Hover foreground color. Default: Blue.
- Border
- Draw border for the top-menu? Default: false.
- Height
- Menu height. Default: 10em.
- Width
- Menu width. Default: 190px.
- SubWidth
- Default sub-menu width. Default: 190px.
AMenuSub:
- Width
- Sub-menu width. Default: none.
MenuLink:
- Text
- Text of the menu-item.
- IconImage
- URL of the icon image. Default: none.
- CommandName
- Item's CommandName parameter. Default: none.
- CommandArgument
- Item's CommandArgument parameter. Default: none.
- PostBackUrl
- URL of the page to post to. Default: none.
- Enabled
- Item enabled? Default: yes.
- OnClientClick
- Client-side handler. Default: none.
- ToolTip
- Tooltip text. Default: none.
Events:
The control provides two options for handling the selection events.
On the first level, the MenuLink component accepts an "OnClick" handler. If specified, this handler is called to process the selection, and the event is not bubbled further.
On the second (top) level the AMenu component accepts an "OnItemClick" handler. If specified, this handler is called to process selection from all MenuLinks that have no handler.
The signature of both handlers is the same as that of a LinkButton:
void OnItemClick(object sender, CommandEventArgs e);
Note: The handlers are only called if the CommandName parameter of the MenuLink has been specified. Generally, the usage of CommandName, CommandArgument, and PostBackUrl is the same as for a LinkButton.
The picture bellow shows the event argument parsed by the handler:
Example
Following is the sample from the Introduction but this time using AMenu. Please make sure you've included a reference to the amenu.dll assembly, and the amenu.css stylesheet is accessible.
File test.aspx:
<%@ Page Language="C#" CodeBehind="test.aspx.cs" Inherits="TestPage" %>
<%@ Register TagPrefix="mt" Namespace="mtweb" Assembly="AMenu" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>AMenu test</title>
<link href="amenu.css" type="text/css" rel="stylesheet" />
</head>
<body>
<form id="Form1" runat="server">
<mt:AMenu ID="Menu3" runat="server" Border="true" OnItemClick="OnItemClick">
<mt:MenuLink ID="PartsCatalog" runat="server" Text="Parts Catalog ...">
<mt:AMenuSub ID="SubParts" runat="server">
<mt:MenuLink ID="Batteries" runat="server" Text="Batteries" />
<mt:MenuLink ID="Alternators" runat="server" Text="Alternators" />
</mt:AMenuSub>
</mt:MenuLink>
</mt:AMenu>
</form>
</body>
</html>
File test.aspx.cs:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class TestPage : System.Web.UI.Page
{
protected void OnItemClick(object sender, CommandEventArgs e)
{
string from = ((Control)sender).ClientID;
string cmd = e.CommandName;
string arg = e.CommandArgument as string;
}
}
And the result:
Limitations
There are several limitation, which may, or may not be an issue when you decide to use this control. In any case, by modifying the amenu.css style sheet, and the menu source code one should be able to adapt the control to a specific scenario.
Several properties are not exposed. E.g.: you can't change the default 1px border without modifying the amenu.css style sheet, and the methods in MenuLink.cs where the position of the sub-menus is calculated. The same for the left margin of the MenuLink text (the distance between the icon and the text), or the 2px padding around the menu items, or the 2em line height.
No support for the Visual Studio designer. Dragging the components from the toolbar will not work.
The markup generated is not "fluid". When a user configures her browser to force a specific font size (e.g.: "Minimum font size" in Firefox), the submenus will not be aligned correctly.
Conclusion
For most scenarios the control could be deployed just by changing the color schema and the icons. For specific needs, the source code should be a good starting point to implement your own.
The included demo application may be of interest to those who are just starting with .NET. It uses dynamically loaded user controls (ascx), update panels (including triggers and progress controls) history management, and more.
History
10/05/2009 - initial release.
10/07/2009 - fixed bug in handling PostBackUrl.
10/08/2009 - minor update to the demo application.