ایجاد Menu اتوماتیک ازکنترلرها در MVC
دوشنبه 28 فروردین 1396شما می توانیدمنوی سایت خود را در ASP.NET MVC به صورت اتوماتیک, و با استفاده از ویژگی هایی که روی کنترلرها اعمال میشود بسازید. در این روش دیگر نیاز به نوشتن کدهای HTML برای ایجاد منو ندارید و کنترلر برای شما یک منوی امن و زیبا می سازد.
مقدمه
این ایده زمانی آغاز شد که ما میخواستیم آیتم های منو را ایجاد کنیم اما نه با برنامه نویسی (hardcode) یا با استفاده از پایگاه داده ، چرا که می دانستیم در برنامه mvc هر ویو( یا صفحه ای که شما بخواهید) توسط متد های موجود در کنترلر(Contoroller) قابل ارائه است. بنابراین ما استفاده از ویژگی ها (attributes ) و بازتاب آنها را برای این کار انتخاب کردیم .
مزایا
1-از هارد کد برای نام منو استفاده نشده است.
2-فقط با استفاده از ویژگی ها و متد هایی که روی کنترلر و یا متد های اکشن وجود دارد می توانید منو را حذف یا اضافه کنید.
3-منو محدود شده است ، به عنوان مثال : تولید منو با ایجاد حق دسترسی قابل کنترل است. در اینجا ما از یک ویژگی استفاده میکنیم اما شما در صورت نیاز میتوانید ویژگی های بیشتری را اضافه کنید(در این صورت باید در کد تغییراتی ایجاد کنید.)
4-سفارشی سازی منو قابل کنترل است.
5-ارتقا متدهای اکشن به عنوان یک منوی اصلی(در این صورت روی کنترلر ویژگی(attribute) گذاشته نمی شود.
6-منوی غیر متحرک ایجاد کنید. برای مثال : نام کنترلر به عنوان یک منوی سطح بالا(منوی اصلی) به کار رفته است ، اما شما نمی خواهید با زدن گزینه منوی اصلی که شامل زیر منو است یک صفحه را باز کنید و فقط گزینه های زیر منو قابلیت کلیک داشته باشند.
7-اضافه کردن آیکون روی منو ( با استفاده از fontawsome)
8-توسعه پذیری – اگر نیاز به ایجاد ویژگی های بیشتری روی منو دارید میتوانید این ویژگی ها را ایجاد کنید.
مهم : منبع دانلود
این کد بدون هیچ مشکلی بر روی Visual studio 2015 اجرا می شود. اگر شما این کد رو با نسخه های قدیمی تر باز کنید ممکن است مشکلاتی ایجاد شود و برای حلش به یک سری چیزها نیاز دارید.(شاید اگر فقط فایل های مهم را کپی کنید بهتر باشد.)
این کد بدون بسته Nuget packages است. بنابراین شما باید به صورت دستی نصب رو انجام بدید در غیر اینصورت کد اجرا نمی شود.
در اینجا میتوانید طریقه نصب NuGet Packages را ببینید.
https://www.codeproject.com/KB/Articles/1130643/nuget.png
استفاده از کد :
MenuItemAttribute نقش کلیدی را در اینجا دارد.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class MenuItemAttribute : Attribute { public MenuItemAttribute() { IsClickable = true; } public bool IsClickable { get; set; } public string Title { get; set; } public string Action { get; set; } public string CssIcon { get; set; } public int Order { get; set; } public Type ParentController { get; set; } }
همه خاصیت ها اختیاری است و شما بر اساس نیاز خود میتوانید از آنها استفاده کنید. اگر از خاصیت های این کد استفاده کنید چه اتفاقی می افتد :
IsClickable- به صورت پیش فرض
true
است.یعنی وقتی شما روی آن کلیک میکنید اجرا میشود .اگر این خاصیت را روی
false
تنظیم کنید با کلیک روی آن اتفاقی نمی افتد مگر اینکه به زیر منو دسترسی داشته باشد(در صورت وجود).
Title- به صورت پیش فرض نام کنترلر را بدون پسوند Controller
می گیرد.مثلا “Home” برای دسترسی به کلاس ” HomeController
” استفاده می شود،نام Action در صورتیکه از متد اکشن استفاده کرده باشیدکاربرد دارد.
Action –به صورت پیش فرض “Index” است و تنها زمانی موثر است که روی کنترلر اعمال شود، روی action method تاثیری ندارد و همیشه نام اکشن( action name)را میگیرد.
CssIcon
–
به صورت پیش فرض آیکون ندارد . شما میتوانید از آیکن های
font-awesome
استفاده کنید.
Order
–
به صورت پیش فرض صفراست یعنی منو کدهای سفارشی را از کنترلر میخواند.اگر مقداری برایش تنظیم کنید به صورت صعودی مرتب میشود.
ParentController
–
اگر بخواهید میتوانید از یک کنترلر دیگر به عنوان زیر منوی کنترلر استفاده کنید.
یک نمونه از کنترلر:
[MenuItem (Title = "مدیر", Order = 500,Action = "Users")] public class AdminController : Controller { // GET: Admin public ActionResult Index() { return View(); } [MenuItem(Title = "کاربران", Order = 500 ,CssIcon = "fa fa-users fa-lg fa-fw")] [AuthorizedRole("Admin")] public ActionResult Users() { return View(); } [MenuItem(Title = "تنظیمات", Order = 500)] [AuthorizedRole("Super user")] public ActionResult Settings() { return View(); } }
خوب ، چه اتفاقی می افتد ؟
در اولین خط نام اکشن "Users" ثبت شده است این به چه معناست؟ یعنی با زدن دکمه مدیر(Admin) در منو وارد صفحه کاربران خواهد شد.
در اکشن “Users” ، یک آیکون font-awesome تنظیم شده است ،که تنها برای کاربرانی که نقش" admin" دارند نمایش داده میشود. AuthorizedRole یکی دیگر از ویژگی های سفارشی شده است که مسئول بررسی ورود و خروج در هر زمانی است. شما می توانید کد را بررسی کنید و منطق آن را در زمان واقعی تغییر دهید.
روی اکشن"settings" عنوان نمایشی روی منو به " Site Settings " تغییر کرده است و به کاربرانی که نقش " Super user " را دارند، محدود شده است.
این فقط یک مثال بود . اما کنترل های دیگر موجود درکد را خودتان چک کنید.
چگونه این ویژگی ها تبدیل به منو میشوند:
MenuItemAttribute روی کنترلر ها و اکشن ها اعمال می شود و با کد نویسی به صورت داینامیک انتخاب میشود و لیستی از کلاس های Bootstrap Menu را جهت زیبا سازی منو برمی گرداند.
کد زیر بخشی از کلاس ساخت منو است.
public static List<Menu> CreateMenu() { var menus = new List<Menu>(); var currentAssembly = Assembly.GetAssembly(typeof(MenuGenerator)); var allControllers = currentAssembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).ToList(); var menuControllers = allControllers.Where(t => t.GetCustomAttribute<MenuItemAttribute>() != null || t.GetMethods().Any(m => m.GetCustomAttribute<MenuItemAttribute>() != null)) .ToList(); var submenuControllers = new List<Menu>(); menuControllers.ForEach(controller => { var navigation = controller.GetCustomAttribute<MenuItemAttribute>(); if (navigation == null) //navigation is set only against actions { controller.GetMethods().ToList().ForEach(method => { navigation = method.GetCustomAttribute<MenuItemAttribute>(); if (navigation == null) return; if (!UserHasAccess(method.GetCustomAttribute<AuthorizedRoleAttribute>())) return; Menu actionMenu = CreateAreaMenuItemFromAction(controller, method, navigation); menus.Add(actionMenu); }); return; } if (!UserHasAccess(controller.GetCustomAttribute<AuthorizedRoleAttribute>())) return; Menu menu = CreateAreaMenuItemFromController(controller, navigation); if (navigation.ParentController != null) { if (navigation.ParentController.IsSubclassOf(typeof(Controller))) { menu.ParentControllerFullName = navigation.ParentController.FullName; submenuControllers.Add(menu); } } menus.Add(menu); }); menus = menus.Except(submenuControllers).ToList(); submenuControllers.ForEach(sm => { var parentMenu = menus.FirstOrDefault(m => m.ControllerFullName == sm.ParentControllerFullName); parentMenu?.SubMenus.Add(new SubMenu() { Name = sm.Name, Url = sm.Url }); }); return menus.OrderBy(m => m.Order).ToList(); }
این متد از MenuController فراخوانی می شود که از ویوی Layout صدا زده شده است.
public class MenuController : Controller { // GET: Menu public PartialViewResult Index() { List<Menu> menus = MenuGenerator.CreateMenu(); return PartialView("Partials/_menu", menus); } }
کد منو و زیر منو :
public class Menu { public Menu() { SubMenus = new List<SubMenu>(); } public string Name { get; set; } public string CssIcon { get; set; } public string Url { get; set; } public List<SubMenu> SubMenus { get; set; } public string ParentControllerFullName { get; set; } public string ControllerFullName { get; set; } public int Order { get; set; } }
public class SubMenu { public string Name { get; set; } public string Url { get; set; } public string CssIcon { get; set; } public int Order { get; set; } }
ویوی منو
@using DynamicMvcMenu.Models @model List<DynamicMvcMenu.Models.Menu> <ul class="nav"> @foreach (Menu menu in Model) { <li class="dropdown"> @if (string.IsNullOrWhiteSpace(menu.Url)) { <a href="#" class="dropdown-toggle" id="dropdownCommonMenu" data-toggle="dropdown"> <span class="icon"> <i class="@menu.CssIcon" aria-hidden="true"></i> </span> @menu.Name </a> } else { <a href="@Url.Content(menu.Url)"> <span class="icon"> <i class="@menu.CssIcon" aria-hidden="true"></i> </span> @menu.Name </a> } @if (menu.SubMenus.Any()) { <a class="dropdown-toggle" data-toggle="dropdown" href="#"> <span class="caret"></span> </a> <ul class="dropdown-menu navmenu-nav" role="menu" aria-labelledby="dropdownCommonMenu"> @foreach (SubMenu subMenu in menu.SubMenus) { <li role="menuitem"> <a href="@Url.Content(subMenu.Url)"> <span class="icon"> <i class="@subMenu.CssIcon" aria-hidden="true"></i> </span> @subMenu.Name </a> </li> } </ul> } </li> } </ul>
نتیجه:
تنظیمات اضافه :
1. شما می توانید منوی دلخواه خود را(لینک یک HTML استاتیک یا یک سایت خارجی (از طریق افزودن به لیست منو از Menucontroller ، به منوی داینامیک اضافه کنید.
2. این یک نوع منوی دوسطحی است شما میتوانید بسته به نیازتان منوی سه سطحی هم ایجاد کنید.
بومی سازی:
شما میتوانید یک ویژگی جدید LanguageKey
روی کلاس
MenuItemAttribute
اضافه کنید و با تغییرات سازنده آن را طوری در نظر بگیرید که یک ویژگی اجباری شود.
public MenuItemAttribute(string LangKey) { IsClickable = true; LanguageKey = LangKey; }
خوب حالا کلاس های Menu
و SubMenu
نامشان را ازlangaugekey میگیرند که برای بازیابی متن بر اساس اولویت کاربران مورد استفاده قرار گیرد. شما می توانید از یک فایل منبع و یا دیتابیس (یا هر چیز دیگری) برای خواندن مقادیرKey استفاده کنید.
LanguageService
public class LanguageService { public string GetText(string LangKey) { //var userlang = get user preferene from cookie or database //read from resourse/database or wherever you want } }
و در تابع CreateMenu
، نام ویژگی هایی از منو و زیر منو با فراخوانی متد
GetText
تنظیم خواهد شد.
menu.Name = LangaugeService.GetText(attribute.LanguageKey);
امیدوارم ساخت منوی داینامیک برای شما سودمند باشد.
آموزش asp.net mvc
- ASP.net MVC
- 2k بازدید
- 7 تشکر