Vous êtes sur la page 1sur 28

Master Page Use in ASP.

NET
Problem. You need to use master pages along with code-behind in your ASP.NET website.
Master pages can be combined with code-behind in C# to simplify and ease maintenance of your
complex site. Solution. Here we look at how to use master pages in ASP.NET and get started
with examples.

1. Understanding master pages in ASP.NET


First, in the examples, I use the Website project type in Visual Studio. This is recommended as it
is a simpler and more streamlined project type. If you are developing a Web Application project,
you may need to navigate slightly different menus and dialogs.
File
Item Usage in ASP.NET
extension
Stores global page elements that occur
Master page .Master
on every content page
Stores page-specific elements which are
Content page .aspx
put into master page
Master page code Can change master page after it acquires
.aspx.cs
behind content

2. What you should put on the master page


The master page serves as a template for the other content pages on the site. The master page has
some code-behind methods, and it could auto-generate the page titles for the pages and some H1
headers for each content page. Here are some likely things you will want on your master page.
• Navigation
The master page also might contain a TreeView for navigation and a footer with your
contact info.
• Styles
It uses markup for the site layout, and contains some CSS styles and JavaScript code. You
can use inline CSS in your master page, for an alternative to an external stylesheet.
• Images or scripts
Your site's logo is a perfect thing to put in your master page. You can also put code such
as Google Analytics, which is implemented as a small piece of JavaScript.

3. How can I make a master page?


You must go to the Website menu in Visual Studio, and then add a new item. Master Page will
appear in that list, so just select it and proceed as normal. You should be familiar enough with
Visual Studio to do this quickly.
4. How master pages work
The ContentPlaceHolder markup tag is where content page content is inserted into each page.
So, now we want to make some content pages. How do we do that? Now, look at your new
master page file. You will see some tags in the master page. One of the most important ones is as
follows:
<asp:ContentPlaceHolder ID="MainContent" runat="server" />

5. How can I make a content page?


Content pages are made in the exact same way that master pages are made. Go to the Website
menu, then add new item, and select Web Form. That's MS-speak for "Content Page". Check the
"Select Master Page" checkbox, and finally select your master page.
6. How content pages work
Next I want to show you an example of a content page and walk you through some parts of it.
You should have a content page now and it will have some special markup in it. It will contain
several lines of markup similar to the following.
<%@ Page Language="C#" MasterPageFile="~/DotNetPerls.Master" Title="Untitled
Page" %>

<script runat="server">

</script>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">


</asp:Content>
• What's the ID?
The ID is Content1, and it is very important when using the content page with the
specified master page.
• ContentPlaceHolderID
The ContentPlaceHolderID is "MainContent". It must match the ID="MainContent" part
of the ContentPlaceHolder tag in the Master Page.
• Always remember runat="server"
As always, you must add runat="server", which indicates that this tag will be "run" on the
server before being sent to the browser.
7. How you can use master pages and content pages together
In the master page, you can change various elements picked up from content pages in the code-
behind. The master page transforms itself and takes on the properties of the content pages. Then,
you can use code in the master pages to change parts of the master page that were retrieved from
the content pages.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web;
using System.Web.UI;

public partial class _Default : MasterPage


{
// This code belongs in the *master.cs file. You can usually open this
// by clicking on the little + box next to your *.master file.
protected void Page_Load(object sender, EventArgs e)
{
// Using code-behind, we can modify each page's title according to
logic.
// Let's say you want each title on your site to have the words "Your
Site: "
// at the start. Try out the following code, and put regular page
titles
// in the content pages.
this.Page.Title = "Your Site: " + this.Page.Title;
// This will turn "Content Page Title" into "Your Site: Content Page
Title"
// on every content page.
}
}

8. Summary
You can dynamically generate pages, leading to fewer typos, with less content to manage. The
master page, content page and code-behind model can simplify and streamline your site. Code-
behind can separate complex logic from page markup.

Master pages are a great addition to the ASP.NET 2.0 feature set. Master pages help us
build consistent and maintainable user interfaces. Master pages, however, are not without their
quirks. Sometimes master page behavior is surprising, and indeed the very name master page can
be a bit misleading. In this article, we are going to examine some of the common problems
developers run into when using master pages, and demonstrate some practical advice for making
effective use of master pages. For an introduction to master pages, see "Master Pages In
ASP.NET 2.0".
To make use of master pages, we first need to understand how master pages work. Many of the
tips and traps covered later in this article revolve around understanding the magic behind master
pages. Let’s dig into these implementation details first.

For Internal Use Only


When a web request arrives for an ASP.NET web form using a master page, the content page
(.aspx) and master page (.master) merge their content together to produce a single page. Let’s say
we are using the following, simple master page.
<%@ Master Language="VB" %>

<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>

The master page contains some common elements, like a head tag. The most important server-
side controls are the form tag (form1) and the ContentPlaceHolder (ContentPlaceHolder1). Let’s
also write a simple web form to use our master page.
<%@ Page Language="C#" MasterPageFile="~/Master1.master"
AutoEventWireup="true" Title="Untitled Page" %>
<asp:Content ID="Content1" Runat="Server"
ContentPlaceHolderID="ContentPlaceHolder1" >
<asp:Label ID="Label1" runat="server" Text="Hello, World"/>
</asp:Content>

The web form contains a single Content control, which in turn is the proud parent of a Label. We
can visualize what the object hierarchies would look like at runtime with the following diagram.

At this point, the page and master page are two separate objects, each with their own children.
When it comes time for the master page to do its job, the master page replaces the page’s
children with itself.

The master page’s next step is to look for Content controls in the controls formerly associated
with the page. When the master page finds a Content control that matches a ContentPlaceHolder,
it moves the controls into the matching ContentPlaceHolder. In our simple setup, the master page
will find a match for ContentPlaceHolder1, and copy over the Label.
All of this work occurs after the content page’s PreInit event, but before the content page’s Init
event. During this brief slice of time, the master page is deserving of its name. The master page
is in control - giving orders and rearranging controls. However, by the time the Init event fires
the master page becomes just another child control inside the page. In fact, the MasterPage class
derives from the UserControl class. I’ve found it useful to only think of master pages as masters
during design time. When the application is executing, it’s better to think of the master page as
just another child control.
The Pre_Init event we just mentioned is a key event to examine if we want to change the master
page file programmatically. This is the next topic for discussion.

