Migrating to ASP.NET 2 - Part 2 - Setup and Master Pages

This is the second part of a (long running) article series about migrating a .NET 1.x Web site to ASP.NET 2. This installment focuses on the general setup of the new web site and the Master Page I am going to use.

Read Part 1 - Introduction
Read Part 2 - Setup and Master Pages
Read Part 3 - Business Logic, Content Pages and User Controls
Read Part 4 - Implementing Custom Providers for Membership, Role Management and Profiles
Read Part 5 - It's the little things...

Introduction

This part of the series discusses the general setup of the site and Master Pages. A master page serves as a template that other pages in your site can be based upon. This ensures a consistent look and feel, while at the same time it greatly decreases development time and enhances the maintainability of your site.

I think Master Pages are one of the coolest of all the new ASP.NET 2 features, mainly because it was so hard to implement in ASP.NET 1.x. Many, many articles, blog entries, forum posts and code have been written about templates in ASP.NET 1.x. If you have the time (as in a couple of long winter nights) check out this long lasting thread at the ASP.NET Web site. It introduces a couple of different approaches for a template based architecture in ASP.NET 1.x. The thread started in June 2002 and people contributed to the thread until late 2004.

How Things Used to Be in my 1.x Website

For my current ASP.NET 1.x site I use a slightly modified approach originally developed by Jonathan Goodyear, which basically came down to this:

  • I created a new class called PageTemplate that inherited from System.Web.UI.Page
  • In this new class I defined some regions (HtmlGenericControls) for the main site elements, like the Main Menu, Site menu and so on.
  • The Page_Load event of this class always loaded the controls into the Main Menu and QuickStuff area.
  • Each page in my site inherited from the PageTemplate class:

    public class MyPage : PageTemplate
    {
    

  • On a page by page basis, I could overwrite the various sections defined in the template, or I could add additional content to each of the sections. For example, to load a voting control for a Content Item, I used the following code:

    SideBarVoting mySideBarItem = (SideBarVoting) 
                        LoadControl("/Controls/SideBarVoting.ascx");   
    mySideBarItem.ContentId = contentId;
    mySideBarItem.SiteContentType = siteContentType;
    mySideBarItem.CategoryId = categoryId;
    SideBar.Controls.Add(mySideBarItem)

This templating solution had quite a number of issues. First of all, there were problems with post backs and control state. (Some of these issues were fixed with a later version of the template solution. However, this approach used separate controls for each page used in the site, making it hard to apply the new template to existing pages).
Another big problem with templates in ASP.NET 1.x is designer support. The Visual Studio .NET Design View was not able to display the content that came from the template. Depending on the template solution you chose, you couldn't even use the Design View anymore.
Finally, because the template code was inside a base class, you needed to recompile your application anytime you wanted to make a change to the template. This isn't a very big problem, but I have wished for the possibility to make minor tweaks to a template on a production server a couple of times.

How Things Are Now in my 2.0 Website

Fortunately, all these issues are gone now. The new Visual Studio 2005 supports Master Pages in the Design View, allowing you to preview your page while you work with it. By changing a single .Master file you can influence the entire look and feel of your site. There is no need to recompile the entire application or the Class Library that holds the PageTemplate class.

If you need more in-depth information and examples about Master Pages in .NET, check out the links at the end of the article in the References section.

Creating the Master Page

In the next section I'll walk you through creating a new Web site and a master page with Visual Studio 2005. I am using the full version of Visual Studio, and not the Web Developer Express Edition. I also use C# as the main language for these articles, as my site is currently written in C#. I'll indicate how things look in Visual Basic .NET when there are large differences.

I'll show you how I migrated the look and feel from my current site to the new ASP.NET framework, and how I configured my site and pages to use Master Pages.

  1. Start by creating a new Web site in Visual Studio 2005. This is now very easy as there is no longer a notion of Projects, Solutions and Web applications that need to run under IIS. All you need to do is choose File | New | Web Site..., point to a folder on your local hard disk and you're set. Alternatively, you can create a new application using ftp, or you can point to a new or existing Web site using the http protocol. The latter works pretty much the same way as it did in Visual Studio .NET 2002 and 2003. It allows you to point to an URL like http://localhost and Visual Studio will create an application for you. Since debugging and previewing is done with a tiny web server that integrates with Visual Studio, you no longer need to have IIS installed on your local development workstation. If you want to deploy your application you still need IIS of course.
  2. Once the new site has been created, delete the default page Default.aspx.
  3. With the basic site set up, create a new master page by right-clicking the site in the Solution Explorer and choosing Add New Item... Name the master page MainMasterPage.master, select C# as the language and make sure Place code in separate file is checked. Once you click the Add button, you'll be presented with a nice and clean, XHTML compatible ASPX page.
  4. The next step is to define the layout for your site. In my case, things were very simple as I just needed to copy the contents of one of my current pages to this new master page, and remove the page specific stuff I didn't want in my master page. I took an empty page from my current Web site, and removed all the stuff I didn't need. All I left in was the <head> section with links to external style sheets and JavaScript files, and my basic <div> based page structure.
    In your case, you may need to think a bit about page design, navigation structure, look and feel (colors, images, fonts etc) and how you set up the various sections in your page (using HTML tables, or a more modern <div> based approach). Either way, don't be afraid to make mistakes. It's very easy to change the master page at a later stage and automatically have the changes applied to all pages that use the master page.
    After a bit of prototyping, I ended up with the following basic page layout:

    <%@ Master Language="C#" AutoEventWireup="true" 
             CodeFile="MasterPage.master.cs" 
             Inherits="Spaanjaars.Imar.Web.MasterPages_MasterPage" %>
    <%@ Register Src="~/Controls/MainMenu.ascx" 
             TagName="MainMenu" TagPrefix="isp" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
      <title>Imar.Spaanjaars.Com</title>
      <link rel="stylesheet" type="text/css" href="~/Css/Core.css" />
      <link rel="stylesheet" type="text/css" href="~/Css/Styles.css" />
      <link rel="stylesheet" type="text/css" href="~/Css/PrintStyles.css" 
           media="print" />
      <script type="text/javascript" src="/JavaScripts/ClientScripts.js">
      </script>
    </head>
    <body>
    <form id="form1" runat="server">
      <div id="PageWrapper">
        <div id="Logo">
          <div id="ImageLogo">
            <a href="~/" runat="server" id="lnkLogoHome">
              <img src="~/Images/Logo.jpg" runat="server" 
                   alt="Spaanjaars.Com logo" />
            </a>
          </div>
          Quick Stuff Here
        </div>
        <div id="MainMenu" class="MainMenu" runat="server">
          <isp:MainMenu ID="MainMenu1" runat="server" />
        </div>
        <div id="SubMenu">
          Sub Menu Here
        </div>
        <div id="SideBar">
          <asp:ContentPlaceHolder ID="SideBarPlaceHolder" runat="server" />
    	   </div>
        <div id="MainContent">
          <asp:ContentPlaceHolder ID="MainContentPlaceholder" 
               runat="server" />
        </div>
      </div>
    </form>
    </body>
    </html>

  5. What's good to know up front are the various "parts" of your site, like menus and content areas. In my site I have 6 different sections as you can see from the following figure:

    Important Sections in the Page Layout
    Figure 1: Important Sections in the Page Layout

    Two of these areas can be modified by the pages that implement this master page. The SideBar and the Main Content area are both sections that are likely to change on a page by page basis. The other four can be implemented as User Controls. Although their content is likely to change to reflect the content being displayed, it's convenient to implement these as static controls that don't need to be changed by the final page. I am going to make the User Controls "aware of their environment", so they are able to display and select the right menu items. I'll discuss this in a future installment of this article series.

    To enable pages based on the master page to change the content of these two areas, you'll need to add a ContentPlaceHolder for each of these areas. To do this, drag a ContentPlaceHolder control from the Toolbox (under Standard) and drop it at the location where you want the area to start. In my case, this is between the <div> tags that make up the SideBar and the MainContent areas. Rename the ID of the control to something more logical. In my case, I ended up with this code:

    <div id="SideBar"><asp:ContentPlaceHolder 
    ID="SideBarPlaceHolder" runat="server"></asp:ContentPlaceHolder></div>
    <div id="MainContent"><asp:ContentPlaceHolder ID="MainContentPlaceholder" runat="server"></asp:contentplaceholder></div>

  6. Once you have added the ContentPlaceHolders, you can switch to Design View and see how things end up. In my case, the Design View looked like this:

    Design View of the Master Page
    Figure 2: Design View of the Master Page

    The two yellow areas are the ContentPlaceHolders that can be overridden by pages implementing this master page.
    By default, these ContentPlaceHolders are empty, but you can add any default content you want (including ASP.NET controls).

  7. For now, that's all I am going to put in the master page. The other four sections, including the menus, will be filled by loading user controls into the respective site areas. This will be discussed in part 3 of this article series.

Using the Master Page

With the master page done, it's time to actually use it. To tell one or more pages in your site to use a specific master page, you have a couple of methods available:

Method Description
Page Based

On each page that uses the Master Template you can set set the MasterPageFile attribute of the Page directive, for example:
MasterPageFile="~/MainMasterPage.master"

This attribute overrides any master page set for the site in the Web.Config file.

Application Based Through the Web.Config file you can apply a specific master page to all pages in your site. To do so, you'll need to add a <pages> node to the <system.web> section of the config file and set the masterPageFile attribute to the master page you want to use like this:

<pages masterPageFile="~/MainMasterPage.master" />

Note that, just like all other settings in the Web.Config file, the masterPageFile attribute is case-sensitive.
Programmatically In the OnPreInit event you can override (or set) the master page with the following code:

protected override void OnPreInit(EventArgs e)
{
  this.MasterPageFile = "~/GreenMasterPage.master";
  base.OnPreInit(e);
}


In most circumstances, I would stick to method 1 and not use method 2 and 3. While they give you a great deal of flexibility, they are also likely to give you some trouble if you don't fully understand their implications. For an ASPX page to implement a master page, it has to have Content controls with IDs that match with those found in the master page. So when you have a content page that was initially created for master page A, and then at run time (through the Web.config file or programmatically) use master page B, it's likely you'll end up with a run-time error about ContentPlaceHolders that cannot be found.
You also loose design support when you don't explicitly specify the master page in the content page.

IMO, it's better to specify the master page on each content page and keep the other solutions for some rare occasions you might need them. If you are going to apply the master page to the entire site or programmatically anyway, make sure you know what you're doing and check and double check that each master page uses the same ContentPlaceHolders so the master pages are interchangeable.

So, how do we apply the master page explicitly? This is very easy as Visual Studio allows you to select a master page when you add a new content page to your site. Follow these steps to add a new file and select the master:

  1. Right-click your site in the Solution Explorer and choose Add New Item...
  2. Choose Web Form from the Visual Studio installed templates section and give the page a name. Make sure Place code in separate file and Select master page are both checked and click Add.
  3. Once you click Add, you get the chance to select the master page for this file. Select MainMasterPage.master from the list on the right and click OK. The page will be added to your web site. To see that the master page has been applied choose View | Designer to switch to the Design View. You'll see the layout you defined in the master page grayed out, while the Content areas are editable:

    Figure 3: A Content Page using the MainMasterPage master

  4. Add some content to the Content placeholders. You can add anything you like, from static text to ASP.NET controls that you can drag from the Toolbox into the Content regions. Once you're happy with the page layout, press Ctrl+F5 to view the page in the browser. In my case, I ended up with something like this:

    Figure 4: The Content Page in the browser.

    It's still a bit boring right now and the page doesn't do much, but it's a nice start.

  5. If you look at the code for the page you can see how Visual Studio hooked up the Content page to the master page:

    <%@ Page Language="C#" 
      MasterPageFile="~/MasterPages/MasterPage.master" 
      AutoEventWireup="true" CodeFile="Default.aspx.cs" 
      Inherits="Spaanjaars.Imar.Web._Default" Title="Imar.Spaanjaars.Com" %>
    The MasterPageFile attribute points to the master page that you chose in step 3. If you want to change the master page at a later stage, click somewhere between the quotes for the master page file and press Ctrl+Space. This brings up a menu from which you can choose Pick Master... and then browse to a new master page in your site. Alternatively, you can click somewhere in the <%@ Page directive and then use the Property grid to browse to a new master page.

Generic BasePage

In addition to the useful MasterPage to define general look and feel, I also created a base page that inherits from System.Web.UI.Page. All my content pages in turn then inherit from this custom BasePage class. Currently, the BasePage class is still empty, but during future modifications of the site, I can easily add properties and methods that apply to each and every page in my site. The code download that comes with this article shows how this works; you find BaseClass.cs in the special App_Code folder. In ASP.NET 1.x, inheriting from a custom BasePage was possible as well, but it meant broken design time capabilities and a host of other problems. In ASP.NET 2.0, this is now both very easy to do, and implemented very well, providing you with a great design-time experience, including full IntelliSense for the base page class.

Conclusion

Master Pages are a great new feature of ASP.NET 2.0. They are very easy to work with, yet they are very powerful. The current master page doesn't do very much; all it does is provide the general layout for pages that use this master. In a future article in this series I'll enhance my master page by loading User Controls for the QuickStuff, Main Menu and Sub Menu areas. Stay tuned....

References

Download Files

Source Code for this Article
(Note that the user control is implemented rather messy; haven't had the time to clean it up)

Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Wednesday, May 04, 2005 11:44:21 AM Suganya said:
Hi,

      I am using asp.net beta version.In that iam trying to use the menu sontrol.Everything going well.but the sub menu's in the menu control is diplaying in center alignment.help me to make the sub menu items in left alignment.

urgent..pls help me
On Wednesday, May 04, 2005 5:22:46 PM Imar Spaanjaars said:
Hi Suganya,

For technical questions *not* directly related to this article, please check out a forum like the one at http://p2p.wrox.com

There are many people there that can help you with this.....

Cheers,

Imar
On Wednesday, August 10, 2005 3:33:52 AM Ali said:
Thanks Imar ,

I've been looking for a real start in Master Pages ...
This article is the best start that cover all important aspects of Master Pages especially the way you describe how design layout can be !

For me - as a new developer - it is inspiring ..

The article series will add more value if you could provide a source code for this example or similar one ..

Thanks Again
On Wednesday, August 10, 2005 10:13:40 AM J.Hart said:
Hi ,

how to pass a value from the search textbox when button is clicked (both controls available in a Master Page).
I wanna pass it to a label inside a content page.

I could only read the textbox value by clicking a button available in the content page not the master.
I want the button of master page to display Textbox's text inside the content ?

Any idea is appreciated !
On Wednesday, August 10, 2005 6:00:07 PM Imar Spaanjaars said:
Hi J.Hart,

There are a couple of ways around this:

1. You could use Response.Redirect in the control and pass the text box values in the Query String. This is how I do it on my current site.

2. A better solution is to create an event in the control that the content page registrers to. Create an event like OnSearch which is triggered by the button in the control. Then the content page gets notified by the event, and can read either a public property on the control, or get information from the event argument passed to the event.

Take a look at this search query in Google: http://www.google.com/search?hl=en&lr=&c2coff=1&q=event+delegate+ASP.NET+control+custom

Among the first few resuls you'll find some useful articles about events and delegates in .NET

HtH,

Imar
On Wednesday, August 10, 2005 6:01:25 PM Imar Spaanjaars said:
Hi Ali,

Good suggestion. It's been a while since I wrote this article (working on a book about ASP.NET 2.0) so I don't have the source ready for posting here. However, I'll look it up, zip it in and add it to the article.

Cheers,

Imar
On Saturday, January 14, 2006 4:07:36 PM Imar Spaanjaars said:
For those of you that have been following this article series: I just posted part 3: http://Imar.Spaanjaars.Com/QuickDocId.aspx?QUICKDOC=378

I am working on follow up articles now as well, so there's more to come. If you have requests about specific topics, let me know through my Contact page.

Sorry all for the long delay; I promise it won't take another year before I post the next article....

Cheers,

Imar
On Wednesday, April 26, 2006 3:05:16 AM Bijan G. said:
The real world implementation of master pages and nested master pages are not practical and the html code will become ugly very soon.  You have to find your way among many lines of brutal, superfluous html markup.  It’s very tedious to develop and maintain master and nested master pages even when you turn on document outlining.   YOU WILL NOT BE ABLE TO CLICK ON THE DESIGN TAB OF NESTED MASTER PAGES!  SO THERE GOES YOUR GUI!!!!

The idea of master pages is great, but it's implemented very poorly by Microsoft.
On Wednesday, April 26, 2006 7:00:00 PM Imar Spaanjaars said:
While it's not as good as true design time support, you may want to take a look at this trick:

http://weblogs.asp.net/scottgu/archive/2005/11/11/430382.aspx

Also, I am not sure I agree with your other statements regarding master pages. I understand the limitations that you mention, but personally, I find that they solve a whole lot more problems than they cause. Granted, you need to know how to work around some of their peculiarities, but after that, I find them really useful....

Imar
On Thursday, July 13, 2006 8:27:01 AM Martin said:
Hi I'm quite interrested in how you would do this in a proper way:

"I am going to make the User Controls "aware of their environment", so they are able to display and select the right menu items. I'll discuss this in a future installment of this article series. "

Have you made any articles conserning this topic? Or know of any?

Thanks for some good articles.
//Martin
On Friday, July 14, 2006 6:03:51 PM Imar Spaanjaars said:
Hi Martin,

Funny that you mention this; I was talking with a colleague about writing an article about configurable user controls. Had forgotten I promised such a thing over here.

Might take a while, though, as I am pretty occupied at the moment. Will post a link here when the article is done...

Cheers,

Imar
On Sunday, July 23, 2006 4:38:58 PM Imar Spaanjaars said:
Hi Martin,

I just published part 1 of a short article series about configurable User Controls. You can find it here: http://Imar.Spaanjaars.Com/QuickDocId.aspx?quickdoc=400

Comments, questions and topic requests are welcome....

Cheers,

Imar
On Sunday, December 02, 2007 9:43:10 PM Matt said:
Hi there I sent you an email. I need to build a login page and registration with the behind code like yours for fun. Can I get an example. I check your site, it is very well done. Please respond,
On Sunday, December 02, 2007 11:19:03 PM Imar Spaanjaars said:
Hi Matt,

What is it that you are looking for that you didn't find in my answer by e-mail?

My site is not open source so I am not going to give it to you, if that's what you are asking. If you want to know how it's done, get a copy of my book Instant Results and look at the CMS chapter.

Cheers,

Imar
On Sunday, December 02, 2007 11:38:18 PM Matt said:
What book is it I will buy it right away.

On Sunday, December 02, 2007 11:46:32 PM Imar Spaanjaars said:
It's called ASP.NET 2.0 Instant Results. Check it out on the About page: http://imar.spaanjaars.com/AboutMyBooks.aspx?aboutitem=instantresults

My site has a lot more features than presented in the CMS in the book, but you should get a good idea about security, content management and so on.

Cheers,

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.