ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session

ASP.NET MVC2 makes so many things so easy to program but there are some basic things that are just not there and they should be.
I will be talking about a basic functionality that is a must these days.
I am talking about a menu and highlighting selected menu item.

Let’s see the problem:

I have 3 links and with each click I would like to add apropiate css class to mark my menu item as selected.

ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session

I have follwed an article on bobby’s blog.
So with this great extension it does work really well:

  public static class HtmlExtensions
    {
        public static MvcHtmlString ActionMenuItem(this HtmlHelper htmlHelper,
        String linkText, String actionName, String controllerName)
        {
            var tag = new TagBuilder("li");
            if (htmlHelper.ViewContext.RequestContext.IsCurrentRoute(
            null, controllerName, actionName) ||
            htmlHelper.ViewContext.RequestContext.IsParentRoute(
            controllerName, actionName))
            {
                tag.AddCssClass("active");
            }
            tag.InnerHtml = htmlHelper.ActionLink(linkText, actionName, controllerName).ToString();

            return MvcHtmlString.Create(tag.ToString());
        }
    }

In the place of menu we stick this:

<%= Html.ActionMenuItem("Home", "Index", "Home") %>

However, there was a one little thing missing in it that I needed to have on my web.

ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session

In the About link I have a link to Biography . (Biography controller and Index action)
Since the Biography page is a part of About section I would like the “About” menu item to remain highlighted.
The solution above doesn’t work in that case.
So I decided to modify it a bit.

Let’s start from the extension:

    public static class HtmlExtensions
    {
        public static MvcHtmlString ActionMenuItem(this HtmlHelper htmlHelper, String linkText, String actionName, String controllerName)
        {
            var tag = new TagBuilder("li");

            if (htmlHelper.ViewContext.RequestContext.IsCurrentRoute(null, controllerName, actionName) ||
                htmlHelper.ViewContext.RequestContext.IsParentRoute(controllerName, actionName))
            {
                tag.AddCssClass("active"); // stick class active
            }
            else
            {
                tag.AddCssClass("inactive"); // stick class inactive
            }

            tag.InnerHtml = htmlHelper.ActionLink(linkText, actionName, controllerName).ToString();

            return MvcHtmlString.Create(tag.ToString());
        }
    }
 

We also neeed Request Extensions:

    public static class RequestExtensions
    {
        public static bool IsCurrentRoute(this RequestContext context, String areaName)
        {
            return context.IsCurrentRoute(areaName, null, null);
        }

        public static bool IsCurrentRoute(this RequestContext context, String areaName, String controllerName)
        {
            return context.IsCurrentRoute(areaName, controllerName, null);
        }

        public static bool IsCurrentRoute(this RequestContext context, String areaName, String controllerName, params String[] actionNames)
        {
            var routeData = context.RouteData;
            var routeArea = routeData.DataTokens["area"] as String;
            var current = false;

            if (((String.IsNullOrEmpty(routeArea) && String.IsNullOrEmpty(areaName)) || (routeArea == areaName)) &&
                 ((String.IsNullOrEmpty(controllerName)) || (routeData.GetRequiredString("controller") == controllerName)) &&
                 ((actionNames == null) || actionNames.Contains(routeData.GetRequiredString("action"))))
            {
                current = true;
            }

            return current;
        }

        public static bool IsParentRoute(this RequestContext context, String controller, String action)
        {
            var routeData = context.RouteData;
            UrlModel returnUrl = null;
            UrlModel requestUrl = new UrlModel { Action = routeData.GetRequiredString("action"), Controller = routeData.GetRequiredString("controller") };
            UrlModel linkUrl = new UrlModel { Action = action, Controller = controller };

            var urls = UrlMap.GetDictionary();
            urls.TryGetValue(requestUrl, out returnUrl);

            if (returnUrl != null && returnUrl.Equals(linkUrl))
                return true;
            else
                return false; ;
        }
    }
 

And now the important part the UrlMap


    public static class UrlMap
    {
        public static Dictionary GetDictionary()
        {
            Dictionary urls = new Dictionary();
            urls.Add(new UrlModel { Controller = "Biography", Action = "Index" }, new UrlModel { Controller = "About", Action = "Index" });
            urls.Add(new UrlModel { Controller = "Contact", Action = "GetInTouch" }, new UrlModel { Controller = "Contact", Action = "Index" });
            return urls;
        }
    }

Here I specify that if Biography/Index is called please mark it the same as About/Index.

Don’t forget to create UrlModel class

public class UrlModel
    {
        public string Action { get; set; }
        public string Controller { get; set; }

        public override bool Equals(object obj)
        {
            return Equals(obj as UrlModel);
        }
        public bool Equals(UrlModel obj)
        {
            return obj != null && obj.Action == this.Action && obj.Controller == this.Controller;
        }
        public override int GetHashCode()
        {
            return (Action + Controller).GetHashCode();
        }
    }

Now you can stick HtmlHelper with menu in any place on the site.

    <ul id="menu">
        <%= Html.ActionMenuItem("Home", "Index", "Home") %>
        <%= Html.ActionMenuItem("About", "Index", "About") %>
        <%= Html.ActionMenuItem("Contact", "Index", "Contact") %>
    </ul>

and add as amny Url Mapsas you like:

 public static Dictionary GetDictionary()
        {
            Dictionary urls = new Dictionary();
            urls.Add(new UrlModel { Controller = "Biography", Action = "Index" }, new UrlModel { Controller = "About", Action = "Index" });
            urls.Add(new UrlModel { Controller = "Contact", Action = "GetInTouch" }, new UrlModel { Controller = "Contact", Action = "Index" });
            return urls;
        }

Here is how it looks: ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session


ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session


ASP.NET MVC 2 Highlight Selected Menu Item on the Site Master Without Session

Here is a complete solution to download: http://www.mediafire.com/?0xabi855suro2rl

GL!

You may also like...

  • Mandeep Bhangu

    thanks will certainly try it mate!!

  • Pepito

    I have my Helpers in a different project. How do i reference RequestContext in my RequestExtension class? Can’t find a way to do this.
    I have added Routing, Webpages, MVc, etc etc. references but nothing seems to happen.
    Thanks

  • http://twitter.com/mahorvath Martin Horvath

    Thanks for your work, I’ll definitely give it a try!

  • Kai Baring

    Thanks for sharing your great work.
    Question – how would you do it, if you have more than 2 levels of menu?

    Many thanks.
    Kai

  • Kai Baring

    Hi,
    Here is what I’ve got. Added a method that calls itself until it finds the parent, or reaches a null. It is being called from the original IsParentRoute when returnUrl is not null and returnUrl != linkUrl. Cheers, Kai.

    public static bool IsParentRoute(UrlModel linkUrl, UrlModel checkUrl)        {            UrlModel returnUrl = null;            var urls = UrlMap.GetDictionary();            urls.TryGetValue(checkUrl, out returnUrl);            return (returnUrl != null ? (returnUrl.Equals(linkUrl) ? true : IsParentRoute(linkUrl, returnUrl)) : false);        }

  • clammers

    Update the following (using MVC4):
    public static Dictionary GetDictionary()
    {
    Dictionary urls = new Dictionary();

    }

    • clammers

      This site though I was writing Html… this is for C#..Do not include the equal sign and the quotations and do not include any of the tags

  • Pingback: drain and sewer cleaning()

  • Pingback: hpv test for men()

  • Pingback: phentermine()

  • Pingback: phentermine()

  • Pingback: phentermine()

  • Pingback: Tickets()