Main Points

Master Page Header

This page was pretty easy to throw together. I went back to the current template, KeyboardAccessible.aspx, and copied everything into the master page. I then used only two points for pulling things out of the master page. First, anything that didn't change from page to page stayed in the master page. Anything that did, I placed an <asp:contentplaceholder> tag above it and named the id attribute appropriately. Then I flipped over to the content page and inserted an <asp:content> tag directed to it. Finally, I grabbed the section that changes from the master page and pasted it into the <asp:content> tag.

Second, sections had to be semantically intact. Absolutely NO opening tags in the master page whose closing tags are in the content page or vice versa. That's unstable and places fairly strict limits on what changes you can and can't make in the future. I had played around with Dreamweaver templates at one point and not been impressed; however, I think that I had made this mistake, and I was determined not to make it again. And that's it, that's all it took to make an ASP.NET master page.

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage02Template.master.cs" Inherits="MasterPage02Template" %>
<!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" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="robots" content="all, index, follow" />
<asp:contentplaceholder id="cphMetaTags" runat="server">
<meta name="description" content="Welcome to Earthchronicle.com Beta! The behind the scenes testing for your online education resource for history." />
<meta name="keywords" content="Earthchronicle, Earth, Chronicle, earth, chronicle, EarthChronicle.com, Beta, beta, Home, Page, Homepage, History, history, Historical, historical, Online, online, On-line, on, line, Education, Educational, education, educational, resource, Resource, test, testing, new features, next, version, generation, website" />
</asp:contentplaceholder>
<link rel="shortcut icon" href="Images/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" type="text/css" media="all" title="ecstylesheet" href="CSS/ecMaster.css" />
<asp:contentplaceholder id="cphTitleAndExtraCss" runat="server" />
</head>
<body>
... Body Content ...
</body>
</html>

Here is the complete page headers. The @ Master directive, the !DOCTYPE, the <html>, <head>, and <meta> tags are all defined in the master page. Only the description and keywords <meta> tags are wrapped in an <asp:contentplaceholder> tag. Naturally, these should be defined on every page, but historically I've been quite lazy about it. So in case, I don't take the time to include these for the page, a generic set is queued up to auto populate from the master page. If I do it like I'm supposed to, this code will be wiped as if it weren't there; this is just a backup. Here's the corresponding code in the content page...

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MasterPage02Structure.aspx.cs" Inherits="MasterPage02Structure" MasterPageFile="~/MasterPage02Template.master" %>
<asp:content ContentPlaceHolderId="cphMetaTags" runat="server">
<meta name="description" content="Welcome to Earthchronicle.com Beta! The behind the scenes testing for your online education resource for history. This is our attempt to create a working template using ASP.NET 2.0 master pages. We're specifying the Page Header and the general document structure." />
<meta name="keywords" content="Earthchronicle, Earth, Chronicle, earth, chronicle, EarthChronicle.com, Beta, beta, Home, Page, Homepage, History, history, Historical, historical, Online, online, On-line, on, line, Education, Educational, education, educational, resource, Resource, test, testing, new features, next, version, generation, website, master, pages, Master Page, content page, Template, Content, ContentPlaceHolder, ASP.NET, ASP.NET 2.0, header, Header, structure, Structure, Document, document" />
</asp:content>

