Click here to Skip to main content
15,893,381 members
Articles / Web Development / ASP.NET

AMenu - A Simple .NET Vertical Menu

Rate me:
Please Sign up or sign in to vote.
4.88/5 (26 votes)
8 Oct 2009CPOL4 min read 64K   3.1K   100  
A CSS based .NET vertical menu control.
// AMenu.cs

using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.Design;

using System.Drawing;
using System.Security.Permissions;


namespace mtweb
{

//////////////////////////////////////////////////////////////////////////

[ParseChildren(false)]
[PersistChildren(true)]
[DefaultEvent("ItemClick")]
[ToolboxData("<{0}:AMenu runat=server></{0}:AMenu>")]
public class AMenu : WebControl, INamingContainer
{
  // Constants
  public const string DEF_WIDTH = "190px";
  public const string DEF_HEIGHT = "10em";
  public const string DEF_FONTSIZE = "12px";
  public const string DEF_FONTNAME = "Arial";
  public const bool   DEF_FONTBOLD = false;
  public const string DEF_FORECOLOR = "#336666";
  public const string DEF_BACKCOLOR = "#f7f7f7";
  public const string DEF_BORDERCOLOR = "#c6c6cc";
  public const string DEF_FOREHOVER = "#0000ff";
  public const string DEF_BACKHOVER = "#dae0e4";
  public const string DEF_MARGIN = "2px";
  public const string DEF_INDENT = "38px";
  public const bool   DEF_BORDER = false;
  public const string DEF_ARROWIMAGE = "";
  public const string DEF_ICONIMAGE = "";
  public const string DEF_SUBWIDTH = "190px";
  public const string DEF_SUBMARGIN = "2px";
  public const string DEF_SUBINDENT = "16px";
  //
  public const string CLASS_AMENU = "amenu";
  public const string CLASS_TOP = "top";
  public const string CLASS_SUB = "sub";
  public const string CLASS_VPADDING_TOP = "vpadding_top";
  public const string CLASS_VPADDING_BOTTOM = "vpadding_bottom";
  public const string CLASS_ARROW = "arrow";
  
  // Fields
  private ArrayList m_Items = new ArrayList();
  private event CommandEventHandler m_ItemClick;



  ///////////////////////////////////////////////
  // Constructor

  public AMenu() : base(HtmlTextWriterTag.Div)
  {
  }


  ///////////////////////////////////////////////
  // Events

  protected override void OnInit(EventArgs e)
  {
    base.OnInit(e);
  }


  ///////////////////////////////////////////////
  // Properties

  // Hide inherited:

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override Unit BorderWidth
  {
    get { return Unit.Empty; }
    set {}
  }

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override BorderStyle BorderStyle
  {
    get { return BorderStyle.NotSet; }
    set {}
  }

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override Boolean Enabled
  {
    get { return true; }
    set {}
  }

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override String SkinID
  {
    get { return string.Empty; }
    set {}
  }

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override Boolean EnableTheming
  {
    get { return false; }
    set {}
  }

