Wasyster
Wasyster

Reputation: 2505

How do I specify different Layouts in the ASP.NET Core MVC

I would like to have 2 separate Layouts in my application. Let say one is for the Public section of the website and the other is empty for some reasons we need.

Before Core I could do this to define a filter:

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;
        }
    }

}

Now the ViewResult do not have the MasterName property. Is it possible to do now, and not to use in the View the layout definition.

Upvotes: 37

Views: 43874

Answers (6)

HorlaCode
HorlaCode

Reputation: 1

To have a Multiple Layout in asp.net core is quite easy, follow the steps below:

Step 1: Create the layout inside the "Shared Folder located Under Views" and give it a name with prefix "_" i.e _myLoginLayout.cshtml

Step 2: Specify your layout at the beginning of the page you want to apply it e.g @{ Layout = "~/Views/Shared/_myLoginLayout.cshtml"; ViewData["Title"] = "TestMe";//you can add the title as well... } So when _ViewStart.cshtml is loading Layout for your page..it will read the Layout you specify.

Upvotes: 0

Emy Ferreira
Emy Ferreira

Reputation: 851

In ASP.NET Core 2.0, I was inspired by the answer of @Daniel J.G.

I made a ViewLayoutAttribute:

[AttributeUsage(AttributeTargets.Class)]
public class ViewLayoutAttribute : Attribute
{
    public ViewLayoutAttribute(string layoutName)
    {
        this.LayoutName = layoutName;
    }

    public string LayoutName { get; }
}

Exemple of a controller class:

[ViewLayout("_Layout2")]
public class MyController : Controller 
{
// Code
}

And I created an extension to get this very attribute from the 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;
        }
    }

And in _ViewStart.cshtml:

@using MyApp.Extensions

@{
    Layout = ViewContext.GetLayoutAttribute()?.LayoutName ?? "_Layout";
}

Upvotes: 4

Kappacake
Kappacake

Reputation: 1937

If you want to have a different layout based on some condition, you could use this code in the _ViewStart.cshtml file:

@{
    if (some condition)
    {
        Layout = "_Layout1";
    }
    else if (some other condition)
    {
        Layout = "_Layout2";
    }
    etc.
}

I use this in one of my projects. In my case the condition is User.IsInRole("admin") and my _ViewStart.cshtml file is as follows:

@{
    if (User.IsInRole("admin"))
    {
        Layout = "_AdminLayout";
    }
    else
    {
        Layout = "_Layout";
    }
}

Since there are only two roles in my project, which result in only one condition, this workaround is not too bad in my case. I hope someone with in a similar situation will find this useful :)

Upvotes: 14

Daniel J.G.
Daniel J.G.

Reputation: 34992

You can still do something very similar to your original approach, using ViewData to pass around the layout name (Although I would create it as a Result Filter):

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;
        }
    }        
}

Then in the _ViewStart.cshtml file:

@{
    Layout = (string)ViewData["Layout"] ?? "_Layout";
}

Finally, assuming you create a new layout like Views/Shared/_CleanLayout.cshtml, you can use that attribute on any controller or action:

[ViewLayout("_CleanLayout")]
public IActionResult About()
{
    //...
}

Upvotes: 50

Sanket
Sanket

Reputation: 19997

This is how I am using multiple layouts in my ASP.NET core MVC application.

You can try like this-

In _ViewStart.cshtml specify default _Layout like this-

@{
    Layout = "_Layout";
}

If you want to set page specific layout then in that page.cshtml, you can assign other view like this-

@{
    Layout = "~/Views/Shared/_Layout_2.cshtml";
    ViewData["Title"] = "Page Title";
}

See if this helps.

Upvotes: 41

XamDev
XamDev

Reputation: 3647

You can use IViewLocationExpander interface to render view as you want. Please refer this link Working with IViewLocationExpander in mvc for more details

Regards,

Rohit

Upvotes: 0

Related Questions