Bootstrap makes it easy to create a nice looking menu system (see Figure 1 and Figure 2). Instead of coding all your menus within your shared layout file, make your menus dynamic by storing them in an XML file or a database table. In this article, you create a simple one-line menu system using two C# classes and a little bit of Razor code in an MVC page. The sample in this article shows you how to build a hierarchical menu structure to be used with drop-down menus. You'll also see how to store and retrieve the menus from an XML file using LINQ to XML and a little bit of recursion.

Figure 1: A simple data-driven menu system
Figure 1: A simple data-driven menu system

For any menu system, you need a minimum of two properties: Title and Action. Title is the text to display to the user and Action is the anchor tag to take the user to the page represented by the menu. As with most everything in .NET, you use a class to represent a menu. In the code snippet below, you'll find a class called BSMenuItem that has these two properties.

public class BSMenuItem
{
    public string Title { get; set; }
    public string Action { get; set; }
}

Create a single instance of the BSMenuItem class for each menu you wish to display on the screen. As shown in Figure 1, you have four menus to display, so you create four instances of this class with the appropriate titles filled in Home, Maintenance, Reports, and Lookup.

A Menu Manager Class

To build this collection of BSMenuItem objects, create a class called BSMenuItemManager (as shown in Listing 1). This class contains a single property called Menus, which is a generic list of BSMenuItem objects. A Load method creates all four of the menus shown in Figure 1. In the constructor, create a new instance of the generic list of BSMenuItem objects. In the Load method, create a new object, fill in the Title and Action properties, and then add that new object to the Menus collection.

Listing 1: A hard-coded approach to a simple menu system.

public class   BSMenuItemManager
{
    public BSMenuItemManager()
    {
        Menus = new List<BSMenuItem>();
    }

    public List<BSMenuItem> Menus { get; set; }

    public void Load()
    {
        BSMenuItem entity = new BSMenuItem();

        entity.Title = "Home";
        entity.Action = "/Home/Home";
        Menus.Add(entity);

        entity = new BSMenuItem();
        entity.Title = "Maintenance";
        entity.Action = "/Maintenance/Maintenance";
        Menus.Add(entity);

        entity = new BSMenuItem();
        entity.Title = "Reports";
        entity.Action = "/Reports/Reports";
        Menus.Add(entity);

        entity = new BSMenuItem();
        entity.Title = "Lookup";
        entity.Action = "/Lookup/Lookup";
        Menus.Add(entity);
    }
}

The Menu Controller

Create a new MVC page called DataDrivenMenu. In the controller for your new page, create a new instance of the BSMenuItemManager class. Call the Load method to build the collection of BSMenuItem objects. Pass the instance of the manager object to the View so you can use the Menus property from the .cshtml page.

public ActionResult DataDrivenMenu()
{
    BSMenuItemManager mgr = new BSMenuItemManager();

    mgr.Load();

    return View(mgr);
}

The .cshtml Page

The first thing you do in your .cshtml page (Listing 2) is set up the model for the page. The model is the instance of the BSMenuItemManager that you created in the controller. The model contains the Menus property, which contains the collection of BSMenuItem objects created in the Load method. Add the appropriate HTML and Bootstrap classes to create the container around the menu items that you need to build. Use a foreach loop to iterate over the collection of menu items. Within the foreach loop, build a <li> tag with an anchor tag inside each. The href attribute is set to the Action property and the text within the anchor is set to the Title property. This is all it takes to build a simple one-line menu system.

Listing 2: A foreach loop creates a bootstrap menu.

@model BSMenuItemManager

@{
    ViewBag.Title = "Data Driven Navbar";
}

<nav class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="container">
        <ul class="nav navbar-nav">
            @foreach (BSMenuItem item in Model.Menus)
            {
                <li>
                    <a href="@item.Action">@item.Title</a>
                </li>
            }
        </ul>
    </div>
</nav>

Hierarchical Menus

In many websites, you need drop-down menus (Figure 2) instead of single-line menus. Using Bootstrap, you can accomplish this quite easily. In order to use a data-driven approach, you need to modify the BSMenuItem class (Listing 3) so it holds a collection of other BSMenuItem objects. A few other properties need to be added as well. You need a MenuId to be used as a primary key. A ParentId property is set to the MenuId value of another menu to indicate that this menu is a sub-menu. Set the ParentId property to zero to signify that this menu is a top-level menu. The last property is DisplayOrder so you can sort the menus in a specific order before you display them.

Figure 2: Drop-down menus are easy to do with Bootstrap.
Figure 2: Drop-down menus are easy to do with Bootstrap.

Listing 3: A self-referencing object is ideal for drop-down menus.

public class BSMenuItem
{
    public BSMenuItem()
    {
        ParentId = 0;
        Menus = new List<BSMenuItem>();
    }

    public int MenuId { get; set; }
    public int ParentId { get; set; }
    public string Title { get; set; }
    public int DisplayOrder { get; set; }
    public string Action { get; set; }

    public List<BSMenuItem> Menus { get; set; }
}

Store Menus in an XML File

Instead of hard-coding the menus, let's put the menus into an XML file. The XML reflects the new structure defined in the BSMenuItem class. The next code snippet shows a single node of the XML file. The ParentId is set to zero to represent that this menu is one of the top-level menus.

<Menus>
    <Menu>
        <MenuId>1</MenuId>
        <ParentId>0</ParentId>
        <Title>Home</Title>
        <DisplayOrder>10</DisplayOrder>
        <Action>/Home/Index</Action>
    </Menu>
</Menus>

In Figure 3, you see the relationship between a ParentId and a MenuId within the XML file. If the ParentId is not zero and is equal to the same value as a MenuId, then that menu is one that is a drop-down under the menu with that MenuId value.

Figure 3: Self-referencing helps avoid multiple XML files.
Figure 3: Self-referencing helps avoid multiple XML files.

Load the Self-Referencing Menus

Modify the Load method of the BSMenuItemManager class to use an XElement class to load the XML file (Listing 4). Pass the collection of all menus to a new method called LoadMenus. This recursive method is passed an XElement object and a value of the menus that have a ParentId that match that value. The Load method passes the value zero to only select those nodes from the XElement object passed in where the ParentId is equal to zero. Zero represents all of the top-level menus only.

Listing 4: Using LINQ to XML and recursion to load hierarchical menus.

public class BSMenuItemManager
{
    public List<BSMenuItem> Menus { get; set; }

    public List<BSMenuItem> Load(string location)
    {
        XElement menus = XElement.Load(location);

        Menus = LoadMenus(menus, 0);

        return Menus;
    }

    private List<BSMenuItem> LoadMenus(XElement menus, int ParentId)
    {
        List<BSMenuItem> nodes = new List<BSMenuItem>();

        nodes = (from node in menus.Elements("Menu")
            where node.Element("ParentId").GetAs<int>() == ParentId
            orderby node.Element("DisplayOrder").GetAs<int>()
            select new BSMenuItem
        {
            MenuId = node.Element("MenuId").GetAs<int>(),
            ParentId = node.Element("ParentId").GetAs<int>(),
            Title = node.Element("Title").Value,
            DisplayOrder = node.Element("DisplayOrder").GetAs<int>(),
            Action = node.Element("Action").Value,
            Menus = (ParentId != node.Element("MenuId").GetAs<int>() ?
                     LoadMenus(menus, node.Element("MenuId").GetAs<int>()) :
            new List<BSMenuItem>())
        }).ToList();

        return nodes;
    }
}

In the LoadMenus method, LINQ to XML is used to select those nodes from the XElement object that match the ParentId passed in. Apply a where clause to perform the filtering and use an orderby to sort by the DisplayOrder element. A select new BSMenuItem statement creates a new object and maps the XML elements to each property of the new object. Notice the use of an extension method GetAs. This method is explained in the sidebar XML Extension Methods.

Notice the line of code within the select that sets the Menus property of the BSMenuItem class. A ternary operator is used to determine whether or not to recursively call the LoadMenus method again or to just create an empty list of BSMenuItem objects. If the ParentId is not equal to the MenuId, you need to check to see if there are any sub-menus that match the current MenuId in the XML node that is being processed. Pass that MenuId to the LoadMenus method and any sub-menus are loaded and returned into the Menus property.

Menus =
    (ParentId != node.Element("MenuId").GetAs<int>() ?
    LoadMenus(menus, node.Element("MenuId").GetAs<int>()) : new List<BSMenuItem>())

Modify the Controller

The controller to call this new Load method in the BSMenuItemManager class must now change. You need to pass in the name of the XML file that contains the menus to the Load method. Use Server.MapPath to calculate the complete file path and name within your website. In the next code snippet, I created a file called Menus.xml that was under the folder \Xml.

public ActionResult DataDrivenMenu()
{
    BSMenuItemManager mgr = new BSMenuItemManager();

    mgr.Load(Server.MapPath("/Xml/Menus.xml"));

    return View(mgr);
}

Modify the .cshtml Page

To display a drop-down menu in Bootstrap, add additional attributes to your anchor tag and create a new <ul> tag with the collection of sub-menus. Add an if statement around the code you had before in the .cshtml page. Check the current menu item and see if there are any sub-menus in its Menus property. If not, use the same code to build the anchor tag that you used previously.

@if (item.Menus.Count == 0)
{
    <a href="@item.Action">
    @item.Title
</a>
}

Next, add an else statement (Listing 5) to build submenus if there are submenus to display. Submenus in Bootstrap are built by adding the class of dropdown-toggle to the anchor tag of the top-level menu. Add data-toggle="dropdown" attribute to the anchor tag as well. Display the Title within the anchor tag, and next to the title, add a blank space and a span tag with the class of caret to display an arrow next to the menu.

Listing 5: A nested foreach loop builds the sub-menus

else
{
    <a href="@item.Action"
        class="dropdown-toggle"
        data-toggle="dropdown">
            @item.Title&nbsp;
        <span class="caret"></span>
    </a>
    <ul class="dropdown-menu">
        @foreach (BSMenuItem subitem in item.Menus)
        {
            <li>
                <a href="@subitem.Action">
                    @subitem.Title
                </a>
            </li>
        }
    </ul>
}

Immediately below the anchor tag add a new <ul> tag and iterate through all submenus building the <li> tags for each menu item just like you did for your normal menus. You can see the complete .cshtml page in Listing 6.

Listing 6: This function strips any non-numeric characters from a string.

@model BSMenuItemManager

@{
    ViewBag.Title = "Data Driven Navbar with Submenus";
}

<nav class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="container">
        <ul class="nav navbar-nav">
            @foreach (BSMenuItem item in Model.Menus)
            {
                <li>
                    @if (item.Menus.Count == 0)
                    {
                        <a href="@item.Action">
                            @item.Title
                        </a>
                    }
                    else
                    {
                        <a href="@item.Action" class="dropdown-toggle" 
                                               data-toggle="dropdown">
                            @item.Title&nbsp;
                        <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            @foreach (BSMenuItem subitem in item.Menus)
                            {
                                <li>
                                    <a href="@subitem.Action">
                                        @subitem.Title
                                    </a>
                                </li>
                            }
                        </ul>
                    }
                </li>
            }
        </ul>
    </div>
</nav>

Summary

In this article, you learned to build a Bootstrap menu system using C# classes and foreach loops. You also saw how to build a hierarchical menu system using LINQ to XML and a recursive method. Keeping menus in an XML file is convenient if you aren't using a database in your application. Using a self-referencing system of a MenuId and corresponding ParentId avoids the need to have multiple XML files.