ساخت منوهای چند سطحی در ASP.NET MVC
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: دو دقیقه

پیش نیاز مطلب جاری مطالب زیر می‌باشند:
1- EF Code First #8
2- مباحث تکمیلی مدل‌های خود ارجاع دهنده در EF Code First
3- نگاهی به اجزای تعاملی Twitter Bootstrap 

هدف از مطلب جاری نحوه نمایش منوی‌های چند سطحی می‌باشد، ابتدا مثال کامل زیر را در نظر بگیرید :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Menu.Models.Entities
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int? ParentId { get; set; }
        public virtual Category Parent { get; set; }
        public virtual ICollection<Category> Children { get; set; }
    }
}

public class MyContext : DbContext
{
        public DbSet<Category> Category { get; set; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Self Referencing Entity
            modelBuilder.Entity<Category>()
                        .HasOptional(x => x.Parent)
                        .WithMany(x => x.Children)
                        .HasForeignKey(x => x.ParentId)
                        .WillCascadeOnDelete(false);
 
            base.OnModelCreating(modelBuilder);
        }
}

همانطور که ملاحظه می‌کنید، مدل ما شامل مشخصات گروه محصولات می‌باشد که به صورت خود ارجاع دهنده (خاصیت Parent به همین کلاس اشاره میکند) تعریف شده است. در مورد خواص مدل‌های خود ارجاع دهنده، مطالبی را در سایت مطالعه کردید (خواص مربوط در مطالب گفته شده دقیقاً به همان صورت می‌باشد و نیازی به توضیح اضافه‌تری نیست).
هدف از این بحث، نحوه نمایش گروه محصولات داخل منو به صورت چند سطحی می‌باشد، جهت نمایش می‌بایست از تکنیک recursive function استفاده کنید، ابتدا در نظر داشته باشید که ساختار منوی تشکیل شده می‌بایست بدین صورت باشد :
 

این حالت می‌تواند تا n سطح پیش برود، حال نحوه نمایش در View مربوطه باید به صورت زیر باشد :

@using Menu.Helper
@model IEnumerable<.Models.Entities.Category>
@ShowTree(Model)
 
@helper ShowTree(IEnumerable<Menu.Models.Entities.Category> categories)
{
    foreach (var item in categories)
    {
    <li class="@(item.Children.Any() ? "dropdown-submenu" : "")">
 
        @Html.ActionLink(item.Name, actionName: "Category", controllerName: "Product", routeValues: new { Id = item.Id, productName = item.Name.ToSeoUrl() }, htmlAttributes: null)
        @if (item.Children.Any())
        {
            <ul>
                @ShowTree(item.Children)
            </ul>
                }
    </li>
 
        }
}
توجه داشته باشید که رندر نهایی توسط Bootstrap انجام شده است. ساختار منو همانطور که ملاحظه می‌کنید با استفاده از کلاس‌های drop-down که از کلاس‌های پیش فرض بوت استرپ می‌باشد تشکیل شده است همچنین کلاس dropdown-submenu که از نسخه 2 به بعد بوت استرپ موجود می‌باشد، استفاده شده است.

