Click here to Skip to main content
15,916,945 members
Articles / Web Development / ASP.NET
Article

Inheritance and Nested Master Pages

Rate me:
Please Sign up or sign in to vote.
3.69/5 (6 votes)
10 May 20071 min read 76.5K   518   22   8
An article is about editing inherited properties of top level master page from .aspx code

Introduction

This article is about small problem I got stuck when I tried to use inheritance with Nested Master Pages. If you looking for something general about Master Pages please try to see this article Inside Master Pages

Problem description

Well, here is my situation

Screenshot - NestedMasterPages.gif

As you can see I try to inherit Child.Master from Main.Master and then use Child.Master in MyPage.

Main.master

HTML
<%@ Master Language="C#" AutoEventWireup="true" 
    CodeBehind="Main.master.cs" Inherits="Master.Main" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
       "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head id="mainHead" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="mainForm" runat="server">
        <asp:Literal ID="TopMessageLiteral" runat="server" />
        
        <div id="content">           
            <asp:contentplaceholder id="cphMain" runat="server" />
    </div>
    </form>
</body>
</html>

Child.master

HTML
<%@ Master Language="C#" MasterPageFile="~/Master/Main.master" AutoEventWireup="true" 
    CodeBehind="Child.master.cs" Inherits="Master.Child" %>
<%@ MasterType VirtualPath="~/Master/Main.master" %>
<asp:Content ID="cntMain" ContentPlaceHolderID="cphMain" Runat="Server">
    <div id="left">       
        <asp:contentplaceholder id="cphLeftCol" runat="server" />
    </div>  
    
    <div id="right">
        <asp:contentplaceholder id="cphRightCol" runat="server" />    
    </div>
</asp:Content>

MyPage

HTML
<%@ Page Language="C#" MasterPageFile="~/Master/Child.master" AutoEventWireup="true" 
    Inherits="MyPage" Title="Untitled Page" Codebehind="MyPage.aspx.cs" %>
<%@ MasterType VirtualPath="~/Master/Child.master" %>

<asp:Content ID="cntLeftCol" ContentPlaceHolderID="cphLeftCol" Runat="Server">
    <p>test left</p>
</asp:Content>
<asp:Content ID="cntRightCol" ContentPlaceHolderID="cphRightCol" Runat="Server">
    <p>test right</p>
</asp:Content>

Code-behinds please see below:

C#
//Main.master
//-------------
public partial class Main : System.Web.UI.MasterPage {
  
  private string _topMessage;

  public string TopMessage {
    get { return _topMessage; }
    set { _topMessage = value; }
  }

  protected void Page_Load(object sender, EventArgs e) {
    TopMessageLiteral.Text = TopMessage;
  }
}

//Child.master 
//------------
public partial class Child : Main {

   protected new void Page_Load(object sender, EventArgs e) {
               
   }

}

//MyPage.aspx 
//----------
public partial class MyPage: System.Web.UI.Page {

  protected void Page_Load(object sender, EventArgs e) {
    Master.TopMessage = "My top message";
  }
}
It looks like good code but it doesn't work. My TopMessage will never be shown. If you try to debug this code you'll see that when you instantiate TopMessage property in MyPage.Page_Load all is going well and value is successfully saved to _topMessage variable. But when it will be read from Main.Page_Load it is empty. Frankly, I don't know why. I have some guesses only. But it seems I found solution.

Solution

The trick is to use Context.Items collection.
C#
//Main.master
//-------------
public partial class Main : System.Web.UI.MasterPage {
  
  public string TopMessage {
    get {
       if (Context.Items["_topMessage"] != null)
          return Context.Items["_topMessage"].ToString();
       else
          return string.Empty;
    }
    set {
        Context.Items["_topMessage"] = value;
    }
  }

  protected void Page_Load(object sender, EventArgs e) {
    TopMessageLiteral.Text = TopMessage;
  }
}
In this way TopMessage value will never be lost.

One More Solution

Suggested by Irwan Hassan technic need not Main.master <- Child.master inheritance

C#
//MyPage.aspx 
//----------
public partial class MyPage: System.Web.UI.Page {

  protected void Page_Load(object sender, EventArgs e) {
    Master.Master.TopMessage = "My top message";
  }
}

History

May 09, 2007: just add a piece of code to play experiments

May 10, 2007: add one more solution

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThere's a built-in way to achieve this: Pin
Jim Raley9-Dec-08 7:03
Jim Raley9-Dec-08 7:03 
GeneralA 5 from me! Pin
Andrei Ion Rînea12-Oct-07 6:14
Andrei Ion Rînea12-Oct-07 6:14 
GeneralTry Master.Master.TopMessage = ... Pin
Irwan Hassan9-May-07 7:23
Irwan Hassan9-May-07 7:23 
GeneralRe: Try Master.Master.TopMessage = ... Pin
Bobac10-May-07 2:54
Bobac10-May-07 2:54 
GeneralRe: Try Master.Master.TopMessage = ... Pin
Irwan Hassan10-May-07 3:51
Irwan Hassan10-May-07 3:51 
GeneralEvent firing sequence Pin
Brian Lowe7-May-07 22:05
Brian Lowe7-May-07 22:05 
But when it will be read from Main.Page_Load it is empty. Frankly, I don't know why. I have some guesses only.

I have a guess too: Check out the event sequencing for the classes.

Asp.NET collects together all of the objects that will make up your final page and fires child events before the equivalent parent event. So the Page_Load() event in the child object gets fired before the Page_Load() in its parent, etc.

If you need code in a master to make use of data initialized in a child, or vice versa, you must take care to use events in the right sequence. Each page has PreInit(), Init() and Load() events, and many others.

Unfortunately I have not been able to find a definitive list telling the absolute sequence of when each event gets fired, especially when considering nested classes or objects, but I have been able to find my own sequence by inserting Trace.Write() statements into lots of event code and then watch the sequence by setting Trace="true" in the page while debugging.

e.g.
class Main<br />
{<br />
  Page_PreInit() { Trace.Write("Main PreInit()"); }<br />
  Page_Init() { Trace.Write("Main Init()"); }<br />
  Page_Load() { Trace.Write("Main Load()"); }<br />
}<br />
<br />
class Child<br />
{<br />
  Page_PreInit() { Trace.Write("Child PreInit()"); }<br />
  Page_Init() { Trace.Write("Child Init()"); }<br />
  Page_Load() { Trace.Write("Child Load()"); }<br />
}


This is commonly needed when you're using user controls in a page and you need to make sure the control data gets initialized. You would use the Page_Init() event of the main page to set up data values that are referenced by the user controls' Page_Load() because all their Page_Load() events get fired before the main Page_Load() event.

I hope this helps.


Brian
----@

AnswerRe: Event firing sequence Pin
Bobac8-May-07 13:19
Bobac8-May-07 13:19 
GeneralRe: Event firing sequence Pin
Brian Lowe9-May-07 3:43
Brian Lowe9-May-07 3:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.