56

ASP.net MVC で動的ブレッドクラムを実現するにはどうすればよいですか?

ブレッドクラムとは何かに興味がある場合:

ブレッドクラムとは オンライン ストアを閲覧したり、フォーラムの投稿を読んだりしたことがあれば、パンくずリストに遭遇したことがあるでしょう。サイトのどこにいるかを簡単に確認できます。Craigslist などのサイトでは、パンくずリストを使用してユーザーの場所を説明しています。各ページのリストの上には、次のようなものがあります。

sf bayarea craigslist > サンフランシスコ市 > 自転車

編集

SiteMapProvider で何ができるかを理解しています。また、サイトノードをコントローラーとアクションにマップできるプロバイダーがネット上にあることも認識しています。

しかし、次のように、ブレッドクラムのテキストを何らかの動的な値に一致させたい場合はどうでしょうか。

ホーム > 製品 > 車 > トヨタ

ホーム > 製品 > 車 > シボレー

ホーム > 製品 > 実行機器 > 電気椅子

ホーム > 製品 > 実行機器 > 絞首台

...製品カテゴリと製品はデータベースからのレコードです。一部のリンクは静的に定義する必要があります (確かにホーム)。

私はこれを行う方法を理解しようとしていますが、誰かが ASP.net MVC で既にこれを行っていると確信しています。

4

7 に答える 7

66

サイトマップは間違いなく 1 つの方法です。代わりに、自分で作成することもできます。(もちろん、標準の MVC ルールが守られている限り) ... 1 つだけ書いたので、ここで共有したいと思います。

@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ブレッドクラムを検索したときに探していたものです。

于 2013-11-27T18:10:07.080 に答える
32

ASP.NET 5 (別名 ASP.NET Core)、MVC コア ソリューション

ASP.NET Core では、拡張メソッドでマークアップを文字列化する必要がないため、さらに最適化されています。

~/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.RawRazor ビューでは、Razor が を処理するときにエスケープを処理するため、は必要ありませんIHtmlContent

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

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

    @RenderBody()
    <hr />
...
...

ASP.NET 4、MVC 5 ソリューション

===元の/以下の古い回答===

(上記のSean Haddyの回答を拡大)

拡張機能を使用する (ビューをきれいに保つ) 場合は、次のようにすることができます。

~/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 />
...
...
于 2014-10-18T12:00:33.847 に答える
25

これを行うためのツールが codeplex にあります: http://mvcsitemap.codeplex.com/ [プロジェクトは github に移動しました]

編集:

データベースから SiteMapProvider を派生させる方法があります: http://www.asp.net/Learn/data-access/tutorial-62-cs.aspx

mvcsitemap ツールを変更して、それを使用して必要なものを取得できる場合があります。

于 2009-07-01T00:33:11.700 に答える
5

この問題を自分で解決するために、この nuget パッケージを作成しました。

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

アイデアがある場合は、ここで貢献できます。

https://github.com/thelarz/MvcBreadCrumbs

于 2015-10-08T19:59:50.843 に答える
2

興味のある人のために、HtmlExtensionエリアも考慮し、さらにリフレクションを使用して、エリア内にデフォルトコントローラーがあるか、コントローラー内にインデックスアクションがあるかを確認する改良版を作成しました。

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

間違いなく改善できる場合 (おそらく、考えられるすべてのケースを網羅しているわけではありません) ですが、今まで失敗したことはありませんでした。

于 2017-04-27T13:28:33.427 に答える