Extending ASP.NET 2.0 Menu Control To Have Tabs With Rounded Corners

Introduction

Image1.gif

Ever get tired of the square tabs on the .Net Menu Control? I did. This article combines several cool works done by others on the web to extend the .Net 2.0 Menu control to produce rounded tabs using no images for the corners.

Technologies explored in this article include:

  1. Using CSS styles to produce rounded corners without the need for corner images.  See More Nifty Corners  for the technique used.
  2. Inheriting the Menu control and overloading it's Render method to produce CSS friendly html elements in which to apply the rounded corner technique above. I borrowed a lot of the code from ASP.NET 2.0 CSS Friendly Control Adapters 1.0 but instead of an Adapter I transformed it into a custom web control.
  3. Packaging this customized Menu control, along with it's CSS styles and javascripts into a separate dll that can be shared between projects.  These CSS styles and javascripts are included into the dll as Web Resources.


Approach Used To Create The Rounded Corners on the Menu's Tabs

Nothing makes a web site look polished like rounded corners. This includes the menu tabs. I've seen many techniques for creating rounded corners but none as elegant as More Nifty Corners . This article shows you how to produce rounded edges w/o the use of corner images. 

After reading this article, I was fired up to apply the techniques to menu tabs, however the aspx menu control uses tables which are not compatible with this technique. So I needed a method of changing these table tags to something that could be styled more easily. After, changing the tags, there was no need to change the main javascript file or the CSS file the technique uses. However, I did want to package these scripts into the control's dll to make it easier to use.

Creating a CSS friendly Menu Control is a matter of inheriting the ASP Menu control and overloading it's "Render" method to generate CSS friendly code. Fortunately, I came across: ASP.NET 2.0 CSS Friendly Control Adapters 1.0  that saved me from  creating the code from scratch. This provided code for creating an Adapter for the Menu control that generated CSS Friendly html output. Adapters are cool but there seemed to be more configuration required than if you just had a custom control that did the same thing.  Fortunately the way an adapter works is pretty similar to how a custom control works; so I was able to copy the code into an overloaded Render method of my Custom Control that was derived from the Menu Control.  Only a few minor tweaks were required to complete the transformation from adapter to custom web control. I was also able to use all of the client side javascript w/o any changes or any need for additional client side code. 

I made a several changes to the CSS style sheet. Mostly deleting the tags, which weren't being used to make it a little easier to follow. 

My goal was to make this rounded tab menu easy to use, without a lot of configuration. It seemed the way to do this was to put as many of the scripts the control uses within the control itself. I elected to put the server side code, the CSS and javascript fils to create the rounded corners and the javascript file for the menu into the control's dll. This dll can be easily referenced from a web project. When the control is used these scripts are automatically included on the page. In addition, the web project will need to include one CSS file to format the menu (colors of the tabs ect) as well as two small images that are used to indicate which menu item's have child menu items.

Download Files

The download includes two projects. One sample web project "MenuRoundedTabsWebSite" which uses the custom control and one project containing the custom menu control.  The web project was created using Visual Web Developer 2005 and the custom menu control was created using Visual C# 2005 Express Edition.  To open up the web site in Visual Web Developer go to the file menu , Open Web Site then navigate to the "MenuRoundedTabsWebSite" folder.

The Code

I'll go over the main features of the code, download the code if you want to see all of the code:

Including script files as Web Resources

The script files, which are included as web resources in the control have their build action set to "embedded resource" (see figure 1).

Image2.gif

Figure 1- Files included as web resources have their build action set to "Embedded Resource"

Also for these web resources to be available the following lines of code needs to be added to AssemblyInfo.cs

Next "Assembly Info.cs" file needs to have the following added to it's end(Figure 2) :

[assembly: System.Web.UI.WebResource("CustomControls.Resources.Menu.css", "text/css")]

[assembly: System.Web.UI.WebResource("CustomControls.Resources.layout.css", "text/css")]

[assembly: System.Web.UI.WebResource("CustomControls.Resources.niftyCorners.css", "text/css")] 

[assembly: System.Web.UI.WebResource("CustomControls.Resources.layout.js", "text/javascript", PerformSubstitution = true)]

[assembly: System.Web.UI.WebResource("CustomControls.Resources.MenuAdapter.js", "text/javascript", PerformSubstitution = true)]
[assembly: System.Web.UI.WebResource("CustomControls.Resources.nifty.js", "text/javascript", PerformSubstitution = true)]

Figure 2 - Coded to be added to Assembly Info.cs

These web resources are used in the control's overload "Init()" method (See figure 3)

 

Figure 3 shows the overloaded Init() method and the private RegisterScripts() method it calls.

/// <summary>
/// Call when the control is first created
/// </summary>
/// <param name="e"></param>

protected override void OnInit(EventArgs e)

{

    base.OnInit(e);

    //check to make sure we don't add the scripts twice; if more than one control on the page

    if (ViewState["MenuRegisterScripts"] == null)

    {

        RegisterScripts();

    }

    ViewState["MenuRegisterScripts"] = true; 

}

/// <summary>

/// Load the scripts from the WebResources

