Reputation: 2505
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
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
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
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
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
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
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