یک نکته :
در خط 9 این مورد را که آیا آیتم جاری فرزندی دارد چک کرده ایم اگر داشته باشد کلاس dropdown-submenu  را به li جاری اضافه میکند.
  • #
    ‫۱۰ سال و ۱۲ ماه قبل، دوشنبه ۲۲ مهر ۱۳۹۲، ساعت ۱۴:۴۱
    با سلام 
    من این برنامه رو اجرا کردم ولی روی خط 5 پیغام خطا 
     System.NullReferenceException: Object reference not set to an instance of an object. 
    رو میگیرم .
    اگر امکانش هست میتونید پروژه این مثال رو بگذارید.
    ممنون
    • #
      ‫۱۰ سال و ۱۲ ماه قبل، دوشنبه ۲۲ مهر ۱۳۹۲، ساعت ۱۵:۲۶
      شما Query رو به View مربوطه پاس دادید؟
      من توی لایه Service متد GetAll رو به این صورت تعریف کردم :
      public IList<Category> GetAll()
              {
                  //return _category.AsNoTracking().ToList();
                  return _category.Where(category => category.ParentId == null)
                                  .Include(category => category.Children).ToList();
              }

      و در نهایت هم توسط Action Method زیر اون رو به Partial View پاس دادم :
      [ChildActionOnly]
              public ActionResult Categories()
              {
                  var query = _categoryService.GetAll();
                  return PartialView("_Categories",query);
              }



  • #
    ‫۱۰ سال و ۱۱ ماه قبل، سه‌شنبه ۳۰ مهر ۱۳۹۲، ساعت ۱۵:۴۱
    سلام  
    اگر امکان دارد مثالی از درج ، ویرایش یا حذف زیر مجموعه‌ها را بیان کنید  یا اگر منبع مفیدی می‌شناسید برای ویرایش و کار با navigation peroperty‌ها لطفا معرفی کنید
    با سپاس فراوان
    • #
      ‫۱۰ سال و ۱۱ ماه قبل، سه‌شنبه ۳۰ مهر ۱۳۹۲، ساعت ۱۷:۳۲
      سلام،
      به عنوان مثال من برای ثبت یک Category جدید از TreeView استفاده میکنم به اضافه یک مورد که تعیین کننده Child یا Parent بودن Category است.
  • #
    ‫۱۰ سال و ۱۰ ماه قبل، دوشنبه ۴ آذر ۱۳۹۲، ساعت ۰۳:۲۴
    جناب مهندس ممنون از زحمت شما.
    یک سوال؟
    در بوت استرپ منوی چند سطحی نمی‌توان درست کرد ایا کد خاصی اضافه کردین یا از پلاگین خاصی استفاده میکنین؟
    • #
      ‫۱۰ سال و ۱۰ ماه قبل، دوشنبه ۴ آذر ۱۳۹۲، ساعت ۰۳:۳۴
      من در این مثال از ورژن 2 بوت استرپ استفاده کرده ام که دارای کلاس dropdown-submenu می‌باشد. این کلاس ظاهراً در ورژن 3 حذف شده است، در هر صورت اگر از ورژن 3 استفاده می‌کنید می‌توانید با افزودن مقداری کد CSS این مورد را اضافه کنید: 
      .dropdown-submenu{position:relative;}
      .dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
      .dropdown-submenu:hover>.dropdown-menu{display:block;}
      .dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
      .dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
      .dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}


      • #
        ‫۹ سال و ۸ ماه قبل، سه‌شنبه ۲۳ دی ۱۳۹۳، ساعت ۱۵:۱۸
        با سلام
        جسارتا جهت RTL منو بوت استرپ 3 به حالت چند سطحی باید کد بالا را به حالت زیر تغییر داد
        .dropdown-submenu{position:relative}
        .dropdown-submenu > .dropdown-menu{top:0;right:100%;margin-top:-6px;margin-right:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px;border-radius:0 6px 6px 6px}
        .dropdown-submenu:hover > .dropdown-menu{display:block}
        .dropdown-submenu > a:after{display:block;content:" ";float:left;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 5px 5px 0;border-right-color:#ccc;margin-top:5px;margin-left:-10px}
        .dropdown-submenu:hover > a:after{border-left-color:#fff}
        .dropdown-submenu.pull-Right{float:none}
        .dropdown-submenu.pull-Right > .dropdown-menu{right:-100%;margin-right:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}

  • #
    ‫۱۰ سال و ۴ ماه قبل، جمعه ۲ خرداد ۱۳۹۳، ساعت ۱۸:۵۵
    با سلام و تشکر به خاطر مطلب مفیدتون

    زمانی که منو رو به صورت پویا ایجاد می‌کنیم در هر بار لود صفحه باید اطلاعات مربوط به منو از بانک اطلاعاتی دریافت بشه آیا اطلاعات کش میشه از روش خاصی استفاده میشه که در هربار لود صفحه درخواست به بنک ارسال نشه
  • #
    ‫۱۰ سال و ۴ ماه قبل، سه‌شنبه ۶ خرداد ۱۳۹۳، ساعت ۱۷:۲۶
    با سلام
    ضمن تشکر از سایت بسیار خوبتون
    اگر ممکن است در خصوص متد  ToSeoUrl 
    که در خط 
       @Html.ActionLink(item.Name, actionName: "Category" , controllerName: "Product" , routeValues: new { Id = item.Id, productName = item.Name.ToSeoUrl() }, htmlAttributes: null )
    بکار رفته توضیح بدید 
  • #
    ‫۹ سال و ۱۲ ماه قبل، پنجشنبه ۳ مهر ۱۳۹۳، ساعت ۱۵:۵۹
    سلام؛ من مطالبی را که ابتدای مقاله فرمودید، مطالعه کرده‌ام. اگر امکان دارد مثال پروژه را قرار دهید تا بتوانیم از آن استفاده کنیم.
  • #
    ‫۸ سال و ۵ ماه قبل، جمعه ۲۷ فروردین ۱۳۹۵، ساعت ۱۶:۰۴
    نمیشه کاری کرد زیرمنوها زمان hover  روی منوهای ریشه، روی منوهای ریشه باز نشن، مثلا پایین بیفتن؟
    • #
      ‫۸ سال و ۵ ماه قبل، جمعه ۲۷ فروردین ۱۳۹۵، ساعت ۱۶:۱۳
      - منوی چند سطحی از بوت استرپ 3 حذف شده‌است. علت آن هم سازگار نبودن یک چنین طراحی با حالت mobile first و مرور آن در صفحات کوچک‌تر است. به صورت خلاصه طراحی که منوی چند سطحی داشته باشد، برای حالت مرور در موبایل مناسب نیست و نیاز به تغییر طراحی جهت یک سطح کردن آن دارد.
      - «رو» و «زیر» توسط z-index در css تنظیم می‌شوند. این‌ها را باید با مثلا فایرباگ به صورت زنده اضافه و بررسی کنید تا به جواب برسید.
      - برای بوت استرپ 3، منوی چند سطحی ویژه‌ای طراحی شده که به نظر با صفحات کوچک هم سازگار است: «smartmenus ».