Handling the PreInit Event


We can use the @ Page directive and the web.config to specify master page files for our web
forms, but sometimes we want to set the master page programatically. A page’s MasterPageFile
property sets the master page for the content page to use. If we try to set this property from the
Load event, we will create an exception. In other words, the following code…
protected void Page_Load(object sender, EventArgs e)
{
MasterPageFile = "~/foo";
}

… creates the following exception.


The 'MasterPageFile' property can only be set in or before the 'Page_PreInit' event.
This exception makes sense, because we know the master page has to rearrange the page’s
control hierarchy before the Init event fires. The simple solution is to just use the PreInit event,
but we probably don’t want to write the PreInit event handler over and over for each web form in
our application. Chances are good the PreInit event handler will need to look up the master page
name from a database, or a cookie, or from some user preference settings. We don’t want to
duplicate this code in every webform. A better idea is to create a base class in a class library
project, or in the App_Code directory. (For a Visual Basic version of the code snippets in this
section, see this post).
using System;
using System.Web.UI;

public class BasePage : Page


{
public BasePage()
{
this.PreInit += new EventHandler(BasePage_PreInit);
}

void BasePage_PreInit(object sender, EventArgs e)


{
MasterPageFile = "~/Master1.master";
}
}

To use this base class, we need to change our code-beside file classes to inherit from BaseClass
instead of System.Web.UI.Page. For web forms with inline code, we just need to change the
Inherits attribute of the @ Page directive.
<%@ Page Language="C#" MasterPageFile="~/Master1.master"
AutoEventWireup="true" Title="Untitled Page"
Inherits="BasePage" %>

The inheritance approach is flexible. If a specific page doesn’t want it’s master page set, it can
choose not to derive from BasePage. This is useful if different areas of an application use
different master pages. However, there may be times when we want an application to enforce a
specific master page. It could be the same type of scenario (we pull the master page name from a
database), but we don’t want to depend on developers to derive from a specific base class
(imagine a third party uploading content pages). In this scenario we can factor the PreInit code
out of the base class and into an HttpModule.
HttpModules sit in the ASP.NET processing pipeline and can listen for events during the
processing lifecycle. Modules are good solutions when the behavior you want to achieve is
orthogonal to the page processing. For instance, authentication, authorization, session state, and
profiles are all implemented as HttpModules by the ASP.NET runtime. You can plug-in and
remove these modules to add or discard their functionality. Here is a module to set the
MasterPageFile property on every Page object.
using System;
using System.Web;
using System.Web.UI;

public class MasterPageModule : IHttpModule


{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
}

void context_PreRequestHandlerExecute(object sender, EventArgs e)


{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit +=new EventHandler(page_PreInit);
}
}

void page_PreInit(object sender, EventArgs e)


{
Page page = sender as Page;
if (page != null)
{
page.MasterPageFile = "~/Master1.master";
}
}

public void Dispose()


{
}
}

When the module initializes, it hooks the PreRequestHandlerExecute event. The


PreRequestHandlerExecute fires just before ASP.NET begins to execute a page. During the event
handler, we first check to see if ASP.NET is going to execute a Page handler (this event will also
fire for .asmx and .ashx files, which don’t have a MasterPageFile property). We hook the page’s
PreInit event. During the PreInit event handler we set the MasterPageFile property. Again, the
event handler might look up the filename from the database, or a cookie, or a session object,
which is useful when you give a user different layouts to choose from.
To use the module, we just need to add an entry to the application’s web.config.
<httpModules>
<add name="MyMasterPageModule" type="MasterPageModule"/>
</httpModules>

Abstract Interaction
Now it’s time to have the master page and content page interact. There are different approaches
we can take to achieve interaction, but the best approaches are the ones that use the master page
for what it is: a user control. First, let’s look at how the content page can interact with the master
page.
Content Page to Master Page Interaction
Let’s imagine we want all of the pages in our application to have some text in a footer area. This
seems like the perfect job for a master page, so we will add a label control to our master.
<form id="form1" runat="server">
<div>
<asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
</asp:contentplaceholder>
</div>

<asp:Label runat="server" ID="FooterLabel"


Text="Default footer text" />

</form>

The catch is, some content pages need to override the default footer text. Here is one approach
we can use from page’s Page_Load event handler.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As EventArgs)

Dim footer As Label = Master.FindControl("FooterLabel")


If Not footer Is Nothing Then
footer.Text = "Custom footer text!!"
End If

End Sub
Use the above approach with extreme caution. FindControl is fragile, and will return null if
someone renames FooterLabel, or removes the control entirely. This problem can't be discovered
until runtime. FindControl also has some additional difficulties when INamingContainers are
involved - we will discuss this topic later.
A better approach is to establish a formal relationship between the master page and content page,
and take advantage of strong typing. Instead of the content page poking around inside the master
page, let’s have the master page expose the footer text as a property. We can add the following
code to our master page.
Public Property FooterText() As String
Get
Return FooterLabel.Text
End Get
Set(ByVal value As String)
FooterLabel.Text = value
End Set
End Property

The best way to use this property is to place a @ MasterType directive in our content page.
When the ASP.NET compiler sees the @ MasterType directive, it creates a strongly typed Master
property in our Page derived class.
<%@ Page Language="VB" MasterPageFile="~/Master1.master"
AutoEventWireup="true" %>
<%@ MasterType VirtualPath="~/Master1.master" %>

<script runat="server">

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)

Master.FooterText = "Custom footer text"

End Sub

</script>

This code is a cleaner and doesn’t depend on the magic string “FooterLabel”. If anyone ever
removes the control from the master page, or renames the control, we will have compilation
errors instead of runtime problems.
What if we have 2 different master pages in the application? In this scenario, we have a problem,
because the VirtualPath attribute supports only a single master page. We’ve tightly coupled our
page to a specific master. If we assign a MasterPageFile that does not match the MasterType, the
runtime will throw an exception.
Unable to cast object of type 'ASP.master2_master' to type 'ASP.master1_master'.
Fortunately, the @ MasterType directive doesn’t require us to use a VirtualPath, we can also
specify a type name. Once again we will turn to inheritance to solve this problem. If all the
content pages expect their master pages to have footer text, then let’s define a base class for the
master pages to inherit.
We can take one of two approaches with the base class. One approach is to use an abstract
(MustInherit) base class:
using System.Web.UI;

