Handling the PreInit Event in Master Page

ASP.NET 2.0 introduces a new concept known as Master Pages, in which you create a common base master file that provides a consistent layout for multiple pages in your application. To create a Master Page, you identify common appearance and behavior factors for the pages in your application, and move those to a master page. In the master page, you add placeholders called ContentPlaceHolders where the content (child) pages will insert their custom content. When users request the content pages, ASP.NET merges the output of the content pages with the output of the master page, resulting in a page that combines the master page layout with the output of the content page. In this article, you'll take a look at the theory behind the master pages and see how to leverage the new Master Pages feature in a Web application.

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 = "~/myMasterFile";
}

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>
Tags: , , ,
Hot on Web:


About author