foop
foop

Reputation: 523

MVC Areas & Shared Layouts

Hopefully I am making myself clear. I have an area in my mvc project with two shared layouts _Application.cshtml (For Areas) _Layout.cshtml (For non-areas)

Reports and Favourites are for the area controller.
Roles and Users are for the default namespace controllers.

<li>@Html.ActionLink("Reports", "Reports", "Home")</li>
<li>@Html.ActionLink("Favourites", "Favourites", "Home")</li>
<li>@Html.ActionLink("System Roles", "Roles", "Support",new { Area = "" },  htmlAttributes: new { title = "System Roles" }) </li>
<li>@Html.ActionLink("Users", "Users", "Support", new { Area = "" },   htmlAttributes: new { title = "Users" })</li>

Defined in the Roles and Users View I am defining a different shared layout. That do not have links to Reports and Favourites (just totally different navigation menu).

Is it possible to assign the layout of Roles and Users to my _Application shared layout without losing the ability to call the actions from the default namespace controller?

if (this.ViewContext.RouteData.DataTokens["Area"] != null)    {
    Layout = "~/Views/Shared/_Application.cshtml";
}
else
{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

I've tried this but the DataToken never receives an Area route because it's not defined.

Edit: Sorry I am having trouble describing the issue (even with co-workers too...)

Area "Procurement": Controller : "Home" has three views Index, Reports and Favourites. Navigation Menu (_Applicaiton.cshtml) is custom to the Procurement Area only

Non Area : Controller "Support" has two view Roles and Users. Navigation Menu (_Layout.cshtml) is generic that contains links to navigate to other Areas of the project

The Action Links above reside in the Index View. If I click Reports or Favourites, it will navigate me to ApplicationName/Procurement/Home/Reports or ApplicationName/Procurement/Home/Favourites

If I click Roles or Users, it will navigate me to ApplicationName/Support/Roles or ApplicationName/SupportUsers

Because of how I have defined the layouts for Roles and Users, the navigation menu defaults back to the generic one instead of displaying the one specific to Procurement Area.

I am looking to see if it is possible that when I click Roles or Users for the navigation menu to stay on the Procurement Layout and reference the same Support controller. I don't want to have to create the Roles and Users page for each Area that I am creating.

Upvotes: 1

Views: 9277

Answers (3)

SandRock
SandRock

Reputation: 5563

If you want to drive conditionally which view is used, you can inherit from your view engine(s) and override the view locations on a per-request basis.

This post illustrates a different static view resolution pattern.

You can override the ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) method to customize the available views for a given request. The controllerContext object will allow you to find the user and its roles so you can return the correct views.

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
    if (/* user has role X */)
    {
        return new ViewEngineResult(new string[] { "Some View.cshtml" });
    }

    return base.FindView(controllerContext, viewName, masterName, useCache);
}

This article show how to inherit from the classic view engines and declare them. This article shows how to use the request object in the custom ViewEngine to select views depending on the user's roles.

Upvotes: 0

SandRock
SandRock

Reputation: 5563

If you desire to have specific layouts for some areas, there is a simple solution.

Use the _ViewStart file to specify the layout to use for each area.

~/Views/_ViewStart.cshtml file:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

~/Areas/Xxx/Views/_ViewStart.cshtml file:

@{
    Layout = "~/Views/Shared/_LayoutXxx.cshtml";
}

You may read ASP.NET MVC 3: Layouts with Razor

Don't forget to remove the Layout = "..." declarations from the views in order to let the ViewStart do its work.

Upvotes: 2

Chris Pratt
Chris Pratt

Reputation: 239210

Traditionally, you'd just use ViewContext.RouteData.Values["key"], rather than DataTokens. Not sure that makes a difference or not. Also, I think the key is "area", not "Area". Dictionary keys are case-sensitive, so that may very well be your issue.

EDIT

Okay. I think I might understand a little better now. To achieve what you want, you would need to re-implement the Roles and Users functionality in your Procurement area. However, that doesn't mean you have to just copy the code over. You can rely on the view conventions to load in the right views. For example, if you move your Roles view into Views\Shared, Razor can find it when searching for that view in different controllers or areas, since Views\Shared is always the last-result path searched. As for the actions themselves, you can utilize controller inheritance to implement the same controller/actions in one or more different areas, while not actually repeating code.

Upvotes: 0

Related Questions