public abstract class BaseMasterPage : MasterPage


{
public abstract string FooterText
{
get;
set;
}
}

Our master pages must inherit from this base class and override the FooterText property.
<%@ Master Language="VB" Inherits="BaseMasterPage" %>

<script runat="server">

Public Overrides Property FooterText() As String


Get
Return FooterLabel.Text
End Get
Set(ByVal value As String)
FooterLabel.Text = value
End Set
End Property

</script>

Now our page can use any master page that inherits from BaseMasterPage. All we need is an @
MasterType directive set to the base class. Instead of using a VirtualPath attribute, we use a
TypeName attribute and specify the name of the base class.
<%@ Page Language="VB" MasterPageFile="~/Master1.master"
AutoEventWireup="true" %>
<%@ MasterType TypeName="BaseMasterPage" %>

<script runat="server">

Protected Sub Page_Load(ByVal sender As Object, _


ByVal e As EventArgs)

Master.FooterText = "Use the base class..."

End Sub

</script>

The second approach is to use a concrete base class. This approach is possible only if we are sure
every master page will have a label with an ID of “FooterLabel”.
using System.Web.UI;
using System.Web.UI.WebControls;

public class BaseMasterPage : MasterPage


{
protected Label FooterLabel;
public string FooterText
{
get
{
return FooterLabel.Text;
}
set
{
FooterLabel.Text = value;
}
}
}

With the above approach we can remove code from our master page – we don’t need to define
the FooterText property. If we are using code-beside files instead of inline script, we need to use
CodeFileBaseClass=”BaseMasterPage” in the @ Master directive to ensure ASP.NET can wire
up the base class’s Label field with the Label control.
Master Page To Content Page Interaction
Here is a case where the master part of the master page name can be misleading. The master
page sounds like a good place to put logic and code that will tell the page how to do something.
After all, a master page is the master, right? We now know that the master page is just another
child control. Ideally, the master page will remain passive. Instead of telling it’s parent page
what to do, the master page should tell a page when something interesting happenes, and let the
page decide what to do.
Let’s pretend every page in our application displays a report, and every page needs a button for
users to click and email the report. Putting a Button and a TextBox inside the master page seems
like a reasonable choice.
<asp:TextBox runat="server" id="EmailAddressBox" />
<asp:Button runat="server" ID="SendEmailButton"
OnClick="SendEmailButton_Click" />

What happens when the user clicks the button? We can choose from the following options:
• Handle the Click event in the master page, and have the master page email
the report.
• Expose the Button and TextBox as public properties of the master page, and
let the content page subscribe to the click event (and email the report).
• Define a custom SendEmail event, and let each page subscribe to the event.
The first approach can be ugly because the master page will need to call methods and properties
on the page. Master pages are about layout, we don’t want to clutter them with knowledge of
reports and specific pages.
The second approach is workable, but it tightly couples the page to the master. We might change
the UI one day and use a DropDownList and a Menu control instead of a TextBox and Button, in
which case we’ll end up changing all of our pages.
The third approach decouples the master page and content page nicely. The page won’t need to
know what controls are on the master page, and the master page doesn’t have to know anything
about reports, or the content page itself. We could start by defining the event in a class library, or
in a class file in App_Code.
using System;

public class SendEmailEventArgs : EventArgs


{
public SendEmailEventArgs(string toAddress)
{
_toAddress = toAddress;
}

private string _toAddress;


public string ToAddress
{
get { return _toAddress; }
set { _toAddress = value; }
}

public delegate void SendEmailEventHandler(


object sender, SendEmailEventArgs e);

We can raise this event from a master page base class (if we have one), or from the master page
itself. In this example, we will raise the event directly from the master page.
<%@ Master Language="VB" %>

<script runat="server">

Public Event SendEmail As SendEmailEventHandler

Protected Sub SendEmailButton_Click(ByVal sender As Object, _


ByVal e As System.EventArgs)

Dim eventArgs As New SendEmailEventArgs(EmailAddressBox.Text)


RaiseEvent SendEmail(Me, eventArgs)

End Sub

</script>

We'll need to add some validation logic to the master page, but at this point all we need is to
handle the event in our page. We could also handle the event from a base page class, if we don’t
want to duplicate this code for every page.
<%@ Page Language="VB" MasterPageFile="~/Master1.master"
AutoEventWireup="true" %>
<%@ MasterType VirtualPath="~/Master1.master" %>

<script runat="server">

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)


AddHandler Master.SendEmail, AddressOf EmailReport
End Sub

Protected Sub EmailReport(ByVal sender As Object, ByVal e As SendEmailEventArgs)

Dim address As String = e.ToAddress

' do work

End Sub

</script>

Master Pages and Cross Page Postbacks


Another common scenario for master pages is to use a cross page post back. This is when a
control on the master page POSTs to a second web form. For more information on cross page
post backs, Let’s add search functionality to our site by adding a TextBox and Button to the
master page.
<asp:TextBox runat="server" id="QueryBox" />
<asp:Button runat="server" ID="SearchButton"
PostBackUrl="~/SearchResults.aspx" />

When the user click the search button, the web request will ultimately arrive at the
SearchResults.aspx. How will SearchResults.aspx find the text the user wants to search for? We
could use the PreviousPage.Master property and FindControl to locate the QueryBox TextBox by
its ID, but we’ve already discussed some reasons to avoid FindControl when possible.
What about the exposing the text as a property? It sounds easy, but...
In ASP.NET 2.0, each master page and web form can compile into a separate assembly. Unless
we establish a reference between two assemblies, the types inside each assembly cannot see one
another. The @ MasterType directive with a VirtualPath attribute ensures the web form’s
assembly will reference the master page assembly. If our SearchResults.aspx page uses the same
@ MasterType directive as the POSTing web form, it will be able to see the master page type,
and life is simple.
Let’s assume our SearchResults.aspx page does not use a master page, and we don’t want to use
FindControl. Inheritance is once again a solution to this problem. We will need a base class (or
an interface) defined in App_Code or a class library (all web form and master page assemblies
reference the App_Code assembly). Here is a base class solution.
public class BaseMasterPage : MasterPage
{
protected Label PageFooter;
protected TextBox QueryBox;

public string QueryText


{
get { return QueryBox.Text; }
}

// ...

SearchResults.aspx will assume the PreviousPage.Master property references a type derived


from BaseMasterPage.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As EventArgs)

