Можно ли указать форматы местоположения для поиска для макета MVC Razor?
В Razor при загрузке частичного представления можно просто указать имя частичного представления, а механизм просмотра Razor будет искать RazorViewEngine.PartialViewLocationFormats:
@Html.RenderPartial("_PartialView", Model);
будет фактически искать местоположения, указанные в PartialViewLocationFormats в механизме просмотра, например,
~/Views/Home/_PartialView.cshtml
~/Views/Shared/_PartialView.cshtml
Однако при указании макета мне кажется, что я вынужден указать конкретный путь к макету:
@Layout = "~/Views/Shared/MyLayout.cshtml";
То, что я хотел бы сделать, это указать макет только по имени и найти фактический макет, выполнив поиск списка общих местоположений:
@Layout = "MyLayout";
... но я не могу найти никаких возможностей для этого. Поскольку я не мог найти никакой документации относительно этого, я попытался играть с настройкой RazorViewEngine.MasterLocationFormats, но это свойство не используется при поиске макетов.
Кто-нибудь знает, как это сделать?
Ответы
Ответ 1
Недавно я боролся с аналогичной проблемой, когда в тематическом приложении, которое использует настраиваемый ViewEngine для поиска местоположения темы сначала для представлений, я пытался переопределить некоторые основные файлы. Один из способов заставить местоположение макета пройти через ViewEngine FindView - указать имя главного представления в действии контроллера при возврате:
return View("myView", "myViewMaster");
Однако, если "myViewMaster" также имеет макет (вложенные макеты), этот метод не работает для вложенного макета. На мой взгляд, самое лучшее решение - вызвать механизм просмотра непосредственно в представлении:
@{
Layout = (ViewEngines.Engines.FindView(this.ViewContext.Controller.ControllerContext, "myViewMaster", "").View as RazorView).ViewPath;
}
Это работает, но, безусловно, может быть инкапсулировано в метод расширения, чтобы избежать повторения и абстрактного кода.
Надеюсь, что это поможет!
Ответ 2
Я пробовал пару разных вещей и не мог найти способ делать то, что вы хотели. Однако вы можете просто оставить макет пустым и использовать файл _ViewStart.cshtml для назначения макета для каждой страницы с использованием свойства ViewBag.
Итак, если у вас есть перечисление (например), и вы устанавливаете свойство dynamicviewbag CustomLayout на значение этого перечисления, вы можете использовать файл _ViewStart, чтобы сделать что-то вроде этого.
@{
MvcApplication12.Controllers.CustomLayouts customLayout = this.ViewContext.Controller.ViewBag.CustomLayout;
switch (customLayout)
{
case MvcApplication12.Controllers.CustomLayouts.Blue: Layout = "~/Views/Shared/Blue.cshtml"; break;
case MvcApplication12.Controllers.CustomLayouts.Green: Layout = "~/Views/Shared/Green.cshtml"; break;
case MvcApplication12.Controllers.CustomLayouts.Default:
default: Layout = "~/Views/Shared/Default.cshtml"; break;
}
}
Не уверен, помогает ли это.
Ответ 3
Я принял принятый ответ от @sowee15 и добавил метод расширения, чтобы немного его обернуть:
using System;
using System.Web.Mvc;
namespace MyApp.Classes
{
public static class ViewEngineCollectionExtensions
{
public static string FindViewPath(this ViewEngineCollection viewEngines, ControllerContext controllerContext, string viewName, string masterName = null)
{
var viewResult = ViewEngines.Engines.FindView(controllerContext, viewName, masterName ?? string.Empty);
if(viewResult == null)
throw new Exception(string.Format("The specified view {0} could not be found.", viewName));
var view = viewResult.View as RazorView;
if(viewResult == null)
throw new Exception(string.Format("The specified view {0} must be a Razor view.", viewName));
return view.ViewPath;
}
}
}
Затем вы можете вызывать расширение из своего представления или viewstart.cshtml следующим образом:
@{
Layout = ViewEngines.Engines.FindViewPath(ViewContext.Controller.ControllerContext, "_PopupLayout");
}