Implementing Page Inheritance: The Render Method...

One of the biggest improvements that was developed for the .NET framework is page inheritance. It is a very useful and dynamic approach when developing web sites.


By: Brian Mains Date: March 3, 2004 Download the code.

One of the biggest improvements that was developed for the .NET framework is page inheritance. It is a very useful and dynamic approach when developing web sites. There are two methods used for page inheritance: the Render and OnInit methods. The first method, which will be discussed in this article, outputs the content through an System.IO.HTMLTextWriter object, which also has support for many characters normally used in encoding HTML tags. The OnInit method outputs content to the screen by adding controls to a collection object. ASP.NET server controls can be added to a web form, as well as simple HTML text (through the System.Web.UI.LiteralControl class).

First, let's look at an alternative approach to page inheritance. The code below uses web user controls to create a header, footer, and sidebar (the HTML, minus the user controls, will be used throughout the examples in this article). It is important to note that this article will not deal with creating styles and an appealing layout for the site layout; it merely is describing the technical implementation:

<html>
  <head>
    <title>Web User Control Example</title>
  </head>
  <body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
    <table cellspacing="0" cellpadding="0" border="0" width="100%" align="center">
      <!--HTML area denoted as the "Header" -->
      <tr>
        <td colspan="2">
          <cc:Header id="ccHeader" runat="server"/>
        </td>
      </tr>
      <!--HTML area denoted as the "Sidebar" -->
      <tr>
        <td valign="top">
          <cc:Sidebar id="ccSidebar" runat="server"/>
        </td>
        <!--HTML area denoted as the "Body" -->
        <td valign="top">

          <!-- Insert content here -->

        </td>
      </tr>
      <!--HTML area denoted as the "Footer" -->
      <tr>
        <td colspan="2">
          <cc:Footer id="ccFooter" runat="server"/>
        </td>
      </tr>
    </table>
  </body>
</html>

The preceding code uses web controls to create the site layout. The drawback to this approach, however, is HTML surrounding the body and setup table HTML is duplicated. If the site layout changes, all of the ASP.NET pages using this template would require modification. Using a page inheritance approach will reduce the amount of duplication between web pages, as you will see below.

ASP.NET pages, by default, inherit from the System.Web.UI.Page class, which defines the properties, methods, and events of the page. To add your own custom properties, methods and events, a custom page class can be created. This class will inherit from System.Web.UI.Page, and all ASP.NET web pages will inherit from this new class. It is also important to note that constant variables (not declared as private) can be defined in the custom page class, which are accessible to the entire application.

'ASP.NET web page code behind file

Public Class ASPNetPage
  Inherits CustomPageClass

'Custom class file that inherits from the Page class

Friend Class CustomPageClass
  Inherits System.Web.UI.Page

The HTML table definition (as displayed in the above example) is generated in the Render method. This method uses the HTMLTextWriter class to write HTML to the output stream (in this case, the browser). Through multiple calls to the Write() method, the HTML will be constructed in a similar format to the example above. Please note, in the example below, that it is more efficient to make multiple calls to the Write() method than writing a concatenated string.

Protected Overrides Sub Render(writer as HTMLTextWriter)
  writer.Write("<html>")
  writer.Write("<head><title>Page Inheritance Example</title></head>")
  writer.Write("<body><table border='0'>")
  writer.Write("<tr><td colspan='2' align='right'><h1>Header</h1></td></tr>")
  writer.Write("<tr><td style='width:200px'>Sidebar</td>")
  writer.Write("<td>")

  'Write the derived pages content to the output stream
  MyBase.Render(writer)

  writer.Write("</td></tr>")
  writer.Write("<tr><td colspan='2'>Footer</td></tr>")
  writer.Write("</table></body></html>")
End Sub

The benefit to page inheritance is that the outcome can be altered by custom properties or variables. For example, a page title property will be added to the class. This property will be assigned in the derived ASP.NET page's New constructor.

'*****************************************
'ASP.NET derived page's code

Public Sub New()
  MyBase.PageTitle = "This is the Title"
End Sub

'*****************************************
'Updated methods and properties
Private m_strPageTitle as String

Public Property PageTitle() as String
  Get
    Return m_strPageTitle
  End Get
  Set(Value as String)
    m_strPageTitle = Value
  End Set
End Property

Protected Overrides Sub Render(writer as HTMLTextWriter)
  writer.Write("<html>")
  writer.Write("<head><title>" & m_strPageTitle & "</title></head>")
  ...
End Sub

This is a simple example of a custom page property that can be added to the custom page class. To make this class more complex, a last modified date will be added to the footer area. The following code will use the System.IO.FileInfo object to retrieve information about the current web page. This object can retrieve many properties about the file, but for this example, the LastWriteTime property will be retrieved, and converted to a short date string. The following code will come after the call to the MyBase's Render method from the previous example.

Protected Overrides Sub Render(writer as HTMLTextWriter)
  ...
  Dim objFileInfo As New FileInfo(Server.MapPath(Request.ServerVariables.Get(“SCRIPT_NAME”)))

  writer.Write(“</td></tr>”)
  writer.Write(“<tr><td colspan=’2’ align=’center’>Page Last Modified: “)
  writer.Write(objFileInfo.LastWriteTime.ToShortDateString() & “</td></tr></table>”)
End Sub

Other user-friendly functionality can be added to the site, such as a printable version of the page. For our next example, we are going to add a link that opens a new browser with the printable version of the page. This will be marked with a querystring property "printable". A PrintableVersion property will be added to the custom page class, which could also be an acceptable form of displaying the printable version.

'Existing code removed for ease of use
Friend Class CustomPageClass
  Inherits System.Web.UI.Page

  Private m_blnPrintableVersion As Boolean = False

  Public Property PrintableVersion() As Boolean
    Get
      Return m_blnPrintableVersion
    End Get
    Set(Value as Boolean)
      m_blnPrintableVersion = Value
    End Set
  End Property

  Protected Overrides Sub Render(writer as HTMLTextWriter)
    'If the querystring variable is present, set the printable version to true
    If (Request.QueryString.Get("printable") <> String.Empty) Then
      m_blnPrintableVersion = True
    End If

    'If not the printable version, output the header and sidebar
    If (not m_blnPrintableVersion) Then
      writer.Write("<html>")
      writer.Write("<head><title>" & m_strPageTitle & "</title></head>")
      writer.Write("<tr><td colspan='2' align='center'>MY HEADER</td></tr>")

      'Printable version link
      writer.Write("<tr><td> </td>")
      writer.Write("<td align=’right’><a href=’" & _
      writer.Write(Request.ServerVariables.Get("SCRIPT_NAME"))
      writer.Write("?printable='true' target='_blank'>Printable Version")
      writer.Write("</a></td></tr>")

      'Rest of site layout
      ...
    End If

    MyBase.Render(writer)

    If (not m_blnPrintableVersion) Then
      'Write the footer from the example above
      ...
    Else
      'If you wanted to add any other attributes to the bottom of the
      'Printable Version page, you can, such as:
      writer.Write("<table border='0'><tr><td>My Web Site</td>")
      writer.Write("<td>" & DateTime.Now.ToShortDateString() & "<td></tr>")
    End If
  End Sub
End Class

The querystring property alters the property value, so if both the property and the querystring values are present, the latter will have precedence.

For the last piece of functionality, a dynamic sidebar will be added. This sidebar will be implemented in XML, and the contents will be transformed against a stylesheet. The transformation will appear on the screen as:

Link1
Link2
Link3
Link4
...

To accomplish this, the System.XML.Xpath.XPathDocument and the System.XML.XSL.XSLTransform classes are used. These objects load the XML and the XSL stylesheet, respectively, and the transformation is assigned to a text writer object. For example, using an XML format of:

<Site>
  <Document>
    <Name>Name of Link</Name>
    <Path>Web Path to link</Path>
  </Document>
</Site>

The code, implemented in the method below, takes an XML and XSLT file, and writes the content to a StringWriter class. This class is then returned to the caller.

Private Function RenderSidebar() As String
  Dim objXSLT As New XSLTTransform
  objXSLT.Load("c:\mystylesheet.xslt")
  Dim objXPath As New XPathDocument(“c:\myxml.xml”)
  'We have enough to make a transformation

  Dim objWriter As New StringWriter
  objXSLT.Transform(objXPath, Nothing, objWriter, Nothing)

  Return objWriter.ToString()
End Sub

That's all that it takes to create a common application layout for your site. Using the OnInit method can leverage server control capabilities, and add to the possibilities for your site design. Each of these methods has about the same performance, but if the OnInit method declares a lot of server variables, it will hurt your performance. As you will see on technical Internet sites and in computer books, simplicity is the key.

You may download the code here.