In-Depth
Create Master Pages in ASP.NET 2.0
ASP.NET 2.0's master pages can help you develop consistent Web apps without the additional overhead of include files, user controls, or third-party add-ins.
Technology Toolbox: VB.NET, ASP.NET
The implementation of master pages is a highly anticipated feature in ASP.NET 2.0. A master page is a template that you can use to define the overall look and feel of your ASP.NET Web applications. In previous versions of ASP.NET, you were forced to embed user controls in every page to get consistency. Master pages now make it much easier to deliver a consistent look and feel, without the need to embed user controls across all of your pages.
I'll discuss the basics of ASP.NET 2.0 master pages, how to create and use them, and how ASP.NET uses them to render a final Web page, complete with postback events. The code for this article is based on VS.NET 2005 beta 1, and as with all beta versions, there is the possibility that some features shown in this article might change before the final release of VS.NET 2005.
A master page is basically a template where you can define the style and functionality of your Web application. You define a master page that contains how you want things to look and act, and then you create what are called content pages (that refer to the master page) for all of the pages in your Web application.
Create a master page by opening up a VS.NET 2005 Web project and selecting Add | New Item from the menu (see Figure 1). It's probably a good idea to place your master pages in a subdirectory within your Web application in order to improve the manageability of the Web project (I used the MasterPages folder in the source code example for this article). Notice that the default master page contains a new control called <asp:contentplaceholder> between an HTML <DIV> tag (see Listing 1). You use the <asp:contentplaceholder> to define the area that content pages will place their content in. Outside the <asp:contentplaceholder> is where you define things such as headers, footers, menus, and styles that you want to implement consistently across your Web application. Adding your HTML (or ASP.NET controls) outside of the <asp:contentplaceholder> is simply a matter of formatting to taste, so I won't waste article space with it here. Suffice it to say that you have the wealth of HTML, CSS, and ASP.NET user and server controls at your disposal. A master page can also have multiple <asp:contentplaceholder> controls.
After creating the master page, notice that it looks like any other page, but it uses a new directive called <%@ Master %>:
<%@ Master Language="VB"
CompileWith="MasterPage.master.vb"
AutoEventWireup="false"
ClassName="MasterPage_master" %>
You can also place default content within the ContentPlaceHolder:
<asp:contentplaceholder
id="BasicContentPlaceHolder"
runat="server">
No default content specified
</asp:contentplaceholder>
This code specifies that a text string will be rendered in the placeholder's position when a page that inherits this master page does not specify any content to be placed within the <asp:contentplaceholder>.
It's All Relative
One trick you'll need to learn when working with master pages revolves around relative addressing. Let's say you place an image in the header of a master page, and all images are in a separate Images directory:
<img src="../Images/AG00004_.GIF" />
This works fine in Design mode for the master page, but the image won't be found if your master pages are in a different location than content pages, because the relative path starts at the content page location and not the master page location when ASP.NET 2.0 begins compiling the page. If you take a look at the content page linked to this master page in Design mode, you'll see that the image can't be found.
You could overcome this problem by using ASP.NET server controls instead of HTML controls or by using the complete physical address. However, this can cause problems when moving the application from development to production. You could also make the image control a server control and use the special "~" character used by ASP.NET during page rendering to represent the home location of the current application:
<img src="~/Images/AG00004_.GIF"
runat=server />
The drawback to this approach is that the images won't display in the IDE, because it appears that the IDE doesn't translate the "~" within HTML controls in Design mode in beta 1. However, notice that the IDE does translate this character correctly inside the <%@ Page %> and <%@ Master %> declarations.
Once you've laid out the master page in Design mode, you can manage events in the code-behind file just like a normal ASP.NET page. You can manipulate all of the properties of the master page as well as respond to any page events, or events for controls placed inside of the master page. Opening up the code-behind file for the first time on a master page, you'll see code that looks pretty much like your normal ASP.NET page code:
Partial Class BasicMasterPage_master
Sub Page_Load (ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Me.Load
End Sub
End Class
Now that you've defined a master page, it's time to create a Web application page that will inherit from the master page (called a content page). Go to Add | New Item in VS.NET 2005 and select Web Form. Make sure that the Select Master Page checkbox is checked so that you can visually select the master page (see Figure 2). You can also select the master page or change it later by altering the new MasterPageFile attribute (see Listing 2 for a sample content page created using the BasicMasterPage.master file in the sample project provided). Notice that the page declaration is just like the one you're probably used to already, except that there is now a MasterPageFile property that defines the link between this content page and the master page:
<%@ Page Language="VB"
MasterPageFile=
"~/MasterPages/BasicMasterPage.master"
AutoEventWireup="false"
CompileWith="BasicContentPage.aspx.vb"
ClassName="BasicContentPage_aspx"
title="Basic Content Page" %>
Remember that the master page also had a <TITLE> property defined inside the HTML <BODY> tag. If you don't define a title property within the content page declaration, the title property of the master page will be used for the title of the rendered ASP.NET page.
The only control at the top of the ASPX page is an <asp:content> control. This control is linked to the <asp:contentplaceholder> in the master page through the ContentPlaceHolderID property. VS.NET 2005 links it automatically if you select the master page through the IDE. The VS.NET 2005 IntelliSense has also been improved so that when adding or modifying this property, you can select any of the content placeholders defined in the master page.
Avoid Common Errors
All content within a content page must be placed within an <asp:content> control. A content page cannot have any other controls or HTML outside of an <asp:content> control; otherwise, you'll get an error (see Figure 3 for one of the most common errors developers come across when using master pages). Remember, there can be only one <HTML> and <BODY> section within a rendered page, and this was created already in the master page. Therefore, you need to think of a content page like a user control, in that it's a subset of HTML to be rendered within another page (the master page). If you don't provide an <asp:content> control, then the page will be rendered with any (if defined) default content provided within the <asp:contentplaceholder>.
When the content page is requested, ASP.NET 2.0 retrieves the referenced master page, compiles it, and then compiles the content page and places its content within the desired content placeholder. Of course, you can cache both master and content pages to improve performance, just as in previous versions of ASP.NET.
You can also define a master page for a Web application inside the web.config file. You might want to do this to enforce the use of a master page. This sets the master page definition for all ASPX pages within a Web application controlled by the web.config file. Of course, you can override this setting by specifying a master page inside the <%@ Page %> declaration:
<configuration>
<system.web>
<pages master=
"~/MasterPages/BasicMasterPage.master"
/>
</system.web>
</configuration>
ASP.NET 2.0 also allows you to nest master pages. For example, you might want to define the overall styles, headers, and footers with a top-level master, and then allow each department to define its own master that inherits the top-level master. Content pages for each department would then inherit their own department-specific master page.
In the sample project provided, I created nested master pages called Level1MasterPage.master and Level2MasterPage.master. Level1MasterPage is the top-level master page that provides the layout for headers and footers. Level2MasterPage is the master for menus that you can customize per department (see Listing 3 for the ASPX code for both pages).
You create the top-level master just as you did in the earlier example. However, the second-level master is a little different. Remember, the top-level master contains the initial <HTML> and <BODY> definitions, so you cannot duplicate them within any submasters that inherit the top-level master. Just as the content page can contain only content with <asp:content> tags, the same thing applies to sublevel master pages. In this example, the Level2MasterPage contains an <asp:content> control. Inside this control is the definition for the menu system, and another <asp:contentplaceholder> area for content pages that inherit it. The ContentPlaceHolderID property is linked to the ID of the placeholder defined within the Level1MasterPage.
One of the drawbacks of using nested master pages is that you currently cannot use the VS.NET 2005 Designer view to work with them. You need to work in Source view if you want to work with nested master pages, or content pages that reference nested master pages. Hopefully, this feature will be added by the time VS.NET 2005 is released to production.
A content page can also reference and set values within the master page through the use of the new Master object. This gives you the capability to query or override the master page's properties. Or, you can use this capability to load a master page dynamically to give users the ability to customize their own look and feel based on a set of available master pages (or templates). Let's say you want to add metadata to the HTML header of the current content pageyou can add this code to the Page_Load event handler of the content page:
Master.Page.Header.Metadata.Add( _
"Description", _
"Sample master/content page")
You can also access ASP.NET controls on a master page by accessing properties within the new Master object through one of two methods. The Master object gives you access to information about the master page, including the Page object inside the master page itself.
Gain Access to Controls
One way to get access to a control in a master page is through the use of the FindControl method. Assuming that there's a Textbox control on the master page called txtSummary, you could reference the textbox like this:
Dim txtSummary As TextBox = _
CType(Master.FindControl("txtSummary"), _
TextBox)
You can also expose controls in the master page through public properties in the class of the master page itself. This method requires you to create a public property inside the master page's code-behind file for each control you want to access. This public property is in the class that contains the actual control you're providing access to, so you cannot give it the same name. A common technique is to use an underscore as a prefix for the property name so that it looks as close as possible to the original control name. This method is the fastest (due to the overhead of the FindControl method), but it can also be confusing and cumbersome. (It also requires additional code for each new control added, just in case it might be accessed from a content page). You'll need to look closely at the pros and cons of each method to select which one is best for you.
You might be wondering about the order of event firing, because both a master and content page can have Page_Load (and other) common events. ASP.NET 2.0 fires page events is this order:
<Content page events>
<Bottom-level master page events>
...
<Top-level master page events>
In the sample project provided with this article, I created a sample master page called SampleMasterPage.master with a textbox control at the top. Here's the code-behind file:
Partial Class SampleMasterPage_master
Sub Page_Load (ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Me.Load
txtEventLog.Text += vbCrLf
txtEventLog.Text +=
"Master Page_Load Event Handler Fired"
End Sub
End Class
I then created a content page linked to this master page called EventTester.aspx (see Listing 4 for the code-behind files for this content page).
As you can see, each class module has a Page_Load event that writes to the textbox defined in the master page when it gets fired. In addition, EventTester.aspx writes an entry to the textbox control when the user clicks on the Press Me button. You can see the order of event firing by running this sample Web application:
Content page Page_Load
Master page Page_Load
Content page "Press Me" Button.Click
As you can see, the inclusion of master pages in ASP.NET 2.0 provides you with another time-saving tool to help develop consistent Web applications without the additional overhead of include files, user controls, or third-party add-ins. Master and content pages are easy to implement and help you provide consistent content without having to worry about the details. By integrating master pages with new personalization and membership features included in ASP.NET, you can easily add the ability to allow your users to customize their own look and feel by selecting from a set of customized master pages that you provide to them.
About the Author
Doug Thews is the director of software development for D&D Consulting Services in Dallas. Doug has more than 19 years of software-development experience in C/C++, VB/ASP, and VB.NET/C#. He writes the Getting Started column for Visual Studio Magazine, and coauthored the book, Professional ASP.NET Performance (Wrox/Wiley Press). Reach Doug at dougthews@ddconsult.com.