If Not PreviousPage Is Nothing AndAlso _


Not PreviousPage.Master Is Nothing Then

Dim master As BaseMasterPage


master = DirectCast(PreviousPage.Master, BaseMasterPage)

Dim searchTerm As String


searchTerm = master.QueryText

' do search

End If

While the above approach works pretty, well, you might consider going a step further. Define an
interface with a QueryText property and derive a base page (not master page) class from the
interface. The base page class can go to the trouble of getting the text from the master page.
Now, SearchResults.aspx doesn’t have to worry about master pages at all. It can use a cast to get
a reference to the interface from the PreviousPage reference, and then ask the interface for the
QueryText. Any type of page can then post to SearchResults, even those without a master page.

A Curious Turn of Events


Another master page twist that catches developers off guard is the order of the page lifecycle
events. Let’s say we write the following code in our web form:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("Hello from Page_Load in default.aspx <br>")
End Sub

.. and the following code in our master page:


Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("Hello from Page_Load in Master1.master<br>")
End Sub

Pop quiz: which Response.Write will appear in the output first?


Hint: most ASP.NET events are raised starting at the top of the control tree and working
downward.
In this case, “Hello from Page_Load in default.aspx” will appear before “Hello from Page_Load
in Master1.master”, because the content page’s Load event fires before the master page’s Load
event.
Let’s set up another quiz using the following code in our content page.
Protected Sub Page_Init(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("Hello from Page_Init in default.aspx <br>")
End Sub

... and the following code in our master page.


Protected Sub Page_Init(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("Hello from Page_Init in Master1.master<br>")
End Sub

Pop quiz: which Init event will fire first?


Earlier we said most ASP.NET events work their way down the tree of controls. The truth is all
lifecycle events (Load, PreRender, etc.) work in this fashion except the Init event. The
initialization event works from the inside out. Since the master page is inside the content page,
the master page’s Init event handler will fire before the content page’s Init event handler.
Obviously, problems will occur if the content page’s Load event handler depends on the master
page's Load event to finish some work or initialize a reference. If you find yourself with this
problem, or are worried about the order of events when a master page is involved, you might be
too tightly coupled to the master page. Consider our earlier approach of using a custom event
when when something interesting happens in the master page, and let the content page subscribe
to the event and take action. This approach achieves greater flexibility.
Headers, Scripts, and Meta Tags, Too
Generally, master pages will take care of including the HTML head tag. The HTML head tag can
include a <title> tag (to set the page title), one or more <script> tags (to include JavaScript
libraries), and one or more <meta> tags (to include meta data about the page). A content page
will often need to modify or augment the contents of the head tag. The title tag is a good
example, because the master page can’t set the title for each content page in an application. Only
the content pages know what thier title will be. Fortunately, ASP.NET provides a public property
on the Page class, and we can set a content page’s title declaratively in the @ Page directive.
<%@ Page Language="VB" MasterPageFile="~/Master1.master"
AutoEventWireup="true" Title="Home"
%>

If we want to add script or meta tags from a content page, we have more work to do. Here is an
example of injecting a redirection meta tag:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)

Dim metaTag As New HtmlMeta


metaTag.HttpEquiv = "Refresh"
metaTag.Content = "2;URL=http://www.OdeToCode.com"
Header.Controls.Add(metaTag)

End Sub

The Page class contains a public property named Header. Header gives us access to the head tag
as a server side control (the head tag in the master page must include runat=”server” for the
Header property to work). We can add style sheets to the header tag, too.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)

Dim cssLink As New HtmlLink()


cssLink.Href = "~/styles.css"
cssLink.Attributes.Add("rel", "stylesheet")
cssLink.Attributes.Add("type", "text/css")
Header.Controls.Add(cssLink)

End Sub

We can also add markup inside the head tag using an HtmlGenericControl, which provides
TagName, InnerText, InnerHtml, and Attributes properties.
Header Place Holders
There is another approach we can use to modify the header, which does have one drawback. The
ContentPlaceHolder and Content controls will merge even when we place a ContentPlaceHolder
control outside of the <form> tag. Take the following master page excerpt as an example.
<head runat="server">
<title>Untitled Page</title>
<asp:ContentPlaceHolder id="headerPlaceHolder" runat="server" />
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
<asp:Label runat="server" ID="PageFooter" Text="Default footer text" />
</form>
</body>
</html>

This master page uses a ContentPlaceHolder inside the head tag. Remember, a Content page isn’t
required to provide a Content control for every ContentPlaceHolder control in a master page. If
there is no Content control available for the master to merge into a ContentPlaceHolder, the
master page uses the default content inside of the ContentPlaceHolder. In the above code, we did
not specify any default content, but this is a trick to remember if you want to provide default
content with the ability to replace the default content from any given content page.
With the ContentPlaceHolder above, any content page can add additional tags inside the head tag
using a Content control.
<asp:Content ID="HeaderContent" runat="server"
ContentPlaceHolderID="headerPlaceHolder">
<link rel="stylesheet" type="text/css" href="customstyles.css" />
</asp:Content>

<asp:Content ID="Content1" Runat="Server"


ContentPlaceHolderID="ContentPlaceHolder1" >
<asp:Label ID="Label1" runat="server" Text="Hello, World"/>
</asp:Content>

We mentioned there is a drawback to this approach -what is the catch?


