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.
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.
This tutorial consists of two parts:
- We will create a template (plain old user control) which hosts
- To merge our web form with the template, we will use the
RegionProvidercomponent 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.
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
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
RegionPlaceHolderserver controls onto your template.
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
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
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.
RegionProvider itself exposes only four properties of which only the template path needs to be set explicitly:
If set, all top-level controls on the webform that do not have a region are being moved into this region of the template.
The time when templating occurs (during
PreRenderor manually). More on this later.
RenderIndexproperty (explained below) of templated controls is not inspected for performance reasons (default).
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):
TargetRegionproperty 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.
RenderIndexproperty controls the order of controls that go into the same region. You will rarely need to set this and it only happens if the
IgnoreRenderIndicesproperty is set to
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
DefaultRegionproperty in the
RegionProvideritself 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
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
OnInitevent. Take this one if eventing of controls does not work with
OnLoadtemplating. 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
Initmethod to mimic this functionality (see below).
- Automatically merge content during the web form's
- Automatically merge content during the web form's
PreRenderevent. 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
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
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
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
OnInit as the templating time.
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