/// </summary>

private void RegisterScripts()

{

    string csslink=null;

    LiteralControl include = null;

 

    // add main javascript file required to round the corners to the header

 

    HtmlGenericControl script = new HtmlGenericControl("script");

    script.Attributes["type"] = "text/javascript";

    script.Attributes["src"] = Page.ClientScript.GetWebResourceUrl(this.GetType(), "CustomControls.Resources.nifty.js");

    this.Page.Header.Controls.Add(script);         

           

    // add the css files to create the rounded corners

    csslink = "<link rel='stylesheet' type='text/css' href='" + Page.ClientScript.GetWebResourceUrl(this.GetType(),

    "CustomControls.Resources.niftyCorners.css") + "' />";

    include = new LiteralControl(csslink);

    this.Page.Header.Controls.Add(include);           

      

    csslink = "<link rel='stylesheet' type='text/css' href='" + Page.ClientScript.GetWebResourceUrl(this.GetType(),

    "CustomControls.Resources.niftyPrint.css") + "' />";

    include = new LiteralControl(csslink);

    this.Page.Header.Controls.Add(include);          

 

     // register the javascript code that specifies the format for these rounded corners

    // this script also specifies the colors for the corners

    if(!Page.ClientScript.IsClientScriptIncludeRegistered(Page.ClientScript.GetWebResourceUrl(this.GetType(),

    "CustomControls.Resources.layout.js")))

    {

        this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "Layout", Page.ClientScript.GetWebResourceUrl

        (this.GetType(), "CustomControls.Resources.layout.js"));

    }

 

    // register the javascript code that specifies the format for these rounded corners

    // this script also specifies the colors for the corners

    if (!Page.ClientScript.IsClientScriptIncludeRegistered(Page.ClientScript.GetWebResourceUrl(this.GetType(),

    "CustomControls.Resources.MenuAdapter.js")))

    {

        this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "MenuAdapter", Page.ClientScript.GetWebResourceUrl

        (this.GetType(), "CustomControls.Resources.MenuAdapter.js"));

    }

 

    //add the css script used by the Menu to the header

    csslink = "<link rel='stylesheet' type='text/css' href='" +

    Page.ClientScript.GetWebResourceUrl(this.GetType(),

    "CustomControls.Resources.Menu.css") + "' />";

    include = new LiteralControl(csslink);

    this.Page.Header.Controls.Add(include);
}

Figure 3-Method's used to add the required scripts to the page.
 
Overloading the Render Method To Generate CSS Friendly Code

Figure 4 shows the over loaded "Render" method. The "RenderContents" method (not shown) loops through each of the menu items and creates html ul and li tags to separate them.  You can download the code to see the implementation of the "RenderContents" method.

protected override void Render(HtmlTextWriter writer)
{
    writer.WriteBeginTag("div");
    writer.WriteAttribute("class", "PrettyMenu");
    writer.WriteAttribute("id", this.ID);
    writer.Write(HtmlTextWriter.TagRightChar); 

    writer.WriteBeginTag("div");

    writer.WriteAttribute("class", "AspNet-Menu-Horizontal");

    writer.Write(HtmlTextWriter.TagRightChar);

           

    RenderContents(writer);

 

    writer.WriteEndTag("div");

    writer.WriteEndTag("div");
}


Figure 4 - Custom's Control Overloaded Render method

Changing the Colors for the Menu's Tab

By altering the layout.js file in the dll's Resources folder, you can change the menu's tab colors. 

Rounded("a.AspNet-Menu-Link-Parent","top","#E8BD8A","#7795BD","border #4B463B")

The third parameter should be the background color of what ever is containing the menu.  The forth parameter should be set to the color of the tabs and the final parameter should be the border color of tabs.

See More Nifty Corners  for additional instructions.

Configuring Your Site To Use Curved Tab Menu Control

When using this menu control for a web project, there is very little configuration required. First you set a reference to the dll containing the menu control. You register this custom control on the web page:

<%@ Register TagPrefix="Custom" Namespace="CustomConrols" Assembly="CustomControls" %>

You then use the control by adding the following:

<Custom:RoundedMenu ID="RoundedMenu1" runat="server" datasourceid="ExampleSiteMapDS" orientation="Horizontal" ></Custom:RoundedMenu>

Then you need to add the CSS file which formats the menu and a few small images which are used in this CSS file into your Theme folder:

Image3.gif 

Finally you need to tell your page where to find the above files:

<%
@ Page Language="C#"  Theme="basic" %>

Note: "Theme" is set to "basic" which is the sub folder containing these files.

That's about all that's required except for changing the MenuCurvedTab.css file to format the tabs to use your site's color schemes.

Conclusion

The code presented illustrates how a control's render method can be manipulated to alter the HTML output.  Also, it shows how cool scripts such as rounding the corners can be encapsulated into the custom control to make them more usable.

The customized menu control presented has a few limitations, i.e. if two menu's are added to the same page the format of both will be the same since the same CSS style sheet will be used for both.  Also, the menu is currently three layers deep.  If more layers are added then the CSS style sheet will need to be extended.