Customizing and Managing Your Site's Appearance / Part 3
Customizing and Managing Your Site's Appearance - Part 3
How Master Pages Work
When a page that uses a master page (i.e., a content page) is requested, ASP.NET merges the content page and master page together (assuming of course that both have already been compiled). It does so by inserting the master page's content at the beginning of the content page's control tree. This means that the master page content is actually a control that is added to the page. In fact, the master page control is a subclass of the
UserControl class (which is covered later in the chapter). Thus, the master page control is the root control in the page's control hierarchy. The content of each individual
Content control is then added as a collection of child controls to the corresponding
ContentPlaceHolder control in the master page control. This occurs after the
PreInit event but before the
Init event of the page.
Like any control, master pages have their own sequence of events that are fired as part of the page lifecycle. Because it is the root control in the page's control hierarchy, its events are always fired before the control events in any content pages. As a result, it is important that you do not conceptualize the master page as a "master" in the sense of a "controller" page. That is, the master page should not be orchestrating and controlling what happens in the individual content pages it contains. The master page is simply a template page and should control only those server controls it directly contains. Thus, in general, you should endeavor to keep the master page and the content pages as uncoupled as possible.
Referencing Issues with Master Pages
Because master page content is ultimately inserted into the content page's control tree, there are some potential issues to be aware of with external references inside of master pages. Because the user's request is for the content page and not the master page, all URL references are relative to the content page. This can cause some problems when the master page and the content pages are in different folders within the site.
For instance, let us imagine a situation in which your content page is in a folder under the root named
content and your master page is contained in a folder under the root named
master; inside this
master folder, you also have a subfolder named
images. Let's say you want to reference an image named
logo.gif, which is inside the
images folder of
master. In such a case, the following references inside your master page do not work.
The first two references in fact refer to
/content/images/logo.gif, because all relative references are relative to the content page. The second reference doesn't work either because the application relative symbol (
~) only works with server controls.
One alternative to this problem is to use an absolute reference to the site root.
The problem with this approach is its fragility; that is, the reference breaks if the site structure changes. Another approach is to use a server control, as in the following.
This approach works because server controls within master pages that contain URLs have their URLs modified by the ASP.NET environment.
Programming the Master Page
As you have seen, the combined master and content pages appear to the user as a single page whose URL is that of the requested content page. From a programming perspective, the two pages act as separate containers for their respective controls, with the content page also acting as the container for the master page. Yet, because the master page content becomes merged into the content page, you can also programmatically reference public master page members in the content page.
Why would you want to do this? Perhaps your master page contains a control whose content varies depending upon which content page is being viewed. One common example is a master page that contains an advertisement image; the precise advertisement to display in that control might vary depending upon which part of the site is being viewed. The master page might also contain a secondary navigation area that again differs depending upon which part of the site is being visited.
There are two principal ways of accessing content in the master page within the content page. You can do so via the
FindControl method of the
Master object or via public members that are exposed by the master page.
Let us begin by examining the
FindControl approach. The
FindControl method searches the current naming container for a specified server control and returns a typed reference to it. Thus, you can retrieve a
txtOne from the current Web Form via the following.
Notice that the reference returned from the method needs to be cast to the appropriate control type. Of course, it doesn't make too much sense to search the
Page naming container for the control when you could replace these two lines with the simpler form with which you are accustomed, namely
string contents = txtOne.Text. Where
FindControl is truly useful is in those situations where you need to reference a control that is "hidden" within some other naming container, such as a
GridView control or within a master page. For instance, imagine that you have the following
HyperLink control contained within your master page.
Now, let's say that you want to change the image and the destination URL for this control in each of your content pages. You could so with the following code somewhere in your content page's code-behind class.
Although this approach does work, it is not ideal. A better approach is to safely encapsulate the data you need from the master page into public properties, which you could then access within your content pages. This way, your content pages are not coupled to the implementation details of the master page. The following example adds two properties to the code-behind for the master page. It allows content pages to manipulate the image and navigation URLs of the
HyperLink control in the master page.
Your content pages can now use these properties; to do so requires the use of the
Master property of the
Page class. Unfortunately, you cannot simply reference one of these properties directly from the
Master property in your content page code-behind, as shown here.
Master.AdImageUrl = "~/Images/something.gif";
You cannot do this because the
Master property returns an object of type
MasterPage, which is the base class for all master pages. Of course, this general
MasterPage class knows nothing of the properties you have just defined in your master page. Instead, you must cast the
Master property to the class name of your master page, and then manipulate its properties.
An alternative to casting is to add the following
MasterType directive to your content pages.
This changes the
Master property of the
Page class so that it is strongly typed (that is, it is not of type
MasterPage, but of type
ProgrammedContentMaster). This eliminates the need for casting the
Master property, and thus you can manipulate the custom properties directly.