How can dynamic breadcrumbs be achieved with ASP.net MVC?

Viewed 91516

How can dynamic breadcrumbs be achieved with ASP.net MVC?

If you are curious about what breadcrumbs are:

What are breadcrumbs? Well, if you have ever browsed an online store or read posts in a forum, you have likely encountered breadcrumbs. They provide an easy way to see where you are on a site. Sites like Craigslist use breadcrumbs to describe the user's location. Above the listings on each page is something that looks like this:

s.f. bayarea craigslist > city of san francisco > bicycles

EDIT

I realize what is possible with the SiteMapProvider. I am also aware of the providers out there on the net that will let you map sitenodes to controllers and actions.

But, what about when you want a breadcrumb's text to match some dynamic value, like this:

Home > Products > Cars > Toyota

Home > Products > Cars > Chevy

Home > Products > Execution Equipment > Electric Chair

Home > Products > Execution Equipment > Gallows

... where the product categories and the products are records from a database. Some links should be defined statically (Home for sure).

I am trying to figure out how to do this, but I'm sure someone has already done this with ASP.net MVC.

7 Answers

Sitemap's are definitely one way to go... alternatively, you can write one yourself! (of course as long as standard MVC rules are followed)... I just wrote one, I figured I would share here.

@Html.ActionLink("Home", "Index", "Home")
@if(ViewContext.RouteData.Values["controller"].ToString() != "Home") {
    @:> @Html.ActionLink(ViewContext.RouteData.Values["controller"].ToString(), "Index", ViewContext.RouteData.Values["controller"].ToString()) 
}
@if(ViewContext.RouteData.Values["action"].ToString() != "Index"){
    @:> @Html.ActionLink(ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString()) 
}

Hopefully someone will find this helpful, this is exactly what I was looking for when I searched SO for MVC breadcrumbs.

For those using ASP.NET Core 2.0 and looking for a more decoupled approach than vulcan's HtmlHelper, I recommend having a look at using a partial view with dependency injection.

Below is a simple implementation which can easily be molded to suit your needs.

The breadcrumb service (./Services/BreadcrumbService.cs):

using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using System;
using System.Collections.Generic;

namespace YourNamespace.YourProject
{  
  public class BreadcrumbService : IViewContextAware
  {
    IList<Breadcrumb> breadcrumbs;

    public void Contextualize(ViewContext viewContext)
    {
      breadcrumbs = new List<Breadcrumb>();

      string area = $"{viewContext.RouteData.Values["area"]}";
      string controller = $"{viewContext.RouteData.Values["controller"]}";
      string action = $"{viewContext.RouteData.Values["action"]}";
      object id = viewContext.RouteData.Values["id"];
      string title = $"{viewContext.ViewData["Title"]}";   

      breadcrumbs.Add(new Breadcrumb(area, controller, action, title, id));

      if(!string.Equals(action, "index", StringComparison.OrdinalIgnoreCase))
      {
        breadcrumbs.Insert(0, new Breadcrumb(area, controller, "index", title));
      }
    }

    public IList<Breadcrumb> GetBreadcrumbs()
    {
      return breadcrumbs;
    }
  }

  public class Breadcrumb
  {
    public Breadcrumb(string area, string controller, string action, string title, object id) : this(area, controller, action, title)
    {
      Id = id;
    }

    public Breadcrumb(string area, string controller, string action, string title)
    {
      Area = area;
      Controller = controller;
      Action = action;

      if (string.IsNullOrWhiteSpace(title))
      {
         Title = Regex.Replace(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(string.Equals(action, "Index", StringComparison.OrdinalIgnoreCase) ? controller : action), "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
      }
      else
      {
         Title = title;
      } 
    }

    public string Area { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }
    public object Id { get; set; }
    public string Title { get; set; }
  }
}

Register the service in startup.cs after AddMvc():

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddTransient<BreadcrumbService>(); 

Create a partial to render the breadcrumbs (~/Views/Shared/Breadcrumbs.cshtml):

@using YourNamespace.YourProject.Services
@inject BreadcrumbService BreadcrumbService

@foreach(var breadcrumb in BreadcrumbService.GetBreadcrumbs())
{
    <a asp-area="@breadcrumb.Area" asp-controller="@breadcrumb.Controller" asp-action="@breadcrumb.Action" asp-route-id="@breadcrumb.Id">@breadcrumb.Title</a>
}

At this point, to render the breadcrumbs simply call Html.Partial("Breadcrumbs") or Html.PartialAsync("Breadcrumbs").

Related