  [Browsable(false)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public override String ToolTip
  {
    get { return string.Empty; }
    set {}
  }


   // Top menu:

  // Child controls
  [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ArrayList Items
  {
    get { return m_Items; }
    set { m_Items = value;}
  }


  // Top menu width.
  // Note: Unit.Empty will reset to def. value
  [Category("Layout")]
  [DefaultValue(DEF_WIDTH)]
  [Bindable(true)]
  public override Unit Width
  {
    get {
      object o = ViewState["TopWidth"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return new Unit(DEF_WIDTH);
    }
    set {
      ViewState["TopWidth"] = value;
    }
  }


  // Top menu height.
  // Note: Unit.Empty will reset to def. value
  [Category("Layout")]
  [DefaultValue(DEF_HEIGHT)]
  [Bindable(true)]
  public override Unit Height
  {
    get {
      object o = ViewState["TopHeight"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return Unit.Parse(DEF_HEIGHT);
    }
    set {
      ViewState["TopHeight"] = value; 
    }
  }


  // Menu font size.
  // Note: Unit.Empty will reset to def. value.
  // Note: In Properties window use Font-Size, 
  // in code use this.FontSize.
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(typeof(Unit),DEF_FONTSIZE)]
  [TypeConverter(typeof(UnitConverter))]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual Unit FontSize
  {
    get {
      Unit u = base.Font.Size.Unit;
      if (!u.IsEmpty) return u;
      return Unit.Parse(DEF_FONTSIZE);
    }
    set {
      base.Font.Size = FontUnit.Parse(value.ToString());
    }
  }


  // Menu font name.
  // Note: NULL will reset to def. value.
  // Note: In Properties window use Font-Names, in code
  // use this.FontNames to set/get the font name(s)
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(DEF_FONTNAME)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual string[] FontNames
  {
    get {
      string[] s = base.Font.Names;
      if (s.Length != 0) return s;
      return new string[1]{DEF_FONTNAME};
    }
    set {
      if (value == null) base.Font.Names = new string[0]{};
      else base.Font.Names = value;
    }
  }


  // Menu font bold [true|false]
  // Note: In Properties window use Font-Bold, in code
  // use this.FontBold to set/get the property
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(DEF_FONTBOLD)]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual bool FontBold
  {
    get {
      return base.Font.Bold;
    }
    set {
      base.Font.Bold = value;
    }
  }


  // Menu font color.
  // Note: Color.Empty will reset to def. value.
  [Category("Appearance")]
  [DefaultValue(typeof(Color),DEF_FORECOLOR)]
  [TypeConverter(typeof(WebColorConverter))]
  [Bindable(true)]
  public override Color ForeColor
  {
    get {
      Color c = base.ForeColor;
      if (!c.IsEmpty) return c;
      return ColorTranslator.FromHtml(DEF_FORECOLOR);
    }
    set {
      base.ForeColor = value;
    }
  }


  // Menu background color.
  // Note: Color.Empty will reset to def. value.
  [Category("Appearance")]
  [DefaultValue(typeof(Color),DEF_BACKCOLOR)]
  [TypeConverter(typeof(WebColorConverter))]
  [Bindable(true)]
  public override Color BackColor
  {
    get {
      Color c = base.BackColor;
      if (!c.IsEmpty) return c;
      return ColorTranslator.FromHtml(DEF_BACKCOLOR);
    }
    set {
      base.BackColor = value;
    }
  }


  // Menu border color.
  // Note: Color.Empty will reset to def. value.
  [Category("Appearance")]
  [DefaultValue(typeof(Color),DEF_BORDERCOLOR)]
  [TypeConverter(typeof(WebColorConverter))]
  [Bindable(true)]
  public override Color BorderColor
  {
    get {
      Color c = base.BorderColor;
      if (!c.IsEmpty) return c;
      return ColorTranslator.FromHtml(DEF_BORDERCOLOR);
    }
    set {
      base.BorderColor = value;
    }
  }


  // Menu hover color.
  // Note: Color.Empty will reset to def. value.
  [Category("Appearance")]
  [DefaultValue(typeof(Color),DEF_FOREHOVER)]
  [TypeConverter(typeof(WebColorConverter))]
  [Bindable(true)]
  public virtual Color ForeHover
  {
    get {
      object o = ViewState["ForeHover"];
      if (o != null && ((Color)o) != Color.Empty) return (Color)o;
      return ColorTranslator.FromHtml(DEF_FOREHOVER);
    }
    set {
      ViewState["ForeHover"] = value;
    }
  }


  // Menu hover background.
  // Note: Color.Empty will reset to def. value.
  [Category("Appearance")]
  [DefaultValue(typeof(Color),DEF_BACKHOVER)]
  [TypeConverter(typeof(WebColorConverter))]
  [Bindable(true)]
  public virtual Color BackHover
  {
    get {
      object o = ViewState["BackHover"];
      if (o != null && ((Color)o) != Color.Empty) return (Color)o;
      return ColorTranslator.FromHtml(DEF_BACKHOVER);
    }
    set {
      ViewState["BackHover"] = value;
    }
  }
  

  // Top menu left and right margins.
  // Note: Unit.Empty will reset to def. value
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(typeof(Unit),DEF_MARGIN)]
  [TypeConverter(typeof(UnitConverter))]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual Unit Margin
  {
    get {
      object o = ViewState["TopMargin"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return new Unit(DEF_MARGIN);
    }
    set {
      ViewState["TopMargin"] = value;
    }
  }


  // Top menu left padding.
  // Note: Unit.Empty will reset to def. value
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(typeof(Unit),DEF_INDENT)]
  [TypeConverter(typeof(UnitConverter))]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual Unit Indent
  {
    get {
      object o = ViewState["TopIndent"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return new Unit(DEF_INDENT);
    }
    set {
      ViewState["TopIndent"] = value;
    }
  }

  // Top menu border [true|false]
  [Category("Layout")]
  [DefaultValue(DEF_BORDER)]
  [Bindable(true)]
  public virtual bool Border
  {
    get {
      object o = ViewState["TopBorder"];
      return (o == null) ? DEF_BORDER : (bool)o;
    }
    set {
      ViewState["TopBorder"] = value;
    }
  }

  // Arrow image for links with sub-menus
  [Category("Appearance")]
  [UrlProperty()]
  [DefaultValue(DEF_ARROWIMAGE)]
  [Bindable(true)]
  public virtual string ArrowImage
  {
    get {
      String s = (String)ViewState["ArrowImage"];
      if (s == null) s = DEF_ARROWIMAGE;
      return ToAbsolute(s);
    }
    set {
      ViewState["ArrowImage"] = value;
    }
  }


  // --------------
  // Sub menu

  // Default width of all sub menus.
  // Can be overwritten for each sub menu.
  // Note: Unit.Empty will reset to def. value
  [Category("Layout")]
  [DefaultValue(typeof(Unit),DEF_SUBWIDTH)]
  [TypeConverter(typeof(UnitConverter))]
  [Bindable(true)]
  public virtual Unit SubWidth
  {
    get {
      object o = ViewState["SubWidth"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return Unit.Parse(DEF_SUBWIDTH);
    }
    set {
      ViewState["SubWidth"] = value;
    }
  }


  // Sub menu left and right margins.
  // Note: Unit.Empty will reset to def. value
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(typeof(Unit),DEF_SUBMARGIN)]
  [TypeConverter(typeof(UnitConverter))]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual Unit SubMargin
  {
    get {
      object o = ViewState["SubMargin"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return Unit.Parse(DEF_SUBMARGIN);
    }
    set {
      ViewState["SubMargin"] = value;
    }
  }


  // Sub menu left padding.
  // Note: Unit.Empty will reset to def. value
  [ReadOnly(true)]
  [Browsable(false)]
  [DefaultValue(typeof(Unit),DEF_SUBINDENT)]
  [TypeConverter(typeof(UnitConverter))]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public virtual Unit SubIndent
  {
    get {
      object o = ViewState["SubIndent"];
      if (o != null && ((Unit)o) != Unit.Empty) return (Unit)o;
      return Unit.Parse(DEF_SUBINDENT);
    }
    set {
      ViewState["SubIndent"] = value;
    }
  }

  
  // Click handler (server)
  [Category("Action")]
  public event CommandEventHandler ItemClick
  {
    add { m_ItemClick += value; }
    remove { m_ItemClick -= value; }
  }



  ///////////////////////////////////////////////
  // Rendering

  public override void RenderBeginTag(HtmlTextWriter writer)
  {
    // Call base: <div>
    base.RenderBeginTag(writer);

    
    // Render UL for this top-menu:

    // ID ... not needed
    //writer.AddAttribute("id", "root");

    // Class
    writer.AddAttribute("class", AMenu.CLASS_TOP);

    // Width
    if (Width != Unit.Empty) {
      if (!AMenu.IsDefault(Width.ToString(), AMenu.DEF_WIDTH)) {
        writer.AddStyleAttribute("width", Width.ToString());
      }
    }

    // Background color
    if (!BackColor.IsEmpty) {
      string bc = ColorTranslator.ToHtml(BackColor);
      if (!AMenu.IsDefault(bc, AMenu.DEF_BACKCOLOR)) {
        writer.AddStyleAttribute("background-color", bc);
      }
    }

    // Open the UL tag
    writer.RenderBeginTag(HtmlTextWriterTag.Ul);
    

    // Render LI for top padding:

    // Class
    writer.AddAttribute("class", AMenu.CLASS_VPADDING_TOP);

    // Width
    if (Width != Unit.Empty) {
      if (!AMenu.IsDefault(Width.ToString(), AMenu.DEF_WIDTH)) {
        writer.AddStyleAttribute("width", Width.ToString());
      }
    }

    // Border (left/right/bottom) color
    if (!BorderColor.IsEmpty) {
      string bc = ColorTranslator.ToHtml(BorderColor);
      if (!AMenu.IsDefault(bc, AMenu.DEF_BORDERCOLOR)) {
        writer.AddStyleAttribute("border-color", bc);
      }
    }

    // Remove borders, if needed
    if (!Border) {
      writer.AddStyleAttribute("border", "none");
      writer.AddStyleAttribute("height", "0");
    }

    // Open/close the LI tag
    writer.RenderBeginTag(HtmlTextWriterTag.Li);
    writer.Write("&nbsp;");
    writer.RenderEndTag();
  }


  // Note: don't call base, set attributes manually
  protected override void AddAttributesToRender(HtmlTextWriter writer)
  {
    // ID
    writer.AddAttribute("id", this.ID);

    // Class
    writer.AddAttribute("class", AMenu.CLASS_AMENU);

    // Height
    if (Height != Unit.Empty) {
      writer.AddStyleAttribute("height", Height.ToString());
    }

    // Font size
    if (FontSize != Unit.Empty) {
      if (!AMenu.IsDefault(FontSize.ToString(), AMenu.DEF_FONTSIZE)) {
        writer.AddStyleAttribute("font-size", FontSize.ToString());
      }
    }

    // Font family
    if (FontNames.Length > 0) {
      string fnames = string.Join(",", FontNames);
      if (!AMenu.IsDefault(fnames, AMenu.DEF_FONTNAME)) {
        writer.AddStyleAttribute("font-family", fnames);
      }
    }

    // Font weight
    if (FontBold != AMenu.DEF_FONTBOLD) {
      writer.AddStyleAttribute("font-weight", "bold");
    }
  }  


  public override void RenderEndTag(HtmlTextWriter writer)
  {
    // Render LI for bottom padding:

    // Class
    writer.AddAttribute("class", AMenu.CLASS_VPADDING_BOTTOM);

    // Width
    if (Width != Unit.Empty) {
      if (!AMenu.IsDefault(Width.ToString(), AMenu.DEF_WIDTH)) {
        writer.AddStyleAttribute("width", Width.ToString());
      }
    }

    // Border (left/right/top) color
    if (!BorderColor.IsEmpty) {
      string bc = ColorTranslator.ToHtml(BorderColor);
      if (!AMenu.IsDefault(bc, AMenu.DEF_BORDERCOLOR)) {
        writer.AddStyleAttribute("border-color", bc);
      }
    }

    // Remove borders, if needed
    if (!Border) {
      writer.AddStyleAttribute("border", "none");
      writer.AddStyleAttribute("height", "0");
    }

    // Open/close the LI tag
    writer.RenderBeginTag(HtmlTextWriterTag.Li);
    writer.Write("&nbsp;");
    writer.RenderEndTag();


    // Close the UL tag
    writer.RenderEndTag();


    // Call base: </div>
    base.RenderEndTag(writer);
  }


  ///////////////////////////////////////////////
  // Methods

  [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
  protected override void AddParsedSubObject(Object obj)
  {
    m_Items.Add(obj);
  }


  protected override void CreateChildControls()
  {
    foreach (Control c in m_Items) {
      this.Controls.Add(c);
    }
  }


  // Return TRUE if event should not be bubbled
  protected override bool OnBubbleEvent(object sender, EventArgs e)
  {
    // If not a command arg, or not from MenuLink, ignore
    if (!(e is CommandEventArgs) || !(sender is MenuLink))
    {
      return false;
    }

    // Call the handler if we got one
    if (m_ItemClick != null) {
      m_ItemClick(sender, (CommandEventArgs)e);
      return true;
    }

    // Event not handled
    return false;
  }


  ///////////////////////////////////////////////
  // Utility

  public static double RoundUp(double d)
  {
    return Math.Floor(d + 0.5);
  }

  public static double RoundDown(double d)
  {
    double floor = Math.Floor(d);
    if ((d - floor) > 0.5) return (floor + 1);
    else return (floor);
  }

  public static bool IsDefault(string val, string defval)
  {
    string s1 = val.Trim().Replace("'","").ToUpper();
    string s2 = defval.Trim().Replace("'","").ToUpper();
    return s1 == s2;
  }

  public static string ToAbsolute(string path)
  {
    path = path.Trim();
    if (string.IsNullOrEmpty(path)) return string.Empty;
    return VirtualPathUtility.ToAbsolute(path);
  }


}  // class
}  // namespace

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Europe Europe
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions