Вопрос по asp.net-mvc – Как можно добиться динамических хлебных крошек с помощью ASP.net MVC?

45

Как можетдинамические сухари быть достигнуто с ASP.net MVC?

Если вам интересно, что такое хлебные крошки:

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

Я понимаю, что это возможно с SiteMapProvider. Я также знаю о провайдерах в сети, которые позволят вам сопоставить ситеноды с контроллерами и действиями.

Но что делать, когда вы хотите, чтобы текст хлебной крошки совпадал с некоторым динамическим значением, например так:

Home > Products > Cars > Toyota

Home > Products > Cars > Chevy

Home > Products > Execution Equipment > Electric Chair

Home > Products > Execution Equipment > Gallows

... где категории продуктов и продукты являются записями из базы данных. Некоторые ссылки должны быть определены статически (Домой наверняка).

Я пытаюсь понять, как это сделать, но я уверен, что кто-то уже сделал это с ASP.net MVC.

Ваш Ответ

7   ответов
2

Maarten Balliauw's MvcSiteMapProvider работал довольно хорошо для меня.

Я создал небольшое приложение MVC, чтобы проверить его провайдера:MvcSiteMapProvider Test (404)

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Ronnie Overby
Error: User Rate Limit Exceeded Ronnie Overby
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Ronnie Overby
52

вы можете написать его самостоятельно! (конечно, до тех пор, пока соблюдаются стандартные правила MVC) ... Я только что написал одно, я решил поделиться здесь.

@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()) 
}

Надеюсь, кто-то найдет это полезным, это именно то, что я искал, когда искал SO для хлебных крошек MVC.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
2

кто использует ASP.NET Core 2.0 и ищет более несвязанный подход, чем HtmlHelper от vulcan, я рекомендую взглянуть на использование частичного представления свнедрение зависимости.

Ниже приведена простая реализация, которая может быть легко отлита в соответствии с вашими потребностями.

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>
}

На этом этапе, чтобы сделать хлебные крошки просто позвонитеHtml.Partial("Breadcrumbs") или жеHtml.PartialAsync("Breadcrumbs").

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
2

HtmlExtension это также учитывает Области и, кроме того, использует Reflection, чтобы проверить, есть ли Контроллер по умолчанию внутри Области или действие Index внутри Контроллера:

public static class HtmlExtensions
    {
        public static MvcHtmlString BuildBreadcrumbNavigation(this HtmlHelper helper)
        {
            string area = (helper.ViewContext.RouteData.DataTokens["area"] ?? "").ToString();
            string controller = helper.ViewContext.RouteData.Values["controller"].ToString();
            string action = helper.ViewContext.RouteData.Values["action"].ToString();

            // add link to homepage by default
            StringBuilder breadcrumb = new StringBuilder(@"
                <ol class='breadcrumb'>
                    <li>" + helper.ActionLink("Homepage", "Index", "Home", new { Area = "" }, new { @class="first" }) + @"</li>");

            // add link to area if existing
            if (area != "")
            {
                breadcrumb.Append("<li>");
                if (ControllerExistsInArea("Default", area)) // by convention, default Area controller should be named Default
                {
                    breadcrumb.Append(helper.ActionLink(area.AddSpaceOnCaseChange(), "Index", "Default", new { Area = area }, new { @class = "" }));
                }
                else
                {
                    breadcrumb.Append(area.AddSpaceOnCaseChange());
                }
                breadcrumb.Append("</li>");
            }

            // add link to controller Index if different action
            if ((controller != "Home" && controller != "Default") && action != "Index")
            {
                if (ActionExistsInController("Index", controller, area))
                {
                    breadcrumb.Append("<li>");
                    breadcrumb.Append(helper.ActionLink(controller.AddSpaceOnCaseChange(), "Index", controller, new { Area = area }, new { @class = "" }));
                    breadcrumb.Append("</li>");
                }
            }

            // add link to action
            if ((controller != "Home" && controller != "Default") || action != "Index")
            {
                breadcrumb.Append("<li>");
                //breadcrumb.Append(helper.ActionLink((action.ToLower() == "index") ? controller.AddSpaceOnCaseChange() : action.AddSpaceOnCaseChange(), action, controller, new { Area = area }, new { @class = "" }));
                breadcrumb.Append((action.ToLower() == "index") ? controller.AddSpaceOnCaseChange() : action.AddSpaceOnCaseChange());
                breadcrumb.Append("</li>");
            }

            return MvcHtmlString.Create(breadcrumb.Append("</ol>").ToString());
        }

        public static Type GetControllerType(string controller, string area)
        {
            string currentAssembly = Assembly.GetExecutingAssembly().GetName().Name;
            IEnumerable<Type> controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(o => typeof(IController).IsAssignableFrom(o));

            string typeFullName = String.Format("{0}.Controllers.{1}Controller", currentAssembly, controller);
            if (area != "")
            {
                typeFullName = String.Format("{0}.Areas.{1}.Controllers.{2}Controller", currentAssembly, area, controller);
            }

            return controllerTypes.Where(o => o.FullName == typeFullName).FirstOrDefault();
        }

        public static bool ActionExistsInController(string action, string controller, string area)
        {
            Type controllerType = GetControllerType(controller, area);
            return (controllerType != null && new ReflectedControllerDescriptor(controllerType).GetCanonicalActions().Any(x => x.ActionName == action));
        }

        public static bool ControllerExistsInArea(string controller, string area)
        {
            Type controllerType = GetControllerType(controller, area);
            return (controllerType != null);
        }


    public static string AddSpaceOnCaseChange(this string text)
    {
        if (string.IsNullOrWhiteSpace(text))
            return "";
        StringBuilder newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]) && text[i - 1] != ' ')
                newText.Append(' ');
            newText.Append(text[i]);
        }
        return newText.ToString();
    }
}

Если можно, безусловно, можно улучшить (возможно, не охватывает все возможные случаи), но это не подвело меня до сих пор.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded@Html.Raw(Html.BuildBreadcrumbNavigation())Error: User Rate Limit Exceeded
21

ASP.NET 5 (aka ASP.NET Core), MVC Core Solution

поскольку нам не нужно упорядочивать разметку в методе расширения.

В~/Extesions/HtmlExtensions.cs:

using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace YourProjectNamespace.Extensions
{
    public static class HtmlExtensions
    {
        private static readonly HtmlContentBuilder _emptyBuilder = new HtmlContentBuilder();

        public static IHtmlContent BuildBreadcrumbNavigation(this IHtmlHelper helper)
        {
            if (helper.ViewContext.RouteData.Values["controller"].ToString() == "Home" ||
                helper.ViewContext.RouteData.Values["controller"].ToString() == "Account")
            {
                return _emptyBuilder;
            }

            string controllerName = helper.ViewContext.RouteData.Values["controller"].ToString();
            string actionName = helper.ViewContext.RouteData.Values["action"].ToString();

            var breadcrumb = new HtmlContentBuilder()
                                .AppendHtml("<ol class='breadcrumb'><li>")
                                .AppendHtml(helper.ActionLink("Home", "Index", "Home"))
                                .AppendHtml("</li><li>")
                                .AppendHtml(helper.ActionLink(controllerName.Titleize(),
                                                          "Index", controllerName))
                                .AppendHtml("</li>");


            if (helper.ViewContext.RouteData.Values["action"].ToString() != "Index")
            {
                breadcrumb.AppendHtml("<li>")
                          .AppendHtml(helper.ActionLink(actionName.Titleize(), actionName, controllerName))
                          .AppendHtml("</li>");
            }

            return breadcrumb.AppendHtml("</ol>");
        }
    }
}

~/Extensions/StringExtensions.cs остается таким же, как показано ниже (прокрутите вниз, чтобы увидеть версию MVC5).

С точки зрения бритвы нам не нужноHtml.Raw, поскольку Razor заботится о побеге, когда имеет дело сIHtmlContent:

....
....
<div class="container body-content">

    <!-- #region Breadcrumb -->
    @Html.BuildBreadcrumbNavigation()
    <!-- #endregion -->

    @RenderBody()
    <hr />
...
...
ASP.NET 4, MVC 5 Solution

=== ОРИГИНАЛЬНЫЙ / СТАРЫЙ ОТВЕТ НИЖЕ ===

(Расширяя ответ Шона Хэдди выше)

Если вы хотите сделать его управляемым расширением (поддерживая чистоту Views), вы можете сделать что-то вроде:

В~/Extesions/HtmlExtensions.cs:

(совместимо с MVC5 / начальная загрузка)

using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace YourProjectNamespace.Extensions
{
    public static class HtmlExtensions
    {
        public static string BuildBreadcrumbNavigation(this HtmlHelper helper)
        {
            // optional condition: I didn't wanted it to show on home and account controller
            if (helper.ViewContext.RouteData.Values["controller"].ToString() == "Home" ||
                helper.ViewContext.RouteData.Values["controller"].ToString() == "Account")
            {
                return string.Empty;
            }

            StringBuilder breadcrumb = new StringBuilder("<ol class='breadcrumb'><li>").Append(helper.ActionLink("Home", "Index", "Home").ToHtmlString()).Append("</li>");


            breadcrumb.Append("<li>");
            breadcrumb.Append(helper.ActionLink(helper.ViewContext.RouteData.Values["controller"].ToString().Titleize(),
                                               "Index",
                                               helper.ViewContext.RouteData.Values["controller"].ToString()));
            breadcrumb.Append("</li>");

            if (helper.ViewContext.RouteData.Values["action"].ToString() != "Index")
            {
                breadcrumb.Append("<li>");
                breadcrumb.Append(helper.ActionLink(helper.ViewContext.RouteData.Values["action"].ToString().Titleize(),
                                                    helper.ViewContext.RouteData.Values["action"].ToString(),
                                                    helper.ViewContext.RouteData.Values["controller"].ToString()));
                breadcrumb.Append("</li>");
            }

            return breadcrumb.Append("</ol>").ToString();
        }
    }
}

В~/Extensions/StringExtensions.cs:

using System.Globalization;
using System.Text.RegularExpressions;

namespace YourProjectNamespace.Extensions
{
    public static class StringExtensions
    {
        public static string Titleize(this string text)
        {
            return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text).ToSentenceCase();
        }

        public static string ToSentenceCase(this string str)
        {
            return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
        }
    }
}

Затем используйте это как (в _Layout.cshtml, например):

....
....
<div class="container body-content">

    <!-- #region Breadcrumb -->
    @Html.Raw(Html.BuildBreadcrumbNavigation())
    <!-- #endregion -->

    @RenderBody()
    <hr />
...
...
CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text.ToSentenceCase())Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded/Error: User Rate Limit Exceeded/Error: User Rate Limit Exceeded>
Error: User Rate Limit ExceededStringBuilderError: User Rate Limit ExceededbreadcrumbError: User Rate Limit ExceededBootstrapError: User Rate Limit Exceeded.breadcrumb > li + li:before { font-family: 'FontAwesome'; content: "\f105"; }Error: User Rate Limit Exceeded/Error: User Rate Limit Exceeded>
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1

https://www.nuget.org/packages/MvcBreadCrumbs/

Вы можете внести свой вклад здесь, если у вас есть идеи для этого:

https://github.com/thelarz/MvcBreadCrumbs

Error: User Rate Limit Exceeded
22

http://mvcsitemap.codeplex.com/ [проектпереехал в github]

Редактировать:

Есть способ извлечь SiteMapProvider из базы данных:http://www.asp.net/Learn/data-access/tutorial-62-cs.aspx

Вы можете изменить инструмент mvcsitemap, чтобы использовать его, чтобы получить то, что вы хотите.

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededControllerOne > Details 1 > ControllerTwo ?
Error: User Rate Limit Exceeded Ronnie Overby

Похожие вопросы