The problem is that Visual Studio 2005 believes all ContentPlaceHolder controls should live
inside the <form> tag. The ContentPlaceHolder we have inside the head tag will produce an error
message in the Visual Studio Error List window. However, the project will compile and run
without any complaints, exceptions, or error messages. The error appears to be generated by the
Visual Studio validation engine. We could disable validation for the project, however, this
disables validation of all HTML mark-up. You’ll have to decide if you can live the spurious
validation error message before taking the ContentPlaceHolder approach.
A Page Directive Approach
A third approach is possible which provides the same flexibility and convenience of the Title
attribute. For example, what if we wanted to set the meta keywords of a page in the @ Page
directive?
<%@ Page Language="VB" MasterPageFile="~/Master1.master"
AutoEventWireup="true" Title="Home" Inherits="BasePage"
MetaKeywords="masterpage ASP.NET"
%>

To use the MetaKeywords attribute in every page of an application, we just need to inherit from a
common base class that exposes a MetaKeywords property. The base class can also inject the
meta tag into the page header.
using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;

public class BasePage : Page


{
public BasePage()
{
Init += new EventHandler(BasePage_Init);
}

void BasePage_Init(object sender, EventArgs e)


{
if (!String.IsNullOrEmpty(MetaKeywords))
{
HtmlMeta metaTag = new HtmlMeta();
metaTag.Name = "Content";
metaTag.Content = MetaKeywords;
Header.Controls.Add(metaTag);
}
}

private string _metaKeywords;


public string MetaKeywords
{
get { return _metaKeywords; }
set { _metaKeywords = value; }
}
}

FindControl, JavaScript, and Naming


Containers
It’s important for us to understand why the following code throws a null reference exception.
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)

Page.FindControl("Label1").Visible = False

End Sub
</script>

<asp:Content ID="Content1" Runat="Server"


ContentPlaceHolderID="ContentPlaceHolder1" >
<asp:Label ID="Label1" runat="server" Text="Hello, World"/>
</asp:Content>

FindControl in the above code returns a null (Nothing) reference. Why? Let’s turn to the
FindControl documentation on MSDN.
FindControl searches the current naming container for the specified server control.
A naming container is any control that carries the INamingContainer interface. Both the
MasterPage and Content controls are naming containers. The key to using FindControl is to
invoke the method on the correct container, because FindControl doesn’t recursively traverse the
entire hierarchy of controls. FindControl only searches inside the current naming container.
Using the FindControl method on the Page reference means we won’t be searching inside of
MasterPage control. course, we don’t need to use FindControl in this scenario because our
content page will have a Label1 field, but if you do need to use FindControl for a control in a
content page, the following code will be helpful.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs)
Dim content As ContentPlaceHolder
content = Page.Master.FindControl("ContentPlaceHolder1")

Dim label As Label


label = content.FindControl("Label1")
label.Visible = False

End Sub

First, our code has to find the ContentPlaceHolder containing the Label control. We will use the
MasterPage control's FindCotnrol method. The MasterPage inside of our page is the naming
container that contains ContentPlaceHolder1. If you are wondering why we are not using the
Content1 control, it’s because no Content controls exist. Remember our early discussion on how
master pages work. Master pages copy the controls inside of the Content controls into
ContentPlaceHolder controls. The Content controls get left behind and don’t exist in the control
hierarchy.
Once we have a reference to the ContentPlaceHolder control, we use FindControl a second time
to locate the Label control. We could shorten all the above code into a single line:
Master.FindControl(...).FindControl(..).Visible = False

Name Mangling
A naming container also mangles its children’s ClientID property. Mangling ensures all ClientID
properties are unique on a page. For instance, the ID for our Label control is “Label1”, but the
ClientID of the Label is “ctl00_ContentPlaceHolder1_Label1”. Each level of naming container
prepends it’s ID to the control (the MasterPage control ID in this form is ctl00). Just as we have
to be careful with FindControl, we have to be careful with client side script functions like
getElementById. If we emit the following script into our page, it will fail with a JavaScript error:
‘Label1 is undefined’.
<script type="text/javascript">
<!--
Label1.innerHTML = 'Hello, from script!';
// -->
</script>

One 'solution' is to use the correct client side ID.


<script type="text/javascript">
<!--
ctl00_ContentPlaceHolder1_Label1.innerHTML = 'boo!';// -->
</script>

Of course, we’d never want to hardcode the client ID into a script. Typically we’ll need to build
the script dynamically using StringBuilder or String.Format. Another alternative is to use
markers in the script and use a call to String.Replace, like the following.
Dim script As String = "[Label1ID].innerHTML = 'boo!';"
Dim scriptKey As String = "SayBoo"
Dim addScriptTags As Boolean = True

Protected Sub Page_Load(ByVal sender As Object, _


ByVal e As EventArgs)

script = script.Replace("[Label1ID]", Label1.ClientID)


ClientScript.RegisterStartupScript( _
Me.GetType(), scriptKey, script, addScriptTags _
)

End Sub

Break Some URLs


Once again, let’s think back to the beginning of the article. At runtime, the master page and the
content page are in the same control hierarchy – the master page is essentially a user control
inside the content page. At design time, however, the master page and content page are two
different entities. In fact, the master page and content page may live in different directories.
During design time, it's easy to put URLs and relative paths into our master pages, but we have
to be careful when using relative paths. Take the following master page excerpt as an example:.
<div>
<img src="logo.gif" alt="Company Logo" />

<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">


</asp:ContentPlaceHolder>
</div>

As long as the master page and the web form live in the same directory, the company logo will
display in the browser. When the master page and web form live in different directories, the
image will not appear. The browser requests knows nothing about master pages. The browser
will interpret any relative paths it finds in the HTML as being relative to the webform. If our
logo and master page files are in the root directory, but the web form is in a subdirectory, the
browser will ask for logo.gif from the same subdirectory. The server will respond with a 404 (file
not found) error.
The good news is, the ASP.NET runtime does provide a feature called “URL rebasing”. The
runtime will try to “rebase” relative URLs it finds on server-side controls inside a master page.
This means the following relative path will work, no matter where the master page and web form
live.
<img src="logo.gif" alt="Company Logo" runat="server" />

We’ve added a runat=”server” attribute to the image tag, making the <img> a server-side control.
When the master page file and logo are in the root directory, but the web form is in a
subdirectory, the ASP.NET runtime will rebase the relative path it finds in the src attribute to
point to the root of the website.

