Back to: ASP.NET Core Tutorials For Beginners and Professionals
Navigation-Menus in ASP.NET Core MVC Application
In this article, I will discuss How to Create Responsive Navigation-Menus in ASP.NET Core MVC Applications using Bootstrap and jQuery. Please read our previous article discussing the Environment Tag Helper in ASP.NET Core MVC Web Application.
Business Requirement:
When we open our web application on large-screen devices like desktops and tablets, we want the navigation menus to look as shown in the image below.
But when we open our website on a small screen device like a mobile, we want to show the navigation menus like the one below.
Let us Proceed and see How we can Implement the above using ASP.NET Core MVC Application.
Creating a New ASP.NET Core MVC Application:
First, we create a new ASP.NET Core Application using the Model-View-Controller Project Template named FirstCoreMVCApplication. Once we create the project, we must add the required Bootstrap and jQuery files.
Adding Bootstrap and jQuery files:
The most important point you need to remember is that Bootstrap depends on jQuery. So, here, we need to download both Bootstrap and jQuery into our application. Here, we will use a tool called Library Manager (LibMan) to download the required Bootstrap and jQuery files.
So, please follow the below steps to install Bootstrap into your application.
- Right-click on the “Project Name” in the Solution Explorer and then select Add > Client-Side Library, which will open the “Add Client-Side Library” Window.
- Leave the default provider as it is, which is “cdnjs” in this case. The other providers are filesystem, jsdelivr, and unpkg.
- In the “Library” text box, type “twitter-bootstrap“. You can also get intelligence support once you start typing. Once you select the matching entry, it tries installing the latest Bootstrap version. Here, we are installing the latest version of Bootstrap, i.e. (twitter-bootstrap@5.3.0).
- There are two radio buttons to select whether to include “All library files” or “Choose Specific files“. Here, I am selecting the “All library files” radio button.
- In the “Target Location” text box, specify the folder location where you want the library files to be installed. By default, the static files are only served from the wwwroot folder. I am going with the default location, i.e., “wwwroot/lib/twitter-bootstrap/”.
- Finally, click on the “Install” button, as shown in the image below.
Once it is successfully installed, you will find two things. One is the libman.json file, and the second is the required bootstrap files, as shown in the image below.
In the same way, you can install jQuery using Libary Manager. The other option to install the jQuery file is to open the libman.json file and then copy and paste the following code into it.
{ "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "library": "twitter-bootstrap@5.3.0", "destination": "wwwroot/lib/twitter-bootstrap/" }, { "provider": "cdnjs", "library": "jquery@3.7.0", "destination": "wwwroot/lib/jquery/" } ] }
As you can see in the above code, here we are adding jquery@3.7.0. It will automatically install the required jQuery files inside the lib folder.
Adding images Folder inside the wwwroot folder:
Add a folder called images with the wwwroot folder and then paste two different images with the names Logo.png and Student.png, as shown in the image below.
Adding Custom CSS:
By default, the ASP.NET Core Model-View-Controller template creates a folder with the name CSS within the wwwroot folder. If not added, then please add a folder with the name CSS within the wwwroot folder and then add a CSS file with the name MyCustomStyleSheet.css. Once you create the MyCustomStyleSheet.css file, copy and paste the following code.
.btn { width: 80px; }
With the above changes in place, your wwwroot folder should look as shown below.
Modifying _ViewImports.cshtml file:
Please modify the _ViewImports.cshtml file as shown below. We are adding the HTML Tag Helper here to use the Tag Helper throughout the application views. By default, you will see the following code as well.
@using FirstCoreMVCApplication @using FirstCoreMVCApplication.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Modifying _Layout.cshtml file:
Please modify the _Layout.cshtml file, which is present inside the Views => Shared folder, as shown below. As you can see here, in the head section, we refer to MyCustomStyleSheet.css, bootstrap.css, query.js, and bootstrap.js files. The navbar-toggler button is the responsive component. It will show up on smaller screen sizes. When clicked, it toggles the visibility of the navigation menu items. The jQuery and Bootstrap JavaScript handle the button’s functionality.
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/css/MyCustomStyleSheet.css" rel="stylesheet" /> <link href="~/lib/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" /> <script src="~/lib/jquery/jquery.js"></script> <script src="~/lib/twitter-bootstrap/js/bootstrap.js"></script> </head> <body> <div class="container"> <nav style="margin-top:15px" class="navbar navbar-expand-sm bg-dark navbar-dark"> <a style="margin-left:5px" class="navbar-brand" asp-controller="home" asp-action="index"> <img src="~/images/Logo.png" width="50" height="50"> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="collapsibleNavbar"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" asp-controller="home" asp-action="index">List</a> </li> <li class="nav-item"> <a class="nav-link" asp-controller="home" asp-action="create">Create</a> </li> <li> <a class="nav-link" asp-controller="home" asp-action="about">About</a> </li> <li> <a class="nav-link" asp-controller="home" asp-action="contact">Contact</a> </li> </ul> </div> </nav> <div> @RenderBody() </div> </div> </body> </html>
Customize:
You can further customize the navbar by:
- Changing its colors or styles using Bootstrap classes or custom CSS.
- Adding other Bootstrap components or utilities.
- Adjusting responsive breakpoints if needed.
Note: The jQuery reference must be loaded before loading the Bootstrap JavaScript file to work on a small-screen device for the navbar toggle button. If you change the order, the navbar toggle button does not work as expected.
Creating Models:
First, we need to create two enums to store the Gender and Branch of a student. So, create two classes within the Models folder named Gender.cs and Branch.cs.
Branch.cs
Open the Branch.cs class file and copy and paste the following code. As you can see, we have created the Branch enum with four named constants, i.e., None, CSE, ETC, and Mech.
namespace FirstCoreMVCApplication.Models { public enum Branch { None, CSE, ETC, Mech } }
Gender.cs
Next, open the Gender.cs class file and copy and paste the following code. As you can see, we have created the Gender enum with two named constants, i.e., Male and Female.
namespace FirstCoreMVCApplication.Models { public enum Gender { Male, Female } }
Student Model:
We want to display the student information on the web page. So, create a class file with the name Student.cs within the Models folder and copy and paste the following code into it.
using System.Collections.Generic; namespace FirstCoreMVCApplication.Models { public class Student { public int StudentId { get; set; } public string? Name { get; set; } public string? Email { get; set; } public Branch? Branch { get; set; } public Gender? Gender { get; set; } public string? Address { get; set; } } }
Modifying Home Controller:
Next, modify the Home Controller as shown below. As you can see in the below code, we have added two action methods. The Index action method will return all the students, while the Details action takes the student ID input parameter and returns that student information.
using FirstCoreMVCApplication.Models; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; namespace FirstCoreMVCApplication.Controllers { public class HomeController : Controller { //Create a Variable to Hold List of Students //This is going to be our data source private List<Student> listStudents = new List<Student>(); public HomeController() { //Within the Constructor we are Initializing listStudents variable //In Real-Time, we will get the data from the database listStudents = new List<Student>() { new Student() { StudentId = 101, Name = "James", Branch = Branch.CSE, Gender = Gender.Male, Address = "A1-2018", Email = "James@g.com" }, new Student() { StudentId = 102, Name = "Priyanka", Branch = Branch.ETC, Gender = Gender.Female, Address = "A1-2019", Email = "Priyanka@g.com" }, new Student() { StudentId = 103, Name = "David", Branch = Branch.CSE, Gender = Gender.Male, Address = "A1-2020", Email = "David@g.com" }, new Student() { StudentId = 104, Name = "Pranaya", Branch = Branch.Mech, Gender = Gender.Male, Address = "A1-2021", Email = "Pranaya@g.com" } }; } public ViewResult Index() { //returning all the students return View(listStudents); } public ViewResult Details(int Id) { //returning the student based on the Student Id var studentDetails = listStudents.FirstOrDefault(std => std.StudentId == Id); return View(studentDetails); } } }
Modifying Index.cshtml file:
Please modify the Index view, which is present inside the Home folder, as shown below. Notice how the buttons (View, Edit, and Delete) are attached to each other. Use the Bootstrap margin classes (m-1, m-2, etc.) to include the margin between these buttons. In the class name, “m” stands for margin, and the number 1, 2, 3, etc., is the size of the space you want between the buttons. In this view, List<Student> is the model, and using a for each loop, we are accessing all the students. Further, if you notice, we have used Tag Helper to generate the link for the View button.
@model List<Student> @{ ViewBag.Title = "Student List"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div class="row"> @foreach (var student in Model) { <div class="col-sm-3"> <div class="card m-3"> <div class="card-header"> <h3>@student.Name</h3> </div> <div class="card-body"> <img class="card-img-top" src="~/images/Student.png" /> </div> <div class="card-footer text-center"> <a asp-controller="home" asp-action="details" asp-route-id="@student.StudentId" class="btn btn-primary m-1">View</a> <a href="#" class="btn btn-primary m-1">Edit</a> <a href="#" class="btn btn-danger m-1">Delete</a> </div> </div> </div> } </div>
Creating Detail.cshtml file:
Please create a view with the name Details.cshtml within the Home folder, which is present inside the Views folder, and then copy and paste the following code into it. In this view, the Student will be the model, and we can access the student model properties using the @Model object.
@model Student @{ ViewBag.Title = "Student Details"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div class="row justify-content-center m-3"> <div class="col-sm-8"> <div class="card"> <div class="card-header"> <h1>@Model?.Name</h1> </div> <div class="card-body text-center"> <img class="card-img-top" src="~/images/Student.png" /> <h4>Studnet ID : @Model?.StudentId</h4> <h4>Email : @Model?.Email</h4> <h4>Branch : @Model?.Branch</h4> <h4>Gender : @Model?.Gender</h4> <h4>Address : @Model?.Address</h4> </div> <div class="card-footer text-center"> <a href="#" class="btn btn-primary">Back</a> <a href="#" class="btn btn-primary">Edit</a> <a href="#" class="btn btn-danger">Delete</a> </div> </div> </div> </div>
Program.cs Class:
Please modify the Program class code as follows. Actually, we are not making any changes in the Main method of the Program class as we have created the Project using Model-View-Controller, which by default includes all the required MVC services and middleware in the request processing pipeline.
namespace FirstCoreMVCApplication { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run(); } } }
Now, run your application and resize the browser window, or use a mobile device to see the responsive navigation menu in action. The menu items should collapse into a hamburger menu (three horizontal lines) on smaller screens. When you click the hamburger menu, it will show the menu items in a dropdown fashion.
I will discuss Form Tag Helpers in ASP.NET Core Application in the next article. I explain how to create Responsive Navigation Menus in ASP.NET Core MVC Applications using Bootstrap with Examples in this article. I hope you enjoy this article, “How to Create Responsive Navigation Menus in ASP.NET Core MVC?”.
About the Author: Pranaya Rout
Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.
Hello,
i tried this implementing but facing small issue. I followed all steps ui is coming correctly but anchore tag helper not rendering its showing as taghelper.
i modified removing ; in _ViewImports.cshtml then tag helpers working properly. but UI is not working properly.
@using AspNetCore_Menu.Models
@using AspNetCore_Menu.Models.DataTypes
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
if ; is kept for each like @using AspNetCore_Menu.Models; then ui is properly coming.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseMvcWithDefaultRoute();
}
Any suggestions.
and thanks for nice article its easy to understand.
Can you update the page to include the img files you referred to (Logo.png and Student.png)? For adding bootstrap and JQuery files, is it the same as adding the client package of twitter-bootstrap you mentioned earlier? Thanks.
[Generator]
public class MenuPagesGenerator : ISourceGenerator
{
private const string RouteAttributeName = “Microsoft.AspNetCore.Components.RouteAttribute”;
private const string DescriptionAttributeName = “System.ComponentModel.Description”;
public void Initialize(GeneratorInitializationContext context) { }
public void Execute(GeneratorExecutionContext context)
{
try
{
var menuComponents = GetMenuComponents(context.Compilation);
var pageDetailsSource = SourceText.From(Templates.MenuPages(menuComponents), Encoding.UTF8);
context.AddSource(“PageDetails”, pageDetailsSource);
context.AddSource(“PageDetail”, SourceText.From(Templates.PageDetail(), Encoding.UTF8));
}
catch (Exception)
{
Debugger.Launch();
}
}
private static ImmutableArray GetMenuComponents(Compilation compilation)
{
// Get all classes
IEnumerable allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes());
IEnumerable allClasses = allNodes
.Where(d => d.IsKind(SyntaxKind.ClassDeclaration))
.OfType();
return allClasses
.Select(component => TryGetMenuComponent(compilation, component))
.Where(page => page is not null)
.Cast()// stops the nullable lies
.ToImmutableArray();
}
private static RouteableComponent? TryGetMenuComponent(Compilation compilation, ClassDeclarationSyntax component)
{
var attributes = component.AttributeLists
.SelectMany(x => x.Attributes)
.Where(attr =>
attr.Name.ToString() == RouteAttributeName
|| attr.Name.ToString() == DescriptionAttributeName)
.ToList();
var routeAttribute = attributes.FirstOrDefault(attr => attr.Name.ToString() == RouteAttributeName);
var descriptionAttribute = attributes.FirstOrDefault(attr => attr.Name.ToString() == DescriptionAttributeName);
if (routeAttribute is null || descriptionAttribute is null)
{
return null;
}
if (
routeAttribute.ArgumentList?.Arguments.Count != 1 ||
descriptionAttribute.ArgumentList?.Arguments.Count != 1)
{
// no route path or description value
return null;
}
var semanticModel = compilation.GetSemanticModel(component.SyntaxTree);
var routeArg = routeAttribute.ArgumentList.Arguments[0];
var routeExpr = routeArg.Expression;
var routeTemplate = semanticModel.GetConstantValue(routeExpr).ToString();
var descriptionArg = descriptionAttribute.ArgumentList.Arguments[0];
var descriptionExpr = descriptionArg.Expression;
var title = semanticModel.GetConstantValue(descriptionExpr).ToString();
return new RouteableComponent(routeTemplate, title);
}
}
_________
//MENU builder
[Route(“string”)]
public IActionResult RenderString()
{
return View(“String”,GetMenuString());
}
private string GetMenuString()
{
var menuItems = GetAllMenuItems();
var builder = new StringBuilder();
builder.Append(“”);
builder.Append(GetMenuLiString(menuItems, null));
builder.Append(“”);
return builder.ToString();
}
private string GetMenuLiString(IList menuList, string parentId)
{
var children = GetChildrenMenu(menuList, parentId);
if(children.Count<=0)
{
return "";
}
var builder = new StringBuilder();
foreach (var item in children)
{
var childStr = GetMenuLiString(menuList, item.ID);
if(!string.IsNullOrWhiteSpace(childStr))
{
builder.Append("”);
builder.Append(““);
builder.AppendFormat(“ {1}”,item.IconClass,item.Content);
builder.Append(“”);
builder.Append(““);
builder.Append(“”);
builder.Append(““);
builder.Append(“”);
builder.Append(childStr);
builder.Append(“”);
builder.Append(“”);
}
else
{
builder.Append(“”);
builder.AppendFormat(““,item.Href);
builder.AppendFormat(“ {1}”,item.IconClass,item.Content);
builder.Append(““);
builder.Append(“”);
}
}
return builder.ToString();
}