In this recipe I will introduce you to Razor layout pages, and then we can move forward to cover every single Razor syntax existing in a layout page.
In a web application, it is very important to have content that is displayed on every web page. It may be a header, footer, menus, and so on. It saves a lot of work and centralizes the markup, style, and code which are easier to maintain.
In ASP.NET 2.0, the master page concept was introduced to help bring a content block from another file on a web page to DRY (Don't Repeat Yourself) and to give the web application a consistent look.
In ASP.NET MVC, Razor supports the concept of a layout page, which is similar to a master page. A layout page allows us to define a common website template. Let's learn more about the layout page.
In the Creating the project (Should know) recipe, we created a project using the Internet Application template and the Razor view engine. Open that project and read on reading further.
Before learning Razor layout pages, you should be aware of the _ViewStart.cshtml
page. So, first we will look at the _ViewStart.cshtml
page and then we will look at the layout page.
With the release of ASP.NET MVC 3 Beta, we can now add a file called
_ViewStart.cshtml
(if the programming language is C#) or_ViewStart.vbhtml
(if the programming language is VB) underneath the\Views
folder of the ASP.NET MVC projects, which contains the information about the layout page.This
_ViewStart.cshtml
file can be used to define a common view code that you need to execute at the start of each view; we no longer need to explicitly set the layout in any of our individual view files except if we wanted to override the default value. If you open this file, you will see the following code snippet:@{ Layout = "~/Views/Shared/_Layout.cshtml"; }
I could write
Layout = "~/Views/Shared/_Layout.cshtml";
on each view page in the project, but it is not DRY.What DRY does here is it writes
Layout = "~/Views/Shared/_Layout.cshtml";
in the_ViewStart.cshtml
file. Now, open the_ViewStart.cshtml
file; the layout filename which is_Layout.cshtml
by default could be anything else.We could write the code within the
_ViewStart.cshtml
file to programmatically set theLayout
property for all views. Depending on the type of device that is accessing the site we can have a phone or tablet to optimize the layout for those devices, and a desktop to optimize the layout for PCs/laptops.Also, what if one wants to apply a new layout to all view pages inside the
Demo
folder? To do this, we could put a_ViewStart.cshtml
file inside the/Views/Demo
folder, which would override the default one in the/Views
folder and specify the desired layout as shown in the following screenshot:If we write
Layout = null;
in the preceding code, it will stop applying any layout to all view pages inside the\Views\Demo
folder and you will see a layout-free web page. In the case where you don't want any layout, this will work for you.When returning the view inside the controller, we could also specify which layout should be used, the following is an example:
return View("Index", "~/Views/Shared/_LayoutName.cshtml", someViewModel);
That's all we need to cover about the _ViewStart.cshtml
page.
Moving ahead, open the _Layout.cshtml
file, which can be found in the /Views/Shared
folder, and have a look at the code; it is shown in the following screenshot:
If you recall, I created this project using the Internet Application template in the Creating the project (Should know) recipe. This is why we have a ready-to-use layout which has <head>
and <body>
sections. In the <head>
section, we have the title, links, meta, styles, and scripts, and in the <body>
section we have the site title, menu, login, and so on, which makes a common layout that we can be used to maintain a consistent look and feel across any number of pages on our site.
Don't get confused with the amount of code in the preceding screenshot, they are pretty simple to understand. I'll discuss what each piece of code means, one by one, in a bit. Some of them will be discussed in the next recipe.
RenderBody()
, which is a Razor syntax, is equivalent to ContentPlaceHolder
, which is an ASPX syntax. In this code, we are calling the RenderBody()
method within the layout file to indicate where we want the views based on this layout to "fill in" their core content at that location in the HTML. The following screenshot gives us an explanation:
Here, View Page 1 is just like the About.cshtml
view page, and View Page 2 is like the Contact.cshtml
view page, and so on. In the About.cshtml
or Contact.cshtml
view page, we do not need to wrap our main body content within a tag or element; by default Razor will automatically treat the content as the body section of the layout page. We can optionally define named sections if our layout has multiple replaceable regions; you will learn it in this recipe.
ViewBag.Title
is used to output the View.Title
property within the <title>
element of our <head>
section, shown in the following screenshot:
To understand this, open any view page; you will notice the ViewBag.Title
property is assigned with a value, in this case we have Index
, and now our <title> will become Index - My ASP.NET MVC Application
:
ViewBag
is a very well-known way to pass the data from controller to view and even from view to view. ViewBag
uses the dynamic feature that was added in C# 4.0. In the preceding screenshot, we are programmatically setting the ViewBag.Title
value within our Index.cshtml
page. The code within our Index.cshtml
file will run before the _Layout.cshtml
code runs and so we can write the view code that programmatically sets values we want to pass to our layout to render. This is particularly useful for things such as setting the page's title, as well as <meta>
elements within the <head>
for SEO.
The System.Web.Optimization
namespace has two awesome helpers, @Styles.Render
and @Scripts.Render
, which help us to perform Bundling and Minification. Minimizing the number of requests the page has to perform can have a considerable effect on our site's performance. Bundling and Minification are very common scenarios that help to reduce the number of requests and file size by bundling (that is, all CSS and JS files into two separate files) and minifying (that is, removing blank spaces that are not required, removing comments, and reducing identifiers).
Bundling and Minification is performed at runtime, so that the process can identify the user agent (for example IE, Mozilla, and so on), and thus improve the compression by targeting the user browser (for instance, removing stuff that is Mozilla-specific when the request comes from IE).
The code in the preceding screenshot actually calls the files included in that particular bundle, which is declared inside the BundleConfig
class in the App_Start
folder. Open this file, it should be as follows:
In that particular case the call to @Styles.Render("~/Content/css")
is made to call "~/Content/site.css"
.
As we know, layouts in Razor serve the same purpose as master pages do in Web Forms. They allow you to specify a layout for your site and create a placeholder for your views to implement. Open the _Layout.cshtml
file, you will see a section called featured
, which is an optional section just above @RenderBody()
, highlighted in the following screenshot:
Now, on any view page, if I want a featured
section to appear, I could write following:
Using Razor, we can also check the existence of a section on a view page (Index.cshtml
) from the layout (_Layout.cshtml
) page, and depending upon its existence, we can display the alternative information.
In the following example, using the IsSectionDefined()
method, we can check if the section has been defined on the view page or not. If it is not defined, the alternative information will be rendered.
On the Index.cshtml
view page, I would not like the featured
section to appear, so I'll not write it and Razor will display Alternative Information, as shown in the following screenshot:
Razor layout pages are equivalent to master pages in ASP.NET Web Forms. Just as it is possible to nest master pages, it is also possible to nest Razor layout pages.
To tryout an example of a nested layout page, create a new project with the Empty Project template. Please don't mix it in the project that we already have in place.
Consider a web application that has header and footer sections. The footer section will be visible to all but I would the like header section to be invisible to the admins. I would like to define a nested layout to enable this feature.
Create a file in
Views/Shared/_Layout.cshtml
and write the following code in it. This is a top-level layout but looks like a regular Razor layout.<!DOCTYPE html> <html lang="en"> <head> <title>@ViewBag.Title</title> </head> <body> @RenderSection("header", required: false) @RenderBody() @RenderSection("footer", required: false) </body> </html>
Now, let's create a public-specific layout file in
Views/Shared/_PublicLayout.cshtml
and write the following code:@{ Layout = "~/Views/Shared/_Layout.cshtml"; } <p>Hello from Public layout.</p> @section header{ <p> header information goes here. </p> } @RenderBody() @section footer{ <p> footer information goes here. </p> }
In this code, first I defined which layout page this layout will inherit, and then I defined both the header and footer sections, including a
RenderBody()
call.Now, let's create an admin-specific layout file in
Views/Shared/_AdminLayout.cshtml
and write the following code:@{ Layout = "~/Views/Shared/_Layout.cshtml"; } <p>Hello from Admin layout.</p> @RenderBody() @section footer{ <p> footer information goes here. </p> }
In this code, first I defined which layout page this layout will inherit and then I defined only the footer section and a
RenderBody()
call.
We are all set to test nested layouts. To test the public layout, add a view as given in the following screenshot and look at the output. Remember to point to the _PublicLayout.cshtml
layout when adding any public view.
To test the admin layout, add a view as given in the following screenshot and look at the output:
You must have noticed that we can't see the header information goes here message in the admin layout. I hope you get the idea of using nested layouts.
Razor is flexible enough so that we can make changes like the ones we saw in this recipe, without having to modify any of our view templates (nor make any controller logic changes) to accommodate this. We can instead make minimal modifications to our layout file and the rest happens cleanly. This type of flexibility makes Razor incredibly powerful and productive.