ASP.NET MVC Menu Selected Item

The answer from Jakub Konecki led me in the right direction... here is the controller action I ended up with:

    [ChildActionOnly]
    public ActionResult MainMenu()
    {
        var items = new List<MenuItem>
        {
            new MenuItem{ Text = "Home", Action = "Index", Controller = "Home", Selected=false },
            new MenuItem{ Text = "My Profile", Action = "Index", Controller = "Profile", Selected = false},
            new MenuItem{ Text = "About", Action = "About", Controller = "Home", Selected = false }
        };

        string action = ControllerContext.ParentActionViewContext.RouteData.Values["action"].ToString();
        string controller = ControllerContext.ParentActionViewContext.RouteData.Values["controller"].ToString();

        foreach (var item in items)
        {
            if (item.Controller == controller && item.Action == action)
            {
                item.Selected = true;
            }
        }

        return PartialView(items);
    }

Hope this helps someone.


stephen,

here's my contribution to the party. a recursive inline function to populate the <ul><li> for as many depths as is required (here's the entire ascx file):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<GBC_Art.Controllers.MenuItemModel>>" %>
<%@ Import Namespace="GBC_Art.Controllers" %>
<%
    Action<List<MenuItemModel>, int> printNodesRecursively = null;
    printNodesRecursively = (List<MenuItemModel> nodeList, int depth) =>
    {
        if (nodeList == null || nodeList.Count == 0) return;
%>
    <ul<%= depth == 0 ? " id='menu'" : ""%>>  
<%
    foreach (var menuItem in nodeList)
    {
    %>
    <li><%= Html.ActionLink(menuItem.Text, menuItem.Action, menuItem.Controller, null, new { @class = menuItem.Selected ? "selected" : "" })%>
        <%printNodesRecursively(menuItem.SubMenu, depth + 1);%>
    </li>
    <%
        }
%>
    </ul>
<%
    };
    List<MenuItemModel> nodes = Model; 
    printNodesRecursively(nodes, 0);
%>

usage -> called as a partialview via the controller as per your example above.

cheers


You should pass all relevant information in the Model. Ideally your menu will be rendered as a Partial View by a separate controller method. I have a Navigation controller with actions like MainMenu, FooterMenu, Breadcrumbs, etc that render individual parts.

Your model will be a collection of menu items like:

    public class MenuItemModel
    {
        public MenuItemModel()
        {
            SubMenu = new List<MenuItemModel>();
        }

        public string Text { get; set; }
        public string Controller { get; set; }            
        public string Action { get; set; }
        public bool Selected { get; set; }

        public List<MenuItemModel> SubMenu { get; private set; }
    }

Your Controller will create a collection of menu items and pass them to the view with the appropriate item selected. Then the view can be as simple as:

<ul id="menu">     
    <% foreach(var menuItem in Model.MenuItems) { %> 
        <li><%: Html.ActionLink(menuItem.Text, menuItem.Action, menuItem.Controller, null, new { @class = menuItem.Selected ? "selected" : "" })%></li>
    <% } %>
</ul>