The content page starts with the @Page directive. It specifies the language (which is C#) and whether Event names match their function (true in this case). The standard CodeFile and Inherits attributes which connect the page to its code behind file as well as the MasterPageFile attribute which identifies which master page to use with this page. Then the first <asp:content> tag inserts the description and keywords meta tags for this page. While they are part of the same section of the website and therefore very similar to the generic meta tags in the master page, there is additional information at the end of each content attribute.

I also insert the favicon, the main CSS file, and define a <asp:contentplaceholder> tag to insert the <title> tag. I can also use it to insert any special CSS files since they come after the main file and by the cascade will override those settings. Here are the <asp:content> tags which are inserted at this point...

<asp:content ContentPlaceHolderId="cphTitleAndExtraCss" runat="server">
<title>2. Master Page Template Code</title>
</asp:content>
<asp:content ContentPlaceHolderId="cphMainTitle" runat="server">
<h1>2. Master Page Template Code</h1>
</asp:content>

The only tag we add in this section is the <title> tag. There is no additional CSS file inserted here, however, we would simply need to insert the <link> tag at this point and we could easily do so. Note also that the next <asp:content> tag comes immediately after it in order to keep these items close together because I like to keep the main <h1> heading and the page title the same. One of the nice things about master pages, is that even though the content is not going to wind up close to each other on the page, I can place the <content> tags right next to each other so that it's easy to make them the same and for maintenance to confirm that they are identical. This is a fantastic trick that ASP.NET lets you pull off.

Skip to Main Points

Master Page Document Header

<body>
<form runat="server" action="" id="aspNetForm">
<!-- THIS IS THE HEADER SECTION -->
<div id="headerWrapper">
<div id="header">
<!-- This Section is for the Title -->
<asp:sitemappath id="smpBreadcrumb" runat="server" cssclass="breadcrumb"></asp:sitemappath>
<asp:contentplaceholder id="cphMainTitle" runat="server" />
<div id="clockBox">
History is Now
</div>
<asp:contentplaceholder id="cphPageBackForward1" runat="server" />
</div>
</div>
<div id="headerSpacer" class="spacer"></div>
... Main Body Content ...
</form>
</body>

Here are the <body> and <form> tags necessary to define the content of the result page and give ASP.NET the ability to interact with it. Then I set up the document header. It includes a <asp:contentplaceholder> for the title (shown in the section above) and another for the page forward and page back links. There's also the sitemappath control to provide the breadcrumb navigation and the div for the clock. If the page won't need forward and back buttons, then I leave that <asp:content> tag out of the content page. Since there's no default code inside it - in fact it's a self-closing tag - this is skipped on the result page unless the content page specifically includes that information. This page does include forward and back links so the content page code looks like this...

<asp:content ContentPlaceHolderId="cphPageBackForward1" runat="server">
<div class="pageBack">
&lt;-- Back to <a href="MasterPage01Testing.aspx">1. Master Page Test</a>
</div>
<div class="pageForward">
Continue to <a href="MasterPage03FooterCode.aspx">3. Master Page Footer Code</a> --&gt;
</div>
</asp:content>
<asp:content ContentPlaceHolderId="cphPageBackForward2" runat="server">
<div class="pageBack">
&lt;-- Back to <a href="MasterPage01Testing.aspx">1. Master Page Test</a>
</div>
<div class="pageForward">
Continue to <a href="MasterPage03FooterCode.aspx">3. Master Page Footer Code</a> --&gt;
</div>
</asp:content>

Note that I've pulled the same trick again. The forward back links are at opposite ends of the page at the top and again at the bottom. However, using master pages I'm able to insert each section with an <asp:contentplaceholder> tag. The <asp:content> tags which insert the sections can live right next to each other. While it'd be nicer to simply refer to the same chunk of code and have it appear in both places, this at least makes managing that section manageable.

Skip to Main Points

Main Content

<!-- THIS IS THE MAIN BODY SECTION -->
<div id="mainBodyWrapper">
<div id="mainBody">
<!-- This Section is for the Main Points Boxout -->
<div id="mainPointsBoxoutWrapper">
<div id="mainPointsBoxout">
<h2 id="mainPoints" tabindex="0">
Main Points
</h2>
<ul>
<asp:contentplaceholder id="cphSection1Link" runat="server" />
<asp:contentplaceholder id="cphSection2Link" runat="server" />
<asp:contentplaceholder id="cphSection3Link" runat="server" />
<asp:contentplaceholder id="cphSection4Link" runat="server" />
<asp:contentplaceholder id="cphSection5Link" runat="server" />
<asp:contentplaceholder id="cphSection6Link" runat="server" />
<asp:contentplaceholder id="cphSection7Link" runat="server" />
<asp:contentplaceholder id="cphSection8Link" runat="server" />
<asp:contentplaceholder id="cphSection9Link" runat="server" />
<asp:contentplaceholder id="cphSection10Link" runat="server" />
<li class="mainPointsSkip">
<sup><a href="#mainPoints">Skip to Main Points</a></sup> links return to the Main Points menu.
</li>
</ul>
</div>
</div>
<div id="mainPointsBoxoutSpacer" class="spacer"></div>
<asp:contentplaceholder id="cphSection1" runat="server" />
<asp:contentplaceholder id="cphSection2" runat="server" />
<asp:contentplaceholder id="cphSection3" runat="server" />
<asp:contentplaceholder id="cphSection4" runat="server" />
<asp:contentplaceholder id="cphSection5" runat="server" />
<asp:contentplaceholder id="cphSection6" runat="server" />
<asp:contentplaceholder id="cphSection7" runat="server" />
<asp:contentplaceholder id="cphSection8" runat="server" />
<asp:contentplaceholder id="cphSection9" runat="server" />
<asp:contentplaceholder id="cphSection10" runat="server" />
<asp:contentplaceholder id="cphPageBackForward2" runat="server" />
</div>
</div>
<div id="mainBodySpacer" class="spacer"></div>

Here's the guts of the page. Again, note how simple it is. There's a mainBody div surrounded by a wrapper and followed by a spacer div. Inside the mainBody div is the mainPointsBoxout div which contains ten <asp:contentplaceholder> tags. These <asp:contentplaceholder> tags contain the code for the main points links, and each one corresponds to one of the ten <asp:contentplaceholder> tags in the mainBody div. This content can then be specified in the content page. But in the master page it takes as much space to include all the main points links as it does to include all the content that it links to. Finally, I've thrown a page forward/page back control <asp:contentplaceholder> at the end so that it can be easily inserted - and of course, ignored if it's not wanted. It's at the opposite end of the master page from the page forward/back yet on the content page they're right next to each other. Here are the <asp:content> tags for the main sections...

<asp:content ContentPlaceHolderId="cphSection1Link" runat="server">
<li>
<a href="#masterPageHeader">Master Page Header</a>
Description of the code for the structure of the master page header.
</li>
</asp:content>

The cphSection1Link is pulled into the master page inside a list. Therefore this is a list item with <li> tags. The list item consists of an in page link with a descriptive title, and a short blurb to give more information about the topic of the section. It links to content section, cphSection1; note how the link <a href="#masterPageHeader"> is matched to the h2 heading, <h2 id="masterPageHeader" tabindex="0">. This connects them as an in page link. There's also a tabindex="0" to fix a bug; without it, in pages links don't work for keyboard users in IE.

<asp:content ContentPlaceHolderId="cphSection1" runat="server">
<!-- This Section is for the First Point -->
<div id="section1Wrapper">
<div id="section1">
<h2 id="masterPageHeader" tabindex="0">
Master Page Header
</h2>
... Body Content ...
<sup><a href="#mainPoints">Skip to Main Points</a></sup>
</div>
</div>
<div id="section1Spacer" class="spacer"></div>
</asp:content>

The basic layout of the content sections is fairly simple. One primary and one wrapper div enclose all the content. Inside it is the section <h2> heading, the content and finally the "Skip to Main Points" link. Following the double div is a spacer div to allow space to be added between sections (anything from virtually nothing to borders, images, or blank space).

I'm gaining a healthy appreciation for the flexibility of master pages. The beautiful thing about them is that you can insert all kinds of <asp:contentplaceholder> tags into the master page without actually adding content. Unless there's an <asp:content> tag in the content page, there's no code when the master page is included in. So by adding <asp:contentplaceholder> tags for contingencies, you can actually use one master page to create a number of different structures. This reduces the number of master pages that you need to use. While you can use as many master pages as you want, pages that share the same basic structure but with variations can still share the same master page. My page forward and back links are simply a <asp:contentplaceholder> tag inserted where those need to appear in the page. If I want them, I insert the <asp:content> tags with the necessary XHTML code into the content page and they appear. If I don't want them, I leave the <asp:content> tags out of the content page and there's no code to tell you, the master page can even had that code.

Skip to Main Points
<-- Back to 1. Master Page Test
Continue to 3. Master Page Footer Code -->
<---- Jump back to Master Page Development