The following code will also work, because we are using a server-side Image object.
<asp:Image ImageUrl="logo.gif" runat="server" />

The ASP.NET runtime will also rebase paths it finds inside of the head tag. Take the following
excerpt from a master page:
<head runat="server">
<title>Untitled Page</title>

<link href="styles/styles.css" type="text/css" rel="stylesheet"/>


</head>

If we request a webform from a subdirectory, the runtime will catch the href inside the link tag
and rebase the URL to "../styles/styles.css". However, the runtime doesn’t catch everything. If we
included our style sheet with the following code, the runtime won’t rebase the relative href.
<head runat="server">

<style type="text/css" media="all">


@import "styles/styles.css";
</style>

</head>

Also, the runtime doesn’t rebase URLs inside of embedded styles, and not all attributes are
covered (the background attribute, for instance).
<body background="logo.gif" runat="server">
<!-- the background for the body tag will break -->
<form id="form1" runat="server">

<div id="Div1" style="background-image: url('logo.gif');" runat="server">


<!-- My background is also broken. -->
</div>

If you need to use a relative path in an area where the runtime does not provide the rebasing
feature, you can compute a client side URL using ResolveClientUrl and passing a relative path.
ResolveClientUrl, when called from inside a master page, will take into account the location of
the master page, the location specified in the HTTP request, and the location specified by the
relative path parameter to formulate the correct relative path to return.
<body background=<%= ResolveClientUrl("logo.gif") %> >

When working with image paths in embedded styles, it’s often a good idea to move the style
definition into a .css file. The ASP.NET runtime will rebase the path it finds inside a link tag, so
we won’t have any problems locating the stylesheet from any webform. Take the following style
definition in a .css file:
body
{
background-image:url('images\logo.gif');
}

Relative paths are safe inside a .css file because the browser will always request logo.gif relative
to the location of the stylesheet.

Master Pages and Themes


Master pages, being just another control inside a page, do not have a separate theme applied.
Master pages use the theme specified by the page that is using them. For an introduction to
themes and skins in ASP.NET 2.0.Here is one question that comes up: how do we specify a
control skin so that the skin only applies to controls on the master page? There is no direct
method to pull this trick off, but ASP.NET themes do have the concept of skin IDs. There are two
types of skins: default skins, and skins with a SkinID attribute. A default skin will apply to any
control with the same type as the skin, but a skin with a SkinID will only apply to controls with
the same type and SkinID.
As an example, let’s say we want to control a logo graphic in our application with the theme and
skin infrastructure. We can define a skin for the logo like the following.
<asp:Image ID="Image1" runat="server" ImageUrl="Images/logo.gif" SkinID="logo" />

Notice the skin uses a relative path, so we can have a different logo graphic underneath each
theme we define. ASP.NET will rebase the path to the gif file. The master page only needs to use
the following markup.
<asp:Image ID="Image1" runat="server" SkinID="logo" />

Different logos can exist theme, and the skin we defined will only apply to Image controls with a
SkinID of “logo”.

Nesting Master Pages


It’s possible for a page to specify a MasterPageFile that itself consists only of Content controls.
The master page in this scenario would in turn specify another master page as its master. The
master pages are nested, but carry out the same steps described in the beginning of the article.
The child master page will first copy the content page’s content into its ContentPlaceHolder
controls. Then the parent master page will copy the nested master page’s content into its own
ContentPlaceHolder controls. In the end, the Page object will still be the top object in a control
hierarchy that renders as HTML.
Although nested master pages work at runtime, they do not work in the Visual Studio 2005
designer. If we try to open a content page in design view and the content page uses a nested
master page design, the designer will display an error message.
Design view does not support creating or editing nested master pages. To create or edit nested
master pages, use Source view.
There is a trick to working around this problem.
Let’s suppose we have our top master page (Master1.master) defined as follows.
<%@ Master Language="VB" %>

<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>

Then, we create a second master page (Nested.master) that uses master1.master as a master page.
<%@ Master Language="VB" MasterPageFile="~/Master1.master" %>

<asp:Content runat="server" ID="Content1"


ContentPlaceHolderID="ContentPlaceHolder1">

<h3>Nested Content</h3>
<asp:contentplaceholder id="NestedContent" runat="server">
</asp:contentplaceholder>

</asp:Content>

Finally, a content page which uses Nested.master as its MasterPageFile.


<%@ Page Language="VB" MasterPageFile="~/Nested.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="NestedContent" Runat="Server">
</asp:Content>

If we attempt to view this content page in design view, Visual Studio will produce the error
message shown earlier. If we really want to use the designer with our content page, we can leave
the MasterPageFile attribute empty, like in the following code:
<%@ Page Language="VB" MasterPageFile="" %>
<asp:Content ID="Content1" ContentPlaceHolderID="NestedContent" Runat="Server">
</asp:Content>

We can’t just drop the MasterPageFile attribute from the @ Page directive, because the designer
will raise a different error (“Content controls are allowed only in content page that references a
master page”). The empty attribute appears to trick the designer into allowing us into design
mode.
At runtime, however, the page will throw an exception because it doesn’t have a master file. We
can avoid the exception by programmatically setting the MasterPageFile property at runtime. We
know we will need to set the master page before or during the PreInit event. The following code
reads the masterPageFile attribute from the <pages> section of web.config. By putting the code
into a base class, we can cover all the content pages in an application.
using System;
using System.Web.UI;
using System.Web.Configuration;
using System.Configuration;

public class BaseContentPage : Page


{
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);

PagesSection pagesConfig =
ConfigurationManager.GetSection("system.web/pages")
as PagesSection;

MasterPageFile = pagesConfig.MasterPageFile;
}
}

Sharing Master Pages


