Wednesday, November 09, 2011

ASP.Net Custom Menu User Control


ASP.Net provides Menu Control to display top navigation. But the problem with that Menu is its complexity while rendering on the Page. It renders the Menu as html table and each menu item is an individual table.
I have developed a user control to render the top menu using UL and LI tags. This control uses a Repeater to render the menu items. For now I have developed it to render Top level menu items.
CustomMenu.Ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomMenu.ascx.cs" Inherits="UserControls_CustomMenu" %>
  <ul id="menu">
  <asp:Repeater ID="rptMainNav" runat="server" OnItemDataBound="rptMainNav_OnItemDataBound" >
    <ItemTemplate>
    <li id="menuItem" runat="server"  >
        <a href="#" id="menuLink" runat="server" ></a>
    </li>
    </ItemTemplate>
  </asp:Repeater>
  </ul>
  <asp:SiteMapDataSource ID="SiteMapDataSource" runat="server" ShowStartingNode="false" />

CustomMenu.Ascx.cs

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

public partial class UserControls_CustomMenu : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        rptMainNav.DataSourceID = "SiteMapDataSource";
        rptMainNav.DataBind();
    }

    protected void rptMainNav_OnItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        SiteMapNode siteMapNode = e.Item.DataItem as SiteMapNode;
        if (siteMapNode != null && (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem))
        {
            HtmlAnchor menuLink = e.Item.FindControl("menuLink") as HtmlAnchor;
            HtmlControl menuItem = e.Item.FindControl("menuItem") as HtmlControl;
            if (menuLink != null)
            {
                menuLink.HRef = siteMapNode.Url;
                menuLink.InnerHtml = siteMapNode.Title;

                if (BelongsToThisNode(SiteMap.CurrentNode, siteMapNode))
                {
                    menuItem.Attributes.Add("class", "selected");
                }

            }
        }
    }

    private bool BelongsToThisNode(SiteMapNode siteMapNode,SiteMapNode parentNode)
    {
        if (siteMapNode.Url == parentNode.Url)
            return true;
        if (siteMapNode.ParentNode == null || string.IsNullOrEmpty(siteMapNode.ParentNode.Url))
            return false;
        if (siteMapNode.ParentNode != null && siteMapNode.ParentNode.Url == parentNode.Url)
            return true;

        return BelongsToThisNode(siteMapNode.ParentNode, parentNode);
    }
}

Web.sitemap

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="" title=""  description="">
      <siteMapNode url="~/Default.aspx" title="Home"  description="" />
      <siteMapNode url="~/AboutUs.aspx" title="About Us"  description="" >
        <siteMapNode url="~/Leadership.aspx" title="Leadership" description="" ></siteMapNode>
      </siteMapNode>
      <siteMapNode url="~/Services.aspx" title="Services"  description="" />
      <siteMapNode url="~/ContactUs.aspx" title="Contact Us"  description="" >
        <siteMapNode url="~/Career.aspx" title="Leadership" description="" ></siteMapNode>
      </siteMapNode>
    </siteMapNode>
</siteMap>



Register this control on Master page and use it instead of the Menu Control. It is easy to  style this control  using css. The below lines of code
if (BelongsToThisNode(SiteMap.CurrentNode, siteMapNode))
                {
                    menuItem.Attributes.Add("class", "selected");
                }
Will add a css class to the selected Menu Item to display it in different style.