Virtual Developer Workshop: Containerized Development with Docker
Creating one page (Default.aspx) that is aesthetically pleasing, easy to use by your visitors, takes various browsers into consideration, uses CSS, is one thing. Copying this code to every page in your site would be, to say the least, a maintenance nightmare. Can you imagine having to update thousands of pages because the client wants a fairly minor design tweak? What you really want is a way to create a reusable template that can be incorporated into every page of your site. This will allow for all pages to look the same throughout your project while allowing for easier maintenance as your projects mature. In a year or so when you want to redo the entire site, you would only need to change the template rather than the coding on every page of your application. With Master Pages, you can do exactly that.
A Master Page is simply a single page that holds the structure of your Web site. The files are designated with a .master file extension and are imported into content pages through the MasterPageFile property of the @Page directive of the content pages. They are meant to provide the template that all of your pages will use throughout the site. They are not really meant to hold the content of an individual page or, even, the stylistic definitions of the page. They are meant to provide a blueprint of what your site should look like and then connect that template to style rules set in detached CSS files (as appropriate).
Enough Talk; Time to Code
This article will go through the ins and outs of Master Pages. You should start a new project to practice these new concepts. So, with a new Web project open in Visual Studio 2005, click on Website and then choose "Add New Item" to get the screen depicted in Figure 1.
You will want to select "Master Page", ironically enough, as the template you want to use for your new item. You can name the page whatever you want but should leave the .master file extension. You should choose the language you code in, although this isn't particularly relevant unless you want to do any "under the hood" coding in the Master Page itself. Again, while this is possible and pertinent in some situations, for the most part, this is not necessarily relevant for most projects. It should also be noted that the language of the Master Page file will not dictate the language of the content page that inherits it. This just means that you can have a Master Page file that has a language declaration of, say, VB and a content page that inherits it that has a language of C# (or the other way around). To expand this thought, you might have a designer who is responsible for creating the look and feel of your projects and is more comfortable with VB.NET. She could easily create the Master Page in VB without any worry about the coding preferences of the rest of the team. Once the VB Master Page is completed, the developers in the group could code against this VB Master Page in their language of choice (VB or C#). Even if there are public properties or methods in the Master Page that are written in VB, C# content pages can access them. This language independence of Master Pages is one of its nicest features.
However, for this example, just choose whatever language you are more comfortable with and press the "Add" button. This will create a file in your project called "MasterPage.master" that should resemble the following:
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </div> </form> </body> </html>
There are a couple of noteworthy elements to the default code provided. The first is the @Page declaration or, in this case, the lack thereof. The declaration statement for a Master Page is the @Master declaration. Other than that, though, the declaration looks pretty much identical to the @Page declaration you are probably used to seeing in other pages you may have created previously. There are Language, AutoEventWireup, CodeFile, and Inherits properties and they are set to intuitive values. In fact, if you compared the @Master declaration to a standard @Page declaration, you wouldn't see any difference except the name of the declaration and the names of the files that are used in the values of the properties just mentioned. They are, in this regard, identical. If you delve into all of the properties available to each, you will see a lot of differences. However, for now, it is sufficient to know that they have some shared properties and that, by default, Visual Studio creates new files with either a @Master or @Page declaration with the same properties in the initial declaration.
The other important thing to notice is the following code in the middle of the file:
<asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder>
This creates the region in the template that will be filled with material from the content pages created by the programmers. Each content page will reference the ID of this placeholder in its code and stick all of the content from that page in this particular region.
It is worth noting that you are not limited to a single placeholder in your Master Page. You can, in reality, set up as many placeholders as you need for your page. However, in practice, you should probably limit the number used to the number you actually need. If you have, for example, one hundred placeholders in your Master Page, you are probably not centralizing the content enough and, more importantly, are creating a maintenance headache. One placeholder is the default and around two or three are fairly common to see in practice. If you are using much more than that, you should really evaluate how you are using the Master Page and whether there is a simpler way to handle the content.
However, to see how these placeholders might be used in a project, examine the following code:
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body bgcolor="navy"> <form id="form1" runat="server"> <div> <table border="0" width="700"> <tr> <td colspan="2" height="150" bgcolor="gray" valign="middle" align="center"> HEADER </td> </tr> <tr> <td width="150" bgcolor="silver">SIDEBAR</td> <td width="550" height="400" bgcolor="white"> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </td> </tr> <tr> <td colspan="2" align="center" height="20" bgcolor="gray"> FOOTER </td> </tr> </table> </div> </form> </body> </html>
Essentially, you have created a fairly typical layout with a header, sidebar, content, and footer region. For simplicity, this example just uses tables to create the layout. If you were to look in the Design tab in Visual Studio 2005, you should see something that resembles Figure 2.
You can see that you have created a header and footer region on the top and bottom of the page respectively and then created a division for a sidebar and another one for your content. Within your content region, you have the content placeholder control. If you sent this Master Page out to all of your programmers, they could simply reference it and then fill in the content placeholder with their own content for every page they develop.
If you wanted to create separate content placeholders for the sidebar and content areas, you could simply modify the sidebar section of your code to look similar to the following:
<tr> <td width="150" bgcolor="silver"> <!-- SIDEBAR REGION --> <asp:contentplaceholder id="ContentPlaceHolder2" runat="server"> </asp:contentplaceholder> </td> <td width="550" height="400" bgcolor="white"> <!-- CONTENT REGION --> <asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </td> </tr>
This will create a content placeholder called ContentPlaceHolder1 in the content area that will be in the main content area of your page and then a separate content placeholder called ContentPlaceHolder2 that will reside in the sidebar region. The programmer will then have access to both of these regions to put in the appropriate content based on the page he is developing.
You can also, at the Master Page level, set the default content that will go in each of these placeholders. If, for example, you wanted the sidebar to say "this is the sidebar" in the absence of any code on the content page setting the content for this area, you would do this simply by inserting the content between the opening and closing tags of the content placeholder control, as such:
<asp:contentplaceholder id="ContentPlaceHolder2" runat="server"> this is the sidebar </asp:contentplaceholder>
When coding the content page that references this master page, the developer has the option of setting the content for this placeholder and, if he does, that is what will show up in the final rendered page. If, however, he decides not to set it on the content page for whatever reason, the default content from the Master Page will show up instead.
Most developers might think of content placeholders as just a place to store content displayed to the end user in the final rendered page. However, this isn't the only way they can be used. For example, if you place a content placeholder within the <head> region of your page, you can give access to that region at the Child Page level. This means that the Child Page can actually insert links to CSS documents that would be set in the <head> region of the rendered page. So, maybe the Master Page includes a blank content placeholder in the <head> region just below the linked stylesheets. By default, nothing would be added to that section. However, if you needed to, you could fill that content placeholder with a custom CSS document only relevant for that page. This allows for much more flexibility in the design of content pages as your project matures.
For now, however, your Master Page should resemble Figure 3 in the Design View.
Even though you can see the look of the page in the Design tab of Visual Studio, you should not get the wrong impression that you, then, just launch this page in a browser and it will work. That just isn't the case. Master Pages are not meant to be rendered on their own; they are only appropriate to be included as a reference to a valid .NET page. So if, for example, you try to view this Master Page in a browser, you would see an error similar to the one shown in Figure 4.
Now that you have a Master Page that you are comfortable with, it is time to implement the template in a content page. There are several ways to do this. The first way is to simply add a new item to your project (Website -> Add New Item), which will give you the screen shown in Figure 5.
As shown in Figure 5, you will want to select "Web Form" for your template, as you have probably done in previous projects. However, you want to make sure that you check the option "Select Master page" near the bottom of the screen. Doing so will do two things: it will prompt you to select which Master Page you want to use and will also set up your ASPX page to use that particular Master. The former will be done through a screen similar to Figure 6.
This screen shows the Master Pages you have available to you. Essentially, it is a directory browser that only allows you to see Master Pages. In this example, there is only one option: MasterPage.master. Select the Master Page you want to use and press the OK button. This will generate an ASPX file that is set up to use this Master with code that resembles the following:
lt;%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder2" Runat="Server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> </asp:Content>
The first thing you will notice when looking at this file in comparison to an ASPX page that does not use a Master Page is in the @Page declaration. There is a new property here called "MasterPageFile" that references your new Master Page, "MasterPage.master". You will see that the Master Page property value starts with a tilde ("~"). This simply means that the Web application will look for the file in the root of the Web application or virtual folder. This means that, if you have a Web page with this reference sitting in a subdirectory, the Web application will look for the Master Page in the root of the Web application when it is attempting to render out the final page. Removing the tilde will force the application to look for the Master Page in whatever folder the ASPX resides. In many projects this is not that big of a deal, especially if you have all of your .NET files sitting in the root folder. However, it is important to know what the tilde is there for and how it affects your rendering.