Many people want to create a single master page, or set of master pages to use across multiple
applications. Unfortunately, there is no built-in capability to share master pages, and this article
will only provide some advice. The ultimate goal is the ability to modify a master page once, and
have the changes reflected in multiple applications with the least effort.
The first alternative is to copy shared master page files into a single location on an IIS web
server. Each application can then create a virtual directory as a subdirectory and point the virtual
directory to the real directory of master pages. The applications can then set the MasterPageFile
property of a page to the name of the virtual directory, plus the name of the master page file.
When we drop an updated master page file into the real directory, the new master page will
appear in all the applications immediately.
A second approach is to use a version control system to share a set of master page files across
multiple projects. Most source control / version control systems support some level of “share”
functionality, where a file or folder can appear in more than one project. When a developer
checks in an updated master page file, the other projects will see the change immediately
(although this behavior is generally configurable). In production and test, each application would
need to be redeployed for the update master page to appear.
Finally, the VirtualPathProvider in ASP.NET 2.0 can serve files that do not exist on the file
system. With the VirtualPathProvider, a set of master pages could live in database tables that all
applications use.
Conclusions
The one point we should take away from this article is that we shouldn’t treat master pages as the
“masters”, but as just another control inside the page. Many design and runtime problems
become easier to solve with this method of thinking. We’ve seen how to handle events, how to
handle interactions in both directions, and how to avoid problems with JavaScript and relative
URLs. In all of these cases we can treat the master page as a user control inside the page, and
have a solid solution.

Master Pages in ASP.NET 2.0


So you have heard of Master pages but haven’t really gotten around to research what is all about?
If this is you, read on!
Master Pages enables you the developer to create a consistent look and feel for your web
application. For most of us, we either create a header and footer on each and every webpage or
we create a header and footer control and then add those controls to each and every .aspx page.
Ofcourse some might have seen the pitfall in this method and weaved their own custom page that
dynamically adds both the header and footer to their pages automagically.
Well the good news is this is now built in, and here is a quick and simply overview of how
MasterPages works in ASP.NET 2.0.
Master Pages
A master page is similar to a ‘.aspx’ page except that it has its own unique extension, ‘.master’.
Furthermore, a master page contains a new ‘Master’ directive, along with a ContentPlaceHolder
control.
The ‘master’ directive tells us that this page is a master page, and the ContentPlaceHolder
control is a placeholder (as the name suggests) for a content area where your content pages will
‘inject’ their content into the template. Keep in mind your master page can contain multiple
ContentPlaceHolder tags, your content pages will simply reference the ID of the
ContentPlaceHolder to ensure the content is injected into the correct area of the template.
So putting this knowledge to work, let’s create a very simply Master Page:
<%@ Master Language=”C#” %>
<script language=c# runat=server>
// your code here you wish!
</script>
<html>
<head><title>Master Pages Tutorial on CSharpFriends!</title>
<body>
<form runat=server>
<asp:ContentPlaceHolder ID=”ContentPlaceHolder1” runat=”server”>
<h1>Look Mom, default content that will render only if not overridden on the
content page!</h1>
</asp:ContentPlaceHolder>
</form>
</body>
</html>
So you will notice the master page has that master directive I mentioned earlier, along with the
ContentPlaceHolder. You will notice that within the ContentPlaceHolder control there is some
content. This content will render in any content page that doesn’t reference this
ContentPlaceHolder’s ID, so in effect you can have default content on all your content pages
which is overridden if you have content.
Once you have your master page, you obviously want to create content pages using your master
page as a template.
Your content page will be like any other .aspx page you have created in the past, except that it
will reference the master page in the Page Directive and it will contain a Content Control and
between this control will be all your content or at least the all the content for the corresponding
ContentPlaceHolder contron in your master page.
<%@ Page Language=”C#” MasterPageFile=”~/csharpfriends.master” %>
<asp:Content id=”content1” ContentPlaceHolderID=”ContentPlaceHolder1”
runat=”server”>
<h1>Look Mom, actual content on my content page! Looks like I’m going to
make it after all!</h1>
</asp:Content>
As I mentioned earlier, your master page can contain numerous content areas using
ContentPlaceHolder controls, just make sure to match up the ContentPlaceHolder’s ID in your
pages. Basically want happens is that when your .aspx page renders, it will cycle through all the
Content controls and match up the PlaceHolder in the master page.
Master Page Configuration Options
As it is now, it is possible to create content pages that don’t use the master page by simply NOT
referencing the master page. This could be something you want, or it could be something you
don’t want. To force pages to use the master page layout you can modify you web.config file by
adding this XML tag. Keep in mind that even though a master page is forced on all pages, if the
.aspx page references another master page file it will override your web.config settings.
<configuration>
<system.web>
<pages masterPageFile=”csharpfriends.master” />
</system.web>
</configuration>
This tutorial on master pages is for beginners, hings can get more complicated with master
pages, such as programmatically including master pages in your content pages, nesting master
pages and how events work etc. These topics might come in future articles so keep a eye out for
them.
Summary
In this brief article, I outlined the benefits of using Master Pages, the differences between a
master page and a regular aspx file along with a simple implementation and possible
configuration options you have.

How to: Create a Minimal Master Page...


One of the first tasks that you must complete when configuring a Microsoft Office SharePoint
Server 2007 Web site is to create one or more master pages. A master page contains references to
elements that you can share across multiple pages in an Office SharePoint Server 2007 site, such
as navigation, search controls, logon controls, and banner images. A master page can also contain
the cascading style sheet (CSS) and ECMAScript (JScript, JavaScript) references that define the
overall look and feel of your site. Commonly, every site—and therefore every page—in your site
collection uses the same master page to present a consistent user experience across the entire site
collection. Depending on your needs, you can use a different master page for one or for all of the
sites in your site hierarchy to distinguish the various areas of your portal.
Master Page Galleries
When you provision a site collection in Office SharePoint Server 2007, the system creates a
master page gallery that contains all the master pages and page layouts for that site collection. If
the site collection uses either the Publishing Portal or Collaboration Portal templates, the master
page gallery includes several master pages that are provided with Office SharePoint Server 2007,
such as BlueBand.master. These master pages are located in the path
C:\Program%20Files\Common%20Files\Microsoft%20Shared\web%20server%20extension
s\12\TEMPLATE\FEATURES\PublishingLayouts\MasterPages\, with other example .master
pages. You can use any of these master pages as they are, or you can customize them fully to
create unique branding for your site.
Why Start with a Minimal Master Page
Creating and completing a master page to begin your SharePoint site customization takes
planning and time. If you can, you want to prevent having to rewrite or back out code you don't
need in your master page. This topic shows you how to create a minimal master page that
includes only the minimal functionality that Office SharePoint Server 2007 requires so that you
have a stable platform upon which to build your own master pages. Creating a minimal master
page can help you avoid the time-consuming process of backing code out of a pre-existing
.master page such as BlueBand.master, or removing functionality and then building it back in
when your customization needs change again.

