記事詳細


投稿日
2026年09月29日20時13分
タイトル

ASP.NET Core MVC: MVCアプリでメニューを、データベースのデータから階層ドロップダウンで表示する方法を教えてください。そのオブジェクトを‘_Layout.cshtml’の<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">内に記述したいです。

内容

了解です。
では、前回の レスポンシブ対応(PCホバー/スマホクリック)多階層ドロップダウンメニュー を、
さらに 現在のページURLに基づくアクティブメニュー自動ハイライト機能 を追加して再生成します。

これにより、現在表示しているページに対応するメニュー項目が自動的に強調表示されます。

1. データベース構造(同じ)

Sql
CREATE TABLE Menus ( Id INT PRIMARY KEY IDENTITY, Title NVARCHAR(100) NOT NULL, Url NVARCHAR(200) NULL, ParentId INT NULL, FOREIGN KEY (ParentId) REFERENCES Menus(Id) );

2. モデルと DbContext

Csharp
// Models/Menu.cs using System.Collections.Generic; public class Menu { public int Id { get; set; } public string Title { get; set; } public string Url { get; set; } public int? ParentId { get; set; } public Menu Parent { get; set; } public ICollection<Menu> Children { get; set; } = new List<Menu>(); } // Data/ApplicationDbContext.cs using Microsoft.EntityFrameworkCore; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Menu> Menus { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Menu>() .HasOne(m => m.Parent) .WithMany(m => m.Children) .HasForeignKey(m => m.ParentId) .OnDelete(DeleteBehavior.Restrict); } }

3. メニュー取得サービス

Csharp
// Services/MenuService.cs using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public interface IMenuService { Task<List<Menu>> GetMenuHierarchyAsync(); } public class MenuService : IMenuService { private readonly ApplicationDbContext _context; public MenuService(ApplicationDbContext context) { _context = context; } public async Task<List<Menu>> GetMenuHierarchyAsync() { var allMenus = await _context.Menus .AsNoTracking() .ToListAsync(); return BuildHierarchy(allMenus, null); } private List<Menu> BuildHierarchy(List<Menu> allMenus, int? parentId) { return allMenus .Where(m => m.ParentId == parentId) .Select(m => new Menu { Id = m.Id, Title = m.Title, Url = m.Url, ParentId = m.ParentId, Children = BuildHierarchy(allMenus, m.Id) }) .ToList(); } }

4. ViewComponent

Csharp
// ViewComponents/MenuViewComponent.cs using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; public class MenuViewComponent : ViewComponent { private readonly IMenuService _menuService; public MenuViewComponent(IMenuService menuService) { _menuService = menuService; } public async Task<IViewComponentResult> InvokeAsync() { var menuHierarchy = await _menuService.GetMenuHierarchyAsync(); return View(menuHierarchy); } }

5. ViewComponent のビュー(レスポンシブ + アクティブハイライト対応)

Views/Shared/Components/Menu/Default.cshtml

Razor
@model List<Menu> @{ // 現在のURLパス(クエリ除去) var currentPath = Context.Request.Path.Value?.TrimEnd('/').ToLower() ?? "/"; } @functions { void RenderMenuItems(List<Menu> menus, bool isSubMenu = false) { foreach (var menu in menus) { bool isActive = !string.IsNullOrEmpty(menu.Url) && currentPath == menu.Url.TrimEnd('/').ToLower(); if (menu.Children.Any()) { var liClass = isSubMenu ? "dropdown-submenu" : "nav-item dropdown"; var aClass = isSubMenu ? "dropdown-item dropdown-toggle" : "nav-link dropdown-toggle"; if (isActive) aClass += " active"; <li class="@liClass"> <a class="@aClass" href="@(!string.IsNullOrEmpty(menu.Url) ? menu.Url : "#")" id="menu-@menu.Id" role="button" data-bs-toggle="dropdown" aria-expanded="false"> @menu.Title </a> <ul class="dropdown-menu" aria-labelledby="menu-@menu.Id"> @RenderMenuItems(menu.Children.ToList(), true) </ul> </li> } else { if (isSubMenu) { <li><a class="dropdown-item @(isActive ? "active" : "")" href="@menu.Url">@menu.Title</a></li> } else { <li class="nav-item"><a class="nav-link @(isActive ? "active" : "")" href="@menu.Url">@menu.Title</a></li> } } } } } <ul class="navbar-nav flex-grow-1"> @RenderMenuItems(Model) </ul> <style> /* 多階層ドロップダウンの位置調整 */ .dropdown-submenu { position: relative; } .dropdown-submenu > .dropdown-menu { top: 0; left: 100%; margin-top: -0.5rem; } /* PC時のみホバー展開 */ @media (min-width: 992px) { .dropdown:hover > .dropdown-menu { display: block; } .dropdown-submenu:hover > .dropdown-menu { display: block; } } /* アクティブメニュー強調 */ .nav-link.active, .dropdown-item.active { background-color: #0d6efd; color: #fff !important; } </style> <script> // スマホではクリック展開を維持(Bootstrap標準動作) document.addEventListener('DOMContentLoaded', function () { const dropdownToggles = document.querySelectorAll('.dropdown-toggle'); dropdownToggles.forEach(function (toggle) { toggle.addEventListener('click', function (e) { if (window.innerWidth >= 992) { e.preventDefault(); // PCではクリックリンク無効化(ホバーで開くため) } }); }); }); </script>

6. _Layout.cshtml に組み込み

Razor
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> @await Component.InvokeAsync("Menu") </div>

7. Program.cs でサービス登録

Csharp
builder.Services.AddScoped<IMenuService, MenuService>();

改良ポイント

  • アクティブメニュー自動ハイライト
    現在のURLと一致するメニュー項目に active クラスを付与
  • レスポンシブ対応
    PCではホバー展開、スマ

Powered by Froala Editor

添付画像

画像はありません
Genreのカテゴリ-名
Views
編集 | 記事一覧