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:
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
Panel
s, 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:
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;
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.