My main menu is ViewUserControl, which appears as a partial view in my MasterPage. Although mine is hardcoded, you can easily create it from ViewData. Its generation from view data is probably related to the implementation of either the FilterAttribute custom attribute, which sets the parameters that will be used to create the menu that will be applied to each controller / action, or, if the menu is the same on each page, the basic controller is implemented, which fills in the data view, overriding OnActionExecuted and adding ViewData to it.
Example (note that you are likely to use caching for results instead of getting them from db every time).
Class Models
public class MenuItem
{
public string Text { get; set; }
public string Action { get; set; }
public string Controller { get; set; }
}
public class Menu
{
public string Heading { get; set; }
public IEnumerable<MenuItem> Items { get; set; }
}
MenuControl.ascx: type System.Web.Mvc.ViewPage<List<Menu>>
<div id="mainMenu">
<% foreach (var menu in Model) { %>
<div class="menu">
<h2 class="menu-heading"><%= menu.Heading %></h2>
<% foreach (var item in Model.Items) { %>
<%= Html.ActionLink( item.Text,
item.Action,
item.Controller,
null,
{ @class = "menu-item" } ) %>
<% } %>
</div>
<% } %>
</div>
Masterpage
<html>
<head>
...
<asp:ContentPlaceHolder runat="server" id="HeaderContent">
</head>
<body>
... other HTML...
<% Html.RenderPartial( "MenuControl", ViewData["mainMenu"], ViewData ); %>
<asp:ContentPlaceHolder runat="server" id="BodyContent" />
... more HTML ...
</body>
</html>
Basecontroller
public override void OnActionExecuted( ActionExecutedContext filterContext )
{
if (filterContext != null)
{
var context = filterContext.Result as ViewResult;
if (context != null) {
context.ViewData["mainMenu"] =
db.MenuData.Where( m => m.Type == "mainMenu" )
.Select( m => new Menu {
Heading = m.Heading,
Items = db.ItemData.Where( i => i.MenuID == m.MenuID )
.OrderBy( i => i.Name )
.Select( i => new MenuItem {
Text = i.Text,
Action = i.Operation,
Controller = i.Table
})
});
}
}
}