asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search










Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

An Innovative Technique for Creating Reusuable Page Templates in ASP.NET 1.x
By Steve Stchur
Rating: 4.3 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

  • download source code
  • Introduction

    Code reusuability is (or at least should be) one of the major goals of any good object-oriented programmer. Reusuable code cuts development time and also minimizes the chances of introducing new bugs into an application. Fortunately, the ASP.NET framework has made code reusuability easier and much more elegant than it was in classic ASP. I, personally, am very grateful for all of the wonderful techniques that we have at our disposal for code reusuability (inheritance, ASP.NET server controls, user controls, etc..).

    One area though, where I feel that ASP.NET still leaves a bit to be desired is reusuability for ASP.NET pages at the UI level. Several techniques exist to combat this problem, one of the most common being a technique that is based on overriding a base page's OnRender() method. By inheriting all of the pages in your web application from the base page, you can ensure a consistent look and feel across the entire application.

    For me though, there are two major drawbacks to this approach. A base page's OnRender() method is nested pretty far into your application, so what happens if you decide you want to change something about the UI once your application has been deployed? Well, you have to edit the base page's OnRender() method, and editing code means recompiling, and recompiling means retesting and redeploying, none of which sound much like a good time to me.

    Another stike against this approach is that is 100% reliant upon a code file (be it C# or VB). In other words, there is no ASP.NET page (or Code-Front, as I like to call it) associated with the base page. Any UI components that you want to exist across your web app, must be programatically built and added to the base page, and I think this is inconvienent (at best).

    What would be nice, is if there were a technique whereby every page in your web application could inherit not only the *functionality* of a base page, but also the UI of that base page. Furthermore, the UI of the base page would be as simple to modify as editing a basic HTML/ASP.NET file.

    Well believe it or not, it *is* possible, and in this article, I'm going to show you my technique for doing just that.

    Basic Look & Feel

    First things first, we must decide what that "basic look & feel" for our web application is going to be. I wanted to keep things simple for this article, so I whipped up this HTML and corresponding CSS that creates a header, footer, and left-side navbar for a ficticious web site about cooking.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <html>
    <head>
        <title>Cooking: it's more fun than you think!</title>

        <style type = "text/css">
            body
            {
                font-family: verdana; font-size: 11px; color: #676767;
                margin: 0px; padding: 0px; background-color: #cccccc;
            }

            #Container
            {
                position: relative;
                width: 760px;
                margin: auto;
                margin-top: 10px;
                background-color: #003366;
            }

            #Header
            {
                height: 80px; line-height: 80px;
                background-color: #336699;
                font-size: 18px; font-weight: bold; color: #ffffff;
                text-align: center;
                border-bottom: solid 1px #cccccc;
            }

            #NavBar
            {
                position: absolute; top: 80px; left: 0px;
                width: 130px; height: 300px;
                padding: 5px;
                color: #ffffff;
            }

            #PageContent
            {
                background-color: #ffffff;
                position: relative; top: 0px; left: 140px;
                padding: 10px;
                width: 600px;
                height: 300px;
            }

            #Footer
            {
                padding-left: 10px;
                height: 30px; line-height: 30px;
                background-color: #336699;
                font-weight: bold; color: #ffffff;
                text-align: center;
                border-top: solid 1px #cccccc;
                border-bottom: solid 1px #cccccc;
                clear: both;
            }
        </style>
    </head>

    <body>
        <div id = "Container">
            <div id = "Header">
                Cooking: it's more fun than you think!
            </div>

            <div id = "NavBar">
                <b>Navigation:</b>
                <ul>
                    <li></li>
                    <li>Searing Chicken</li>
                    <li>Roasting Pork</li>
                    <li>Onion Soup</li>
                </ul>
            </div>

            <div id = "PageContent">
                <asp:Label id = "lblBaseLabel" runat = "server">Hi, I'm the Base Label!</asp:Label>

                <br />
                <asp:PlaceHolder id = "phMainContent" runat = "server" />
            </div>

            <div id = "Footer">
                Copyright (C) Cooking: It's more fun than you think, Inc. 2005. All rights reserved.
            </div>
        </div>
    </body>
    </html>

    Of course, the above would be better if the CSS were placed in an external stylesheet like so:

    <link rel = "stylesheet" type = "text/css" href = "/_stylesheets/layout.css" />

    But I wanted the reader to be able to get a glimpse of the CSS before I hid it in an external file.

    The next step in building our Page Template is to place the above ASP.NET/HTML in an XML file. That's right, an XML file. Something like this would work just fine:

    <?xml version="1.0" encoding="utf-8" ?>
    <PageTemplate>
        
        <![CDATA[
        
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <html>
    <head>
        <title>Cooking: it's more fun than you think!</title>
        <link rel = "stylesheet" type = "text/css" href = "./_stylesheets/layout.css" />
    </head>

    <body>
        <div id = "Container">
            <div id = "Header">
                Cooking: it's more fun than you think!
            </div>

            <div id = "NavBar">
                <b>Navigation:</b>
                <ul>
                    <li></li>
                    <li>Searing Chicken</li>
                    <li>Roasting Pork</li>
                    <li>Onion Soup</li>
                </ul>
            </div>

            <div id = "PageContent">
                <asp:Label id = "lblBaseLabel" runat = "server">Hi, I'm the Base Label!</asp:Label>
                
                <br />
                <asp:PlaceHolder id = "phMainContent" runat = "server" />
            </div>

            <div id = "Footer">
                Copyright (C) Cooking: It's more fun than you think, Inc. 2005. All rights reserved.
            </div>
        </div>
    </body>
    </html>    

        ]]>

    </PageTemplate>

    You can see that I haven't done anything spectacular here. Just wrapped the whole thing inside of an XML tag <PageTemplate>. You'll also notice the CDATA block. I use this just to ensure that I won't get any XML errors later on when I'm reading the file programatically (of course, this means, it's up to *you* to ensure your HTML is XHTML compliant -- but that's another article).

    Finally, notice the <asp:PlaceHolder id = "phMainContent" runat = "server" /> control. This is critical. This PlaceHolder represents the *exact spot* that any non-templated content will be displayed. In other words, the phMainContent PlaceHolder will hold all of the controls that you will eventually define on the inheriting page.

    We've now got our XML template file written and ready to go. You can think of this file as the corresponding ASP.NET page (or Code-Front) to the base page class that we're going to write shortly.

    Creating the Base Page Class

    The concept I'm going to use to create this Page Template is as follows:

    1. Grab all of the controls from the inheriting page (remove them), and place them in a temporary control array.
    2. Read the HTML from the XML template file into a string variable.
    3. Parse string variable (which contains HTML) into series of ASP.NET Literal Controls, and/or HtmlControls, and/or WebControls, and (if applicable) 3rd party controls, and add the newly parsed control to the Page's ControlCollection.
    4. Take all of the controls that were placed in the temporary control array (from Step 1) and add them to the PlaceHolder, phMainContent.

    I know this may seem a bit convoluted, but I'm confident that it will become clearer as we go, so let's get started by first creating a BasePge class and then implementing Step 1.

    The basic shell of the BasePage class should look something like this:

    using System;
    using System.Xml;
    using System.Web.UI;
    using System.Reflection;
    using System.Web.UI.WebControls;

    namespace sstchur.web.Pages
    {
        public class BasePage : System.Web.UI.Page
        {
            // Protected Variables (UI Components)
            protected Label lblBaseLabel;

            // Private Variables
            private string m_strTemplateFile;

            // Public Properties
            public string TemplateFile
            {
                set { m_strTemplateFile = value; }
            }

            // Constructor
            public BasePage()
            {
                // Initialize the TemplateFile in case one is not specified
                m_strTemplateFile = "~/_templates/standardpage.xml";
                     }

            protected override void OnInit(EventArgs e)
            {
                // Load the XHTML Template
                InvokePageTemplate();

                // Initialize any components/variables specific to this class (or its base)
                InitializeComponent();

                // Let the base class do its thing
                base.OnInit (e);
            }

            private void InitializeComponent()
            {
                // Wireup Page_Load Event
                this.Load += new EventHandler(Page_Load);
            }

            private void Page_Load(object sender, EventArgs e)
            {
                // Put any needed Page_Load functionality here
            }

            // Here is where all the tricky stuff happens
            private void InvokePageTemplate()
            {
                // Here is where the bulk of the BasePage's implementation will eventually go
            }
        }
    }

    One of the first things you'll notice is a few non-orthodox using statements: namely, using System.Xml and using System.Reflection. You might have already guessed that we'd need access to Xml functionality since our TemplateFile is written in XML, but the System.Reflection namespace should be a bit of a surprise. The reason it's needed won't become clear until a little later on, so I'll postpone its discussion until we get to the code that actually make use of it.

    Beyond that, there's nothing earth-shattering in the BasePage class. You can see that we inherit from the standard System.Web.UI.Page class, and we go ahead and wire up the Page_Load method using the default technique that VS.NET implements for you when you create a new WebForm (overriding the OnInit() method and calling InitializeComponent()).

    You'll notice however, that prior to calling InitializeComponent(), we have a call to a custom, private, method: InvokePageTemplate() (which we have yet to implement). If you remember back to the 5 steps I outlined for creating the BasePage class, step 1 was grabbing all of the controls from the inheriting page (removing them), and placing them in a temporary control array.

    private void InvokePageTemplate()
    {
        // Copy off inheriting page's control into a temporary Control[] array
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);
        this.Controls.Clear();
    }

    Step 2 was reading the HTML/XHTML from the XML template file into a string variable:

    private void InvokePageTemplate()
    {
        // Copy off inheriting page's control into a temporary Control[] array
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);
        this.Controls.Clear();

        // Load the XML tempalte file into an XmlDocument object
        doc = new XmlDocument();
        doc.Load(Server.MapPath(m_strTemplateFile));

        // Place the content's of the <PageTemplate> tag into a string variable
        XmlElement root = doc.DocumentElement;
        XmlNode nodeTemplate = root.SelectSingleNode("//PageTemplate");
        string strTemplate = nodeTemplate.InnerText;
    }

    Step 3 was parsing the string variable (which contains HTML) into series of ASP.NET Literal Controls, and/or HtmlControls, and/or WebControls, and (if applicable) 3rd party controls, and adding the newly parsed control to the Page's ControlCollection. This is surprisingly easy to do with the Page.ParseControl() method. What surprised me the most about this method, was that it does not need to be called repeatedly. Rather, any nested controls in the string to be parsed, will become child controls of their containing controls (essentially the same way .NET parses your ASP.NET page!).

    private void InvokePageTemplate()
    {
        // Copy off inheriting page's control into a temporary Control[] array
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);
        this.Controls.Clear();

        // Load the XML tempalte file into an XmlDocument object
        doc = new XmlDocument();
        doc.Load(Server.MapPath(m_strTemplateFile));

        // Place the content's of the <PageTemplate> tag into a string variable
        XmlElement root = doc.DocumentElement;
        XmlNode nodeTemplate = root.SelectSingleNode("//PageTemplate");
        string strTemplate = nodeTemplate.InnerText;

        // Parse the HTML/XHTML contained in the strTemplate string;
        // add the parsed controls to the Page's ControlCollection.
        Control ctrlTemplate = Page.ParseControl(strTemplate);
        this.Controls.Add(ctrlTemplate);
    }

    Step 4 was taking all of the controls that were placed in the temporary control array (from Step 1) and adding them to the PlaceHolder, phMainContent. Before we can do this though, we need a reference to the phMainContent PlaceHolder. This is straight-forward enough, so let's tackle everything at once:

    private void InvokePageTemplate()
    {
        // Copy off inheriting page's control into a temporary Control[] array
        Control[] controls = new Control[this.Controls.Count];
        this.Controls.CopyTo(controls, 0);
        this.Controls.Clear();

        // Load the XML tempalte file into an XmlDocument object
        doc = new XmlDocument();
        doc.Load(Server.MapPath(m_strTemplateFile));

        // Place the content's of the <PageTemplate> tag into a string variable
        XmlElement root = doc.DocumentElement;
        XmlNode nodeTemplate = root.SelectSingleNode("//PageTemplate");
        string strTemplate = nodeTemplate.InnerText;

        // Parse the HTML/XHTML contained in the strTemplate string;
        // add the parsed controls to the Page's ControlCollection.
        Control ctrlTemplate = Page.ParseControl(strTemplate);
        this.Controls.Add(ctrlTemplate);

        // Get a reference to the phMainContent PlaceHolder
        PlaceHolder phMainContent = (PlaceHolder)ctrlTemplate.FindControl("phMainContent");

        // Add each control from the inheriting page (saved from Step 1) to the phMainContent's ControlCollection
        foreach (Control c in controls)
            phMainContent.Controls.Add(c);
    }

    At this point, we *could* be done. If you create a new ASP.NET page with literally NOTHING in it (save for an <%@ Page ... %> directive) which inherits from sstchur.web.Pages.BasePage, you'll have a working ASP.NET page that looks just like the template file we created way back at the beginning of this article. You can go ahead an try it if you want (don't forget to compile everything though).

    The only problem at this point, is that we don't have access to any of the base page's components. You may have noticed that I added a Label to the template file, whose ID was lblBaseLabel and whose default Text was "Hi, I'm the Base Label!" I did this specifically for demonstration purposes. We obviously want to be able to access the base page's components (like lblBaseLabel), and with our BasePage class in its current state, we can't do that. ASP.NET will let you try alright, but you'll get a NullReferenceException, and that's never good.

    There are two solutions that I can think of, both are similar, but one is much more convenient. The first way, is to simply use FindControl() to obtain a reference to a control with a given ID. In the case of our lblBaseLabel, the code (which would be placed in the inheriting page) would look something like this:

    Label baseLabel = (Label)Page.FindControl("lblBaseLabel);
    baseLabel.Text = "Overridden value";

    However, having to call FindControl EVERY time we want to access one of the base page's controls, is sloppy, repetitive, and I dunno... just all around bad.

    A much better solution, would be one that would allow us to access the base page's components via base.{ComponentId} where {ComponentId} is the Id of the component we want to access (in our case, base.lblBaseLabel).

    How do we do this? Why, with Reflection of course! While a detailed discussion of Reflection is far, far beyond the scope of this article, I'm going to try to touch on just the bare minimum required to understand the reflection-based code in our BasePage class. A good start would be to try to obtain a thorough understanding of just what it is we're trying to accomplish. Let's recap:

    • We have a BasePage class which all of our ASP.NET pages will inherit from.
    • Inheriting from the BasePage class mean inheriting not only functionality, but also UI components contained within the XML template file.
    • If we want to be able to access any of the components from XML template file, in the BasePage, then we need to add a protected member variable to the BasePage, whose variable name is the same as the ID of the ASP.NET component in the XML template file.
    • We also want each protected member variable of the BasePage to be available to inheriting pages (and not throw back NullReferenceExceptions when we try to access them).
    • Finally, we already know that in order to avoid a NullReferenceException, one solution is a call to FindControl().

    If we take all of the requirements that I've outlined above, the logic to implement it can be summarized as follows:

    For each protected member variable in the BasePage, do the following:

    • Determine the variable's name (as far as the compiler is concerned).
    • Call FindControl(variableName) to get a reference to the control whose Id is the same as that variableName.
    • Set the value of the protected member variable equal to the control that was returned with the call to FindControl()

    Sounds complicated doesn't it? Well, to be truthful, is kind of is. Fortunately though, the .NET framework wraps up a good portion of the complexity in the System.Reflection namespace, which makes what we're trying to do a lot less daunting that it might at first seem.

    The first step is to determine what protected member variables have been defined in our BasePage. We can do this by using the FieldInfo class. In our case, I'm going to create an array of FieldInfo objects to hold information about each variable defined in our base page. Then, I'm going to create a Type object which is of the type sstchur.web.Pages.BasePage. Finally, I'll call GetFields(...) on my type object and assign the resulting array to the FieldInfo array I created a moment earlier. It looks something like this:

    FieldInfo[] fieldInfo;
    Type myType = typeof(sstchur.web.Pages.BasePage);
    fieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    The BindingFlags that were passed into the GetFields(...) method are just a way of specifying which types of variables we want to retrieve information about. In our case, we're only interested in NonPublic (protected actually, but we'll get to that in a minute), instance variables that have been explicitely declared.

    Once we've got our fieldInfo array populated, we can loop through it and take a crack at those few remaining tasks:

    FieldInfo[] fieldInfo;
    Type myType = typeof(sstchur.web.Pages.BasePage);
    fieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    for (int i = 0; i < fieldInfo.Length; i++)
    {
        if (!fieldInfo[i].IsPrivate)
        {
            string id = fieldInfo[i].Name;

            Control c = ctrlTemplate.FindControl(id);
            if (c != null)
                fieldInfo[i].SetValue(this, c);
        }
    }

    In the above for loop, we simply check to make sure the current variable is not private (we pulled back NonPublic fields with our call to GetFields. So a variable that isn't public and isn't private, much be protected). Next, we retrieve the variable Name, and assign it to the string variable id. Finally, we attempt to get a reference to a Control object by calling FindControl(id). If the control that's returned is not null, we assign it to the current variable (field) in our for loop.

    And that's all there is too it. See, now that wasn't so bad was it?

    Now, I know what a lot of you much be thinking. Doesn't this add a tremendous amount of overhead to your pages? I would suspect that the additional overhead probably is significant. However, in my testing (which was far from extensive) the pages seemed to perform pretty darned well. Infact, they performed well enough, that the company I work for agreed to use this concept in a web application we're working on.

    The only thing I can say with regards to overhead is to do some benchmarking of your own. If the numbers don't satisfy you, you can always elect to use a different technique for page templates. At the very least, you'll have gotten to read a stunningly interesting article by yours truely :-)

    Putting It All Together

    So now we've got pretty much everything we need. Let's take a look at each of the completed files that would go into an example page:

    -- The XML template file --

    <?xml version="1.0" encoding="utf-8" ?>
    <PageTemplate>
        
        <![CDATA[

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <html>
    <head>
        <title>Cooking: it's more fun than you think!</title>
        <link rel = "stylesheet" type = "text/css" href = "./_stylesheets/layout.css" />
    </head>

    <body>
        <div id = "Container">
            <div id = "Header">
                Cooking: it's more fun than you think!
            </div>

            <div id = "NavBar">
                <b>Navigation:</b>
                <ul>
                    <li></li>
                    <li>Searing Chicken</li>
                    <li>Roasting Pork</li>
                    <li>Onion Soup</li>
                </ul>
            </div>

            <div id = "PageContent">
                <asp:Label id = "lblBaseLabel" runat = "server">Hi, I'm the Base Label!</asp:Label>

                <br />
                <asp:PlaceHolder id = "phMainContent" runat = "server" />
            </div>

            <div id = "Footer">
                Copyright (C) Cooking: It's more fun than you think, Inc. 2005. All rights reserved.
            </div>
        </div>
    </body>
    </html>

        ]]>

    </PageTemplate>

    -- CSS file -- (note that I put all of my CSS files in a subdirectory ./_stylesheets)

    body
    {
        font-family: verdana; font-size: 11px; color: #676767;
        margin: 0px; padding: 0px; background-color: #cccccc;
    }

    #Container
    {
        position: relative;
        width: 760px;
        margin: auto;
        margin-top: 10px;
        background-color: #003366;
    }

    #Header
    {
        height: 80px; line-height: 80px;
        background-color: #336699;
        font-size: 18px; font-weight: bold; color: #ffffff;
        text-align: center;
        border-bottom: solid 1px #cccccc;
    }

    #NavBar
    {
        position: absolute; top: 80px; left: 0px;
        width: 130px; height: 300px;
        padding: 5px;
        color: #ffffff;
             }

    #PageContent
    {
        background-color: #ffffff;
        position: relative; top: 0px; left: 140px;
        padding: 10px;
        width: 600px;
        height: 300px;
    }

    #Footer
    {
        padding-left: 10px;
        height: 30px; line-height: 30px;
        background-color: #336699;
        font-weight: bold; color: #ffffff;
        text-align: center;
        border-top: solid 1px #cccccc;
        border-bottom: solid 1px #cccccc;
        clear: both;
    }

    -- BasePage.cs --

    using System;
    using System.Xml;
    using System.Web.UI;
    using System.Reflection;
    using System.Web.UI.WebControls;

    namespace sstchur.web.Pages
    {
        public class BasePage : System.Web.UI.Page
        {
            // Protected Variables (UI Components)
            protected Label lblBaseLabel;

            // Private Variables
            private string m_strTemplateFile;

            // Public Properties
            public string TemplateFile
            {
                set { m_strTemplateFile = value; }
            }

            // Constructor
            public BasePage()
            {
                // Initialize the TemplateFile in case one is not specified
                m_strTemplateFile = "~/_templates/standardpage.xml";
            }

            protected override void OnInit(EventArgs e)
            {
                // Load the XHTML Template
                InvokePageTemplate();

                // Initialize any components/variables specific to this class (or its base)
                InitializeComponent();

                // Let the base class do its thing
                base.OnInit (e);
            }

            private void InitializeComponent()
            {
                // Wireup Page_Load Event
                this.Load += new EventHandler(Page_Load);
            }

            private void Page_Load(object sender, EventArgs e)
            {
                // Put any needed Page_Load functionality here
            }

            // Here is where all the tricky stuff happens
            private void InvokePageTemplate()
            {
                Control[] controls = new Control[this.Controls.Count];
                this.Controls.CopyTo(controls, 0);
                this.Controls.Clear();

                XmlDocument doc;
                doc = new XmlDocument();
                doc.Load(Server.MapPath(m_strTemplateFile));

                XmlElement root = doc.DocumentElement;
                XmlNode nodeTemplate = root.SelectSingleNode("//PageTemplate");
                string strTemplate = nodeTemplate.InnerText;

                Control ctrlTemplate = Page.ParseControl(strTemplate);
                this.Controls.Add(ctrlTemplate);
                PlaceHolder phMainContent = (PlaceHolder)ctrlTemplate.FindControl("phMainContent");

                foreach (Control c in controls)
                    phMainContent.Controls.Add(c);

                FieldInfo[] fieldInfo;
                Type myType = typeof(sstchur.web.Pages.BasePage);
                fieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                for (int i = 0; i < fieldInfo.Length; i++)
                {
                    if (!fieldInfo[i].IsPrivate)
                    {
                        string id = fieldInfo[i].Name;

                        Control c = ctrlTemplate.FindControl(id);
                        if (c != null)
                            fieldInfo[i].SetValue(this, c);
                    }
                }
            }
        }
    }

    One more file... err, actually 2 more files remain. An ASPX example file and its corresponding code-behind. You're not going to believe how absolutely simple these files are to create:

    -- Example.aspx --

    <%@ Page language = "C#" AutoEventWireup = "false" Codebehind = "example.aspx.cs" Inherits = "sstchur.web.ExamplePage" %>

    -- Example.aspx.cs --

    using System;

    namespace sstchur.web
    {
        public class ExamplePage : sstchur.web.Pages.BasePage
        {
            private void Page_Load(object sender, System.EventArgs e)
            {
                base.lblBaseLabel.Text = "My value has been overridden by the example.aspx page";
            }
            override protected void OnInit(EventArgs e)
            {
                InitializeComponent();
                base.OnInit(e);
            }

            private void InitializeComponent()
            {
                this.TemplateFile = "~/template.xml";
                this.Load += new System.EventHandler(this.Page_Load);
            }
        }
    }

    Now, create a new web project in Visual Studio, and add each of the files that we created above to it. Compile the project, and browse to the file Example.aspx.

    You should see something like this:

    Of course, just for grins and giggles, it might be interesting to add a few additional components to our Example.aspx file, just to see if they do indeed act the way we'd expect them to in a normal ASP.NET page.

    -- Revised Example.aspx --

    <%@ Page language = "C#" AutoEventWireup = "false" Codebehind = "example.aspx.cs" Inherits = "sstchur.web.ExamplePage" %>

    <form id = "theForm" runat = "server">
        <p><asp:TextBox id = "txt" runat = "server" /></p>
        <p><asp:Button id = "btn" Text = "Click Me!" runat = "server" />
    </form>

    It's worth noting that if you happen to have any components in your base page's XML template file that require a <form runat="server"> tag, then you'll want to make sure you put the server-side form in the XML template file, rather than in the Code-Front of the inheriting page. And of course, you then won't need the <form runat="server"> in the Code-Front of your inheriting page anymore.

    Caveats

    There are two caveats I'd like to point out:

    1. EVERY control you use MUST be given an ID. I suspect this has to do with timing and reflection, but it's just a good habit to name your controls, so do it!

    2. Accessing the base page's components can't be done any earlier than in the Page_Load method of the inheriting page. If you try to do it in InitializeComponent() (or OnInit()) you'll be attempting to access the controls before the reflection has had a chance to works its magic. The result? A NullReferenceException.

    Conclusion

    Well, I hope this article has intrigued you and given you the confidence to start exploring the possibilities of Reusuable Page Templates using XML and reflection. The examples we've seen in this article are very very basic, but rest assured, they can be as complex as you like. The most recent web application I'm working on for example, makes use of this Page Template concept, and we're using 3rd party navigation components, and several user controls all within our page template! We even exposed method whereby certain navigational components that might not be applicable to every page, can be shown or hidden at run time.

    So I encourage you to give this concept a try. At best, it will save you a ton of time, and at worst, you'll learn something!

    Happy programming.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Jul 28, 2005 - N-Tier Web Applications using ASP.NET 2.0 and SQL Server 2005 - Part 2
    In the second part of his series on building N-tier web applications using ASP.NET 2.0 and SQL Server 2005, Thiru Thangarathinam covers the business logic and user interface layers. In the process, he also examines some new features in ASP.NET 2.0 that greatly simplify the development process.
    [Read This Article]  [Top]
    Jun 23, 2005 - Monitoring SharePoint Usage through an ASP.NET Web Application
    In this article, Gayan Peiris looks at creating an ASP.NET web application that will display the usage details of a selected SharePoint site. Building such an application enables SharePoint administrators to gather all SharePoint usage data from a central location.
    [Read This Article]  [Top]
    May 12, 2005 - Retrieving SharePoint Site Information in an ASP.NET Web Application
    In this article, Gayan Peiris examines using the SharePoint Object Model to access SharePoint site information from an ASP.NET web application. It should be of particular interest to SharePoint administrators who can use the included code as a starting point for development of their own web-based SharePoint administration application.
    [Read This Article]  [Top]
    Dec 23, 2004 - Automated Deployment for Side By Side .NET Web Apps for Visual Studio .NET 2003
    In this article, David Every outlines a step-by-step account of how he solved the problems he encountered while implementing an auto-deployment process. He also describes how to create a stable process for automated remote .NET deployment featuring "side-by-side" capability.
    [Read This Article]  [Top]
    Sep 29, 2004 - Developing Web Parts with ICellConsumer Interface
    Most default SharePoint Server Web Parts can be connected across organizations. The third article in this series shows how to develop connectable Web Parts that consume information provided by other Web Parts.
    [Read This Article]  [Top]
    Aug 10, 2004 - Implementing and Promoting Daily Builds
    Automatic daily builds is a well known software engineering best practice. This article introduces a strategy for implementing and promoting daily builds and offers tips and tricks for preventing and fixing breaks.
    [Read This Article]  [Top]
    Jun 21, 2004 - Using Open Source .NET Tools for Sophisticated Builds
    Building an application can be more than pressing F5. With an increasing number of quality packages being released, developers for the .NET platform now have options to create a very sophisticated build process. Aaron Junod describes a sample build environment and shows how a number of tools can work together to make reliable, predictable, and value-added builds.
    [Read This Article]  [Top]
    Jun 24, 2003 - Programming for the Palm Part 1 - Creating the Palm Application
    The first part of this three part series walks through the process of creating a mobile blog application using a BASIC development environment for Palm OS devices called NS Basic. Subsequent articles will focus on synchronizing the data to the desktop using C# and creating an installer.
    [Read This Article]  [Top]
    Jun 18, 2003 - Online Database Functions Testing Tool
    This short article provides source code for a classic ASP online database functions testing application and shows how to configure and use the tool for either SQL Server or Oracle.
    [Read This Article]  [Top]
    May 22, 2003 - Rewrite.NET -- A URL Rewriting Engine for .NET
    In this article Robert Chartier shows you how to use functionality in the .NET Framework to rewrite requested URLs on the fly.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers