Implementing Master Pages
A professional web site will have a standardized look across all pages.
For example, one popular layout type places a navigation menu on the left side of the page, a copyright on the bottom, and content in the middle. It can be difficult to maintain a standard look if you must always put the common pieces in place with every web form you build. In ASP.NET 2.0, master pages will make the job easier. You’ll only need to write the common pieces once – in the master page. A master page can serve as a template for one or more web forms. Each ASPX web form only needs to define the content unique to itself, and this content will plug into specified areas of the master page layout.
To add a master page to a web project, right-click your web project in the Solution Explorer window, select Add New Item, and select the Master Page item type from the Add New Item dialog.
The generated master page will have the extension “.master” as opposed to “.aspx” for an ASP.NET form and will start with the following tag:
<%@ Master Language="VB" CodeFile="Site.Master.vb" Inherits="Site" %>
You can have as many master pages as you want per website and implement different layouts in each. You can then set different pages to use different master pages.
Master pages will define the <html>, <head>, <body >, and <form>, tags. A new control, the ContentPlaceHolder control also appears in our master page.
You can have one or more ContentPlaceHolder controls in a master page.
ContentPlaceHolder controls are where we want our ASPX web forms to place their content.
You can see in the picture below the two ContentPlaceHolders generated when the master page was created.
The head ContentPlaceHolder can be used to store meta tags which are individual to each page and the MainContent can be used to store the different page contents. Just like any ASPX form, our master page can contain code in a <script> block, or in a code-behind file, and can respond to page lifecycle events. The MasterPage class (from the System.Web.UI namespace) derives from UserControl and will have all of the usual events: Init, Load, PreRender, etc.
The ASPX web forms we use to put content into master pages are plain .aspx files, although with a few differences. When you add a new ASPX web form to a project you’ll have the option of associating the web form with a master page.
Doing so will place a MasterPageFile attribute in the @ Page directive for the web form.
The attribute points to the .master file you have selected (you can have multiple master pages in a web project). The source code below is for a default.aspx file using the site.master master page we defined earlier.
<%@ Page Title="Home Page" Language="VB" MasterPageFile="~/Site.Master" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
A web form associated with a master page is called a content page.
A content page may only contain markup inside of Content controls.
If you try to place any markup or controls outside of the Content controls, you’ll receive a compiler error:
Only Content controls are allowed directly in a content page that contains Content controls.
Each Content control in a content page maps to exactly one of the ContentPlaceHolder controls in the Master Page.
You do not need to define a content control for every ContentPlaceHolder control in the master.
If a Content control doesn’t exist for a place holder, the master page will simply render the default content inside of the ContentPlaceHolder of the master page (we did not define any in this example).
The runtime examines the ID of each Content control and finds a ContentPlaceHolder with a matching ID. When the runtime finds the match it will take the markup and controls inside the Content control and insert them into the ContentPlaceHolder of the master page. Even though our Content controls in this example do not contain any server side controls, like GridView controls and Calendar controls, any ASP.NET control can appear inside the Content areas.
One benefit to working with master pages in the Visual Studio IDE is the built-in design support for master pages. The screen below shows our default.aspx form in a design time view. The Content controls are the only editable sections of the form. The master page markup displays in design view, but since we are editing default.aspx and not otc.master in the screen shot, the sections defined by the master page are un-editable and ghosted out. This gives designers and developers a clear idea of content versus the layout structure defined by the master page.
Programatically Working With Master Pages
The MasterPage class derives from UserControl. The master page will inject itself as the top control in a page’s control
hierarchy by clearing the page’s Controls array and adding itself to into the collection.
Doing so includes all of the markup and controls defined inside the master page in the page’s control tree.
The master page can then walk the Content controls that existed in the web form and bring them back them into the control hierarchy
in the appropriate locations. The master page injection happens after the PreInit event fires for a Page object, but before
the Init event fires.
This ordering of events outlined above is important if you want to programmatically set the master page to use for a web form with the MasterPageFile property. This property must be set in the PreInit event (or earlier), like the code below. One the master page has injected itself into the control hierarchy it is too late to try to set a new master page for the web form. If you try to set the MasterPageFile property after the PreInit event fires, the runtime will throw an InvalidOperationException.
Protected Sub Page_PreInit(ByVal sender As Object, _ ByVal e As EventArgs) _ Handles Me.PreInit ' we can select a different master page in the PreInit event Me.MasterPageFile = "~/Site.master" End Sub
Another way to interact with a master page is through the Master property of a page object.
Let’s say we need to get to the Menu defined in our master page. We would like to change its background to Gray. For this, we will define a new property inside the masterpage with the following code:
Public Property NavigationMenuBk As System.Drawing.Color Get Return NavigationMenu.BackColor End Get Set(ByVal value As System.Drawing.Color) NavigationMenu.BackColor = value End Set End Property
There are two approaches to touching this property.
The first is to use the Master property of the System.Web.UI.Page class, which returns a MasterPage reference.
In order to get to the Footer property, though, we would have to cast the reference to our derived master page type, like the following code.
CType(Master, Site).NavigationMenuBk = Drawing.Color.AliceBlue
A second approach, if you know the exact master page file your page will be using at runtime,
is to let the ASP.NET page parser generate a strongly typed Master property by adding an @ MasterType
directive to your ASPX file, as shown below.
<%@ MasterType VirtualPath=”~/otc.master” %>
The MasterType directive will instruct the runtime to add a new Master property to code-generated file for the page. The new property will return a reference matching the MasterType. With a MasterType directive in place for our web form, we don’t need a cast.
Master.NavigationMenuBk = Drawing.Color.AliceBlue
Problems with Relative URLs when Using MasterPages
As the master page and the content page can be found in different directories, referencing relative URLs (like for images) which lead to
different directories will not work properly.
The solution would be either using full path URLs (website name, directory, file name) or changing the control to run at server:
<img src="images/tree.png" alt="1" /> will be converted to: <img src="images/tree.png" alt="1" runat="server" />
The following markup demonstrates what will work, and not work from a master page.
<!– this will break –>
<img src=”images/tree.png” alt=”1″ />
<!– this wil work –>
<!– server side control will rebase itself –>
<img src=”images/tree.png” runat=”server” alt=”2″ />
<!– this will work –>
<!– rooted references will always work, but are fragile –>
<img src=”/masterp/images/tree.png” alt=”3″ />
<!– this will break –>
<!– app relative (~) only works on a server side control –>
<img src=”~/images/tree.png” alt=”4″ />
<!– this will work –>
<img src=”~/images/tree.png” runat=”server” alt=”5″ />
<!– both of these will work –>
<!– the second Image will rebase URL –>
<asp:Image ID=”Image6″ runat=”server” ImageUrl=”~/images/tree.png” AlternateText=”6″ />
<asp:Image ID=”Image7″ runat=”server” ImageUrl=”images/tree.png” AlternateText =”7″ />
My background is relative, and broken
My background works
My background is broken. URL rebasing doesn‘t work for embedded style, event with server side control.
Nested Master Pages
You can write a content page that is itself a master page, allowing an arbitrarily deep nesting of master pages.
This technique can be useful when your application is broken into sub-applications
that need to inherit a common branded look but still be able to define their own customized layout template within the brand.
To add a nested master page, Right click on Solution Explorer and choose “New Item” – Master Page and then after filling in the new master page name (for example Master2.master), tick the “Select Master Page” tickbox and pick the master page you wish to nest the new one to.
The following listing shows a master page that would serve as a content page for our first master page example.
Notice we must obey the rules for a content page in this master page, that is, we can only define content inside of Content controls. The ContentPlaceHolders inside of these controls can then be filled in with web forms addressing the IDs of the nested master page. A web form cannot reach the ContentPlaceHolders in the master page above it’s own master page.
The code for a content page would look like this:
<%@ Page Title="" Language="VB" MasterPageFile="~/Master2.master" AutoEventWireup="false" CodeFile="Test.aspx.vb" Inherits="Test" %> <asp:Content ID="Content2" ContentPlaceHolderID="main1" Runat="Server"> </asp:Content> <asp:Content ID="Content4" ContentPlaceHolderID="Main2" Runat="Server"> </asp:Content>
Master Pages and Configuration
There are three ways to associate a web form with a master page.
You can use the Master attribute in the @ Page directive, and you can write to the MasterPageFile property in the PreInit event or earlier.
If you are assinging the master page after the PreInit directive, you will receive the following exception:
The ‘MasterPageFile’ property can only be set in or before the ‘Page_PreInit’ event.
We’ve seen examples of both of these techniques. Any web form using the Master attribute of the @ Page directive or setting the MasterPageFile property programmatically will override the web.config settings.
We can also assign a default master page for all web forms in a site in our web.config file using the <pages> element. An example excerpt from web.config is shown below.
<configuration> <system.Web> <pages master="otc.master" /> </system.Web> </configuration>