Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MasterPages reinvented - a Component Based Template Engine for ASP.NET

0.00/5 (No votes)
27 Apr 2005 17  
User-friendly but powerful template engine which provides clean and painless separation of content and MasterPages (templates).

Introduction

A template engine allows you to render the contents of your ASP.NET web forms as parts of a Master Page (template). This template engine provides a new approach regarding the separation of ASP.NET templates and web forms.

The features at a glance:

  • Full designer support for all settings of your web forms - no coding and no additional HTML required.
  • No change of your design as the HTML of web forms is kept completely untouched - all settings are serialized to the code-behind.
  • No custom base class needed for the web forms.
  • As the HTML does not change, you can use the template engine with existing web forms without having to adapt your design.
  • Painless removal of template bindings on a web form (just one component per web form).
  • Different layouts possible by just assigning different templates at runtime.

Background

The template engines I evaluated were either hard to use (lots of custom code needed) or forced me to design my ASP.NET web forms according to certain requirements (e.g., to put controls into a specialized container control). What I wanted was a complete and clean separation of content and templates without having to adapt any of my HTML and existing web forms - so I came up with this solution. It's easy to use and speeds up development of templated web sites enormously - my new web site which is powered by the engine was made in less than two days (mostly struggling with content) as I just had to define the content after having created the templates.

The basic idea

An important part of the templating work is... templates! They provide two things:

  • the common structure of your web pages like header, footer and navigation.
  • so-called Regions - areas of the page that get individual content.

The other parts are your individual web forms. They contain the content which is being assigned to regions of the used template. Simply said, the template engine merges your web form with the template in two steps:

  • the template is loaded dynamically
  • the content of the web form is moved into its target Region of the template

The nice thing is - once you have your templates, everything is done through a single component that is placed on each web form - no custom base classes, coding and no additional HTML are required.

Prerequisites

This tutorial consists of two parts:

  • We will create a template (plain old user control) which hosts RegionPlaceHolder controls.
  • To merge our web form with the template, we will use the RegionProvider component which is placed on the web form.

To work with these controls in your own projects, you should add them to your toolbox which allows you to simply drag and drop them on your web forms. However, this is not needed to have a look at the sample application.

Creating Templates

A template is a UserControl (.ascx file) that derives from Evolve.Portals.Framework.PortalTemplate. You're getting your template up and running in two steps:

  • Create a new user control (.ascx file).
  • In the code-behind, change the base class from System.Web.UI.UserControl to Evolve.Portals.Framework.PortalTemplate.

What remains is to design your template and to define the Regions that get the content. Again, this is very easy:

  • Design your template like a standard web page (HTML and ASP.NET controls).
  • Complete your work by dragging RegionPlaceHolder server controls onto your template.

The RegionPlaceHolder control exposes a single property - a Region name. A Region is just an enum that makes it easier to remind your settings. The property dialog will present you a list of possible regions. If you think they don't fit your needs, you can define your own Region names by simply changing the PortalRegion enum in the source code.

Using the property dialog of the controls, I assigned the value Left1 to the RegionPlaceHolder control on the left and the value Content to the place holder on the right. This allows me to render content in either the left pane (yellow) or the content section (blue) of the sample.

That's it already :-)

RegionProvider Component

The workhorse is a component called RegionProvider. It is placed on your web forms to link them to your templates. As it's a component and not a control, the RegionProvider's properties are being serialized to the code-behind rather than the .aspx file of the web form. As a result, your HTML is kept completely untouched and all settings are compiled rather than late bound.

To assign a provider to a page, simply drag and drop it on the web form. Visual Studio then displays it at the bottom of the designer window.

The RegionProvider itself exposes only four properties of which only the template path needs to be set explicitly:

  • DefaultRegion

    If set, all top-level controls on the webform that do not have a region are being moved into this region of the template.

  • TemplatingTime

    The time when templating occurs (during OnInit, OnLoad, PreRender or manually). More on this later.

  • IgnoreOrderIndices

    The RenderIndex property (explained below) of templated controls is not inspected for performance reasons (default).

  • RegionTemplatePath

    The path of the template which is used for this web form.

Assigning Regions to controls

