Как указать различные макеты в ASP.NET Core MVC
Я хотел бы иметь 2 отдельных макетов в моем приложении. Допустим, один для публичного раздела сайта, а другой пуст по тем причинам, которые нам нужны.
До Core я мог сделать это, чтобы определить фильтр:
public class LayoutInjecterAttribute : ActionFilterAttribute
{
private readonly string _masterName;
public LayoutInjecterAttribute(string masterName)
{
_masterName = masterName;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = _masterName;
}
}
}
Теперь ViewResult не имеет свойства MasterName. Можно ли это сделать сейчас, а не использовать в просмотре определение макета.
Ответы
Ответ 1
Вы все равно можете сделать что-то очень похожее на ваш оригинальный подход, используя ViewData
, чтобы передать имя макета (хотя я бы создал его как Фильтр результатов):
public class ViewLayoutAttribute : ResultFilterAttribute
{
private string layout;
public ViewLayoutAttribute(string layout)
{
this.layout = layout;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
var viewResult = context.Result as ViewResult;
if (viewResult != null)
{
viewResult.ViewData["Layout"] = this.layout;
}
}
}
Затем в файле _ViewStart.cshtml
:
@{
Layout = (string)ViewData["Layout"] ?? "_Layout";
}
Наконец, если вы создадите новый макет, например Views/Shared/_CleanLayout.cshtml
, вы можете использовать этот атрибут для любого контроллера или действия:
[ViewLayout("_CleanLayout")]
public IActionResult About()
{
//...
}
Ответ 2
Вот как я использую несколько макетов в моем основном MVC-приложении ASP.NET.
Вы можете попробовать это -
В _ViewStart.cshtml
укажите значение по умолчанию _Layout как это -
@{
Layout = "_Layout";
}
Если вы хотите установить макет страницы, а затем в page.cshtml
, вы можете назначить другое представление, подобное этому -
@{
Layout = "~/Views/Shared/_Layout_2.cshtml";
ViewData["Title"] = "Page Title";
}
Посмотрите, поможет ли это.
Ответ 3
Если вы хотите иметь другой макет, основанный на некоторых условиях, вы можете использовать этот код в файле _ViewStart.cshtml
:
@{
if (some condition)
{
Layout = "_Layout1";
}
else if (some other condition)
{
Layout = "_Layout2";
}
etc.
}
Я использую это в одном из моих проектов. В моем случае условие User.IsInRole("admin")
и мой _ViewStart.cshtml
файл выглядит следующим образом:
@{
if (User.IsInRole("admin"))
{
Layout = "_AdminLayout";
}
else
{
Layout = "_Layout";
}
}
Поскольку в моем проекте есть только две роли, которые приводят только к одному условию, это обходное решение не так уж плохо в моем случае. Надеюсь, что кто-то в подобной ситуации найдет это полезным:)
Ответ 4
В ASP.NET Core 2.0 меня вдохновил ответ @Daniel JG
Я сделал ViewLayoutAttribute
:
[AttributeUsage(AttributeTargets.Class)]
public class ViewLayoutAttribute : Attribute
{
public ViewLayoutAttribute(string layoutName)
{
this.LayoutName = layoutName;
}
public string LayoutName { get; }
}
Пример класса контроллера:
[ViewLayout("_Layout2")]
public class MyController : Controller
{
// Code
}
И я создал расширение, чтобы получить этот атрибут из ViewContext
:
public static class RazorExtensions
{
/// <summary>
/// Gets the <see cref="ViewLayoutAttribute"/> from the current calling controller of the
/// <see cref="ViewContext"/>.
/// </summary>
public static ViewLayoutAttribute GetLayoutAttribute(this ViewContext viewContext)
{
// See if Razor Page...
if (viewContext.ActionDescriptor is CompiledPageActionDescriptor actionDescriptor)
{
// We want the attribute no matter what.
return Attribute.GetCustomAttribute(actionDescriptor.ModelTypeInfo, typeof(ViewLayoutAttribute)) as ViewLayoutAttribute;
}
// See if MVC Controller...
// Property ControllerTypeInfo can be seen on runtime.
var controllerType = (Type)viewContext.ActionDescriptor
.GetType()
.GetProperty("ControllerTypeInfo")?
.GetValue(viewContext.ActionDescriptor);
if (controllerType != null && controllerType.IsSubclassOf(typeof(Microsoft.AspNetCore.Mvc.Controller)))
{
return Attribute.GetCustomAttribute(controllerType, typeof(ViewLayoutAttribute)) as ViewLayoutAttribute;
}
// Nothing found.
return null;
}
}
И в _ViewStart.cshtml
:
@using MyApp.Extensions
@{
Layout = ViewContext.GetLayoutAttribute()?.LayoutName ?? "_Layout";
}
Ответ 5
Вы можете использовать интерфейс IViewLocationExpander
для рендеринга вида по своему усмотрению.
Для получения дополнительной информации см. Ссылку Работа с IViewLocationExpander в mvc
Привет,
Рохит