Razor: displaying sections in master pages of master pages
Description of the problem
Let the application have a couple of views: View.cshtml and ViewWithSide.cshtml, and there are also two master pages: Layout.cshtml and LayoutWithSide.cshtml, the first being the master page for the second. As you can guess from the file names, XxxWithSide.cshtml adds a sidebar to the page, the output format of which is defined in the master page, and the insides in the view. In the main Layout master page, in addition to the main layout, the output of the “navigation” section is defined, which is set in the views.
And so, when the “navigation” section is defined in the ViewWithSide code, but not in the LayoutWithSide, because this section should be processed by the “next” master page (Layout), an error will be generated when trying to open the ViewWithSide in the application:The following sections have been defined but have not been rendered for the layout page "~ / Views / Shared / LayoutWithSide.cshtml": "navigation" (The "navigation" section is defined, but is not displayed anywhere on the master page) .
The idea of solving this problem is quite simple: you need to transfer the output of this section to the “next” master page, and there let them understand.
Some code
Layout.cshtml
@ViewBag.Title
@if (IsSectionDefined("navigation")) { }
@RenderBody()
LayoutWithSide.cshtml
@{
Layout = "~/Views/Shared/Layout.cshtml";
}
@RenderSection("side", required:false)@RenderBody()View.cshtml
@{
ViewBag.Title = "view";
Layout = "~/Views/Shared/Layout.cshtml";
}
@section navigation {
page1 |
page2
}
viewWithoutSide
Main contentViewWithSide.cshtml
@{
ViewBag.Title = "viewWithSide";
Layout = "~/Views/Shared/LayoutWithSide.cshtml";
}
@section navigation {
page1 |
page2
}
@section side {
side content
}
viewWithSide
Main contentNaturally, I expected that Razor and ASP.NET MVC will figure it out and display the section in the master page where it is needed. But alas and ah ... However, there is a problem, there is an idea how to solve - it is necessary to solve.
Search for a solution
In my case, the “navigation” section is not just optional, but if it is not defined, then some more piece of markup is not displayed. For this reason, simply entering the section of the same name in LayoutWithSide and displaying the contents transferred from the view into it would not have worked.
I tried to wrap the section declaration inside
if (IsSectionDefined("navigation")), maybe ... I didn’t have any particular hopes for this method - it didn’t work (the analyzer just does not expect such a design and swears “Parser Error” on it). A quick and superficial search on MSDN and the Internet did not yield anything useful. But in the methods available inside the view, the DefineSection method (string name, SectionWriter action) was immediately noticed
Since it was not possible to wrap the section declaration in the Razor style in the if declaration, you can try to wrap the section creation from C # code. It turned out like this:
if (IsSectionDefined("navigation"))
{
DefineSection("navigation", delegate() { Write(RenderSection("navigation")); });
}
And this code successfully worked out and completed the task.
Decision
Of course, I didn’t stop there, not to write
We have at our disposal
public static class WebPageHelpers
{
public static void PropagateSection(this WebPageBase page, string sectionName)
{
if (page.IsSectionDefined(sectionName))
{
page.DefineSection(sectionName, delegate() { page.Write(page.RenderSection(sectionName)); });
}
}
public static void PropagateSections(this WebPageBase page, params string[] sections)
{
foreach (var s in sections)
PropagateSection(page, s);
}
}
And after it is connected to the project, it is enough to call the method, passing it the names of the necessary sections. Then LayoutWithSide.cshtml will look like this:
@{
Layout = "~/Views/Shared/Layout.cshtml";
this.PropagateSection("navigation");
}
@RenderSection("side", required:false)@RenderBody()And if you need to transfer several sections to the master page, you can call
this.PropagateSections("section1", "section2", "section3"), well, you understand ...