Как указать различные макеты в 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

Привет,

Рохит