As soon as you have a RegionProvider on your web form, the component extends the properties of all controls on the form by two new properties (see screenshot of the Image control's property dialog above):

  • The TargetRegion property of your controls tells the template engine where to put your controls on the template when it comes to rendering. In the example, the image will be rendered into the template's Region Left1.
  • The RenderIndex property controls the order of controls that go into the same region. You will rarely need to set this and it only happens if the RegionProvider's IgnoreRenderIndices property is set to false.

Unlike the standard properties of your controls, these settings are being serialized into the designer's IntializeComponent method - your HTML is kept untouched. You can check your settings in the web form's code-behind class. It looks like this:

// 

// regionProvider1

// 

this.regionProvider1.HookIntoRendering = true;
this.regionProvider1.HostingPage = this;
this.regionProvider1.PropertySets.Add(new 
  Evolve.Portals.Framework.RegionPropertySet(this.Image1, 
  Evolve.Portals.Framework.PortalRegion.Left1, 0));
this.regionProvider1.PropertySets.Add(new 
  Evolve.Portals.Framework.RegionPropertySet(this.HelperPanel1, 
  Evolve.Portals.Framework.PortalRegion.Content, 0));
this.regionProvider1.RegionTemplatePath = "templates/menutemplate.ascx";
DefaultRegions and Container Controls

Of course, you don't have to set the Region of every control on your form.

  • If you use containers like Panels, you don't have to touch the controls within that container.
  • You may also set the DefaultRegion property in the RegionProvider itself and leave the Region of the controls to None. All controls with no explicit settings will be moved into this region. Of course, this will be more work for the template engine which needs to inspect the web form's control collection.

Manual vs. Automated Templating

The TemplateTime property of the RegionProvider allows you to perform the templating at a given time. In most cases, you can just leave the default value and you're fine. Still, you have four possibilities:

  • Automatically merge content already during the web form's OnInit event. Take this one if eventing of controls does not work with OnLoad templating. Unfortunately, VB.NET does not seem to be able to handle this timing as the designer code is called after this event has occurred in VB pages. VB-developers need to call the template renderer manually during their Init method to mimic this functionality (see below).
  • Automatically merge content during the web form's OnLoad event (default).
  • Automatically merge content during the web form's PreRender event. Generally not recommended (make sure you know the ASP.NET execution lifecycle before using this one.
  • Trigger manually.

If you want to do it manually, set TemplateTime to Manual and add the following code somewhere to your code-behind:

   //merge with template

   Evolve.Portals.Framework.TemplateRenderer.PerformTemplating(this);

It's highly recommended to call the PerformTemplating method before rendering occurs (e.g., by overwriting the RenderChildren event). If you do so, there might be issues with client side validation (thanks to Gareth Brown for detecting this one): as ASP.NET re-registers JavaScript event handlers again, this leads to a messed up client side script.

Important note: The TemplateTime property replaces the HookIntoRendering flag of the first release which was used to override the rendering of the web form and caused the above mentioned issues with client side validation. If you're updating from an earlier version, your IDE will refuse to compile as the HookIntoRendering has been marked Obsolete. To correct your code, just try to compile, double-click on the error messages, and remove the line in the code-behind that sets HookIntoRendering:

this.regionProvider1.HookIntoRendering = true; //does not work anymore!

Working with Controls on the Templates

Note: The following notes only apply to controls on the templates, not on the web forms.

Templates and relative links

If your template contains images or links (or other controls that work with relative links), they might not render properly. This is because the path of the image during design time may not be the same path during runtime where the path of the rendered web form counts. However, it's easy to work with relative links anyway:

Use the tilde (~) for images or links that are placed directly on the template. The tilde is being rendered as the root path of your web application during runtime - a very nice feature (more on this on ASP.NET PRO). Example: ~/img/mylogo.gif instead of ../../img/mylogo.gif.

...again: you only have to consider this for your templates, not the web forms.

Accessing controls of the template

Your template's controls are not available to the web form until the template engine did its work. However, I would rather recommend to handle template logic in the template itself to ensure a clean separation of templates and web forms.

Getting rid of the Template Engine

With ASP.NET 2.0's Master Pages, you may want to switch and get rid of this template engine. This is where the separation between page design and templating really pays off: as no additional HTML was produced by the RegionProvider, all settings of a whole web form are removed at once if you delete the RegionProvider component.

Requirements

Installing Service Pack 1 for .NET 1.1 is highly recommended. Some users observed invalid JavaScript generation on machines without the service pack. If you can't install the SP and you need controls that create JavaScript, use OnInit as the templating time.

Conclusion

The mixture of components (RegionProvider on web forms) and controls (RegionPlaceHolder on templates) makes it extremely easy to get your templates working. It prevents you from scattering additional HTML all over your web application and enforces a clean separation of design and development. I hope you will enjoy it as much as I do :-)

Newsletter

With a growing number of users who use the engine in productive applications, I decided to provide a newsletter to keep people up-to-date who do not want to check here regularly. If you like, you can subscribe: here.

History

  • Version 1.0 - Sept 01, 2004
  • Updated article plus new sample: Sept 24, 2004
  • Version 1.0.1 - Introduced TemplateTime property which replaces HookIntoRendering: Oct 1, 2004
  • Version 1.0.2 - Introduced OnInit templating time which solves issues with data-bound controls' client side scripting under some conditions: Oct 28, 2004.

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