This topic supports using the Minimal Master Page described as a Site Master Page in Office
SharePoint Server 2007. It does not support using the Minimal Master Page described in this
topic as a System Master Page in Office SharePoint Server 2007. Using this content with
Windows SharePoint Services 3.0 is not explicitly supported.
You can, of course, create a master page from scratch. However, we generally do not recommend
this because a truly empty master page does not include all the content placeholders that the
Office SharePoint Server 2007 page model needs to work correctly.
The sample code in the following procedure includes only what the Office SharePoint Server
2007 page model requires—necessary content placeholders and controls to work with the page
layouts that are included in a default Office SharePoint Server 2007 installation. Office
SharePoint Server 2007 requires a master page that includes a title, branding, logon functionality,
search functionality, breadcrumb functionality, and basic structural elements such as page areas,
separators, borders, consoles, and description placeholders.
The following procedure uses Office SharePoint Designer 2007 as the master page design
environment. You can, however, use a text editor, a Web editor such as Microsoft Office
SharePoint Designer 2007, or an integrated development environment (IDE) such as Microsoft
Visual Studio 2005 to create a master page.
The master pages included with Office SharePoint Server 2007 are based on the
SPWeb.CustomMasterUrl property of the SPWeb class in Windows SharePoint Services.

To create a minimal master page


1. Open SharePoint Designer.
2. On the File menu, click New, point to SharePoint Content, and then click the Page tab.
3. Double-click Master Page to create a new master page.
4. Click Design to show the master page in design view. You should see header and left
margin areas and several content placeholders in the master page.
5. Click Code to show the master page in code view.
6. Copy the following code into the master page.
Xml

Copy Code
<%-- Identifies this page as a .master page written in Microsoft Visual
C# and registers tag prefixes, namespaces, assemblies, and controls. --
%>
<%@ Master language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="SPSWC"
Namespace="Microsoft.SharePoint.Portal.WebControls"
Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint"
Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages"
Namespace="Microsoft.SharePoint.WebPartPages"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingWebControls"
Namespace="Microsoft.SharePoint.Publishing.WebControls"
Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingNavigation"
Namespace="Microsoft.SharePoint.Publishing.Navigation"
Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="Welcome"
src="~/_controltemplates/Welcome.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="DesignModeConsole"
src="~/_controltemplates/DesignModeConsole.ascx" %>
<%@ Register TagPrefix="PublishingVariations"
TagName="VariationsLabelMenu"
src="~/_controltemplates/VariationsLabelMenu.ascx" %>
<%@ Register Tagprefix="PublishingConsole" TagName="Console"
src="~/_controltemplates/PublishingConsole.ascx" %>
<%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu"
src="~/_controltemplates/PublishingActionMenu.ascx" %>
<%-- Uses the Microsoft Office namespace and schema. --%>
<html>
<WebPartPages:SPWebPartManager runat="server"/>
<SharePoint:RobotsMetaTag runat="server"/>

<%-- The head section includes a content placeholder for the page
title and links to CSS and ECMAScript (JScript, JavaScript) files that
run on the server. --%>
<head runat="server">
<asp:ContentPlaceHolder runat="server" id="head">
<title>
<asp:ContentPlaceHolder id="PlaceHolderPageTitle"
runat="server" />
</title>
</asp:ContentPlaceHolder>
<Sharepoint:CssLink runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead"
runat="server" />
</head>

<%-- When loading the body of the .master page, SharePoint Server
2007 also loads the SpBodyOnLoadWrapper class. This class handles .js
calls for the master page. --%>
<body onload="javascript:_spBodyOnLoadWrapper();">
<%-- The SPWebPartManager manages all of the Web part controls,
functionality, and events that occur on a Web page. --%>
<form runat="server" onsubmit="return _spFormOnSubmitWrapper();">
<wssuc:Welcome id="explitLogout" runat="server"/>
<PublishingSiteAction:SiteActionMenu runat="server"/>
<PublishingWebControls:AuthoringContainer id="authoringcontrols"
runat="server">
<PublishingConsole:Console runat="server" />
</PublishingWebControls:AuthoringContainer>
<%-- The PlaceHolderMain content placeholder defines where to
place the page content for all the content from the page layout. The
page layout can overwrite any content placeholder from the master page.
Example: The PlaceHolderLeftNavBar can overwrite the left navigation
bar. --%>
<asp:ContentPlaceHolder id="PlaceHolderMain" runat="server" />
<asp:Panel visible="false" runat="server">
<%-- These ContentPlaceHolders ensure all default SharePoint
Server pages render with this master page. If the system master page is
set to any default master page, the only content placeholders required
are those that are overridden by your page layouts. --%>
<asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderPageTitleInTitleArea"
runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderPageImage" runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderBodyLeftBorder" runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderNavSpacer" runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderTitleLeftBorder" runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderTitleAreaSeparator"
runat="server"/>
<asp:ContentPlaceHolder ID="PlaceHolderMiniConsole" runat="server"/>
<asp:ContentPlaceHolder id="PlaceHolderCalendarNavigator" runat
="server" />
<asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat ="server"/>
<asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat
="server"/>
<asp:ContentPlaceHolder id="PlaceHolderBodyAreaClass" runat ="server"/>
<asp:ContentPlaceHolder id="PlaceHolderTitleAreaClass" runat ="server"/>
<asp:ContentPlaceHolder id="PlaceHolderBodyRightMargin" runat="server"
/>
</asp:Panel>
</form>
</body>
</html>
7. On the File menu, click Save As, provide a unique file name with the .master extension,
and then save the file to the master page gallery (/_catalogs/masterpage) in your site
collection.

Vous aimerez peut-être aussi