Introduction
Designing Web pages is something, and designing nice looking pages and controls is something else, which is always one of the important factors in evaluating web application usability and look and feel. Usually, Web developers do not pay much attention on making nice looking pages and controls, also maintaining the look and feel for the entire application is a real headache we all know about. Besides, nowadays, we are getting non-stop demands on customizing layouts and personalizing pages, along with themes, coloring, and layout. With this article, I will try to introduce a simple technique to easily define and customize controls through templates designed through the IDE.
In other words
Customizing user controls is nothing but wrapping a control, with rich layout, borders, titles, and footers.
Closer look at the solution
To make this technique work for your application, you need to have a base control for your web controls, where you can control the control's rendering behaviour. Nothing specific with this control except to have a property to hold the template ID, and override the Render
method to apply the template through the template's services.
public class BaseUserControl: UserControl, IBaseUserControl
{
#region Private fields
private string _templateId = "";
private string _title = "";
#endregion Private fields
#region Public properties
public string TemplateId
{
get
{
return _templateId;
}
set
{
_templateId = value;
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
#endregion Public properties
protected override void Render(HtmlTextWriter writer)
{
if( _templateId != null)
{
CustomizedControlsTemplates.Global.
UserControlTemplate.ApplyTemplate(this,
writer, _templateId);
}
else
{
base.Render (writer);
}
}
public void RenderUserControl(HtmlTextWriter writer)
{
base.Render (writer);
}
}
public interface IBaseUserControl
{
void RenderUserControl(HtmlTextWriter writer);
}
In the BaseUserControl
class, the Render
method will check the _templateId
and call the ApplyTemplate
, where the method RenderUserControl
represents the primitive Render
method.
Well, as you might have noticed, the BaseUserControl
implements the interface IBaseUserControl
, which enforces the user controls to implement the method RenderUserControl
.
What are templates?
Templates are nothing but normal user controls, with a place holder, which could be replaced at run time with any user control, so the template is something like:
<script runat="server">
protected string hitTime = DateTime.Now.ToString("hh:mm:ss");
//overriding the method
public override void Control_Loaded()
{
base.MainPlaceHolder = MainPlaceHolder;
hitTime = DateTime.Now.ToString("hh:mm:ss");
}
</script>
<TABLE style="BORDER-COLLAPSE: collapse" cellSpacing="0"
cellPadding="0" border="0" ID="Table1">
...
...
<TR>
<TD width="2" background= "images/left.gif" height="100%"></TD>
<TD width="145" height="16">
<asp:PlaceHolder id="MainPlaceHolder" runat= "server">
</asp:PlaceHolder>
</TD>
<TD width="4" background="images/right.gif" height="100%"></TD>
</TR>
...
...
</TABLE>
The customized templates should inherit a base template MyTemplate
that implements the interface IMyTemplate
. Mainly, the MyTemplate
class will implement the method SetPlaceHolderControl
which overrides the Render
method of the place holder. The new Render
method will not render the place holder, instead, it will render the user control, by calling the method RenderUserControl
, to avoid cyclic calls between Render
and ApplyTemplate
.
public class MyTemplate: UserControl,IMyTemplate
{
public System.Web.UI.WebControls.PlaceHolder
MainPlaceHolder = new PlaceHolder();
public BaseUserControl LoadedBaseControl;
public MyTemplate()
{
}
protected override void Render(HtmlTextWriter writer)
{
base.Render (writer);
}
private void BaseControlRenderMethod(HtmlTextWriter
output, Control container)
{
LoadedBaseControl.RenderUserControl(output);
}
virtual public void Control_Loaded()
{
}
virtual public void SetPlaceHolderControl(BaseUserControl
userControl, HtmlTextWriter writer)
{
LoadedBaseControl = userControl;
Control_Loaded();
MainPlaceHolder.SetRenderMethodDelegate( new
RenderMethod(BaseControlRenderMethod));
this.Render(new HtmlTextWriter(writer));
}
}
The method Control_Loaded
could be overridden by the templates to do some logic before starting to render the control.
Loading templates
The templates defined in the web application can be loaded at application startup time. These templates could be placed in a hash table for later reference, as in the following code snippet:
public class Global : System.Web.HttpApplication
{
public static IUserControlTemplate UserControlTemplate =
new CustomizedUserControlTemplate();
public static string DefaultTemplateId = "SampleTemplate1";
public static string [] AvailableTemplatesId =
new string[] {"SampleTemplate1","SampleTemplate2"};
public static Hashtable AvailableTemplates =
new Hashtable(AvailableTemplatesId.Length);
...
protected void Application_Start(Object sender, EventArgs e)
{
foreach(string templateId in AvailableTemplatesId)
{
if( AvailableTemplates.Contains(templateId) == false)
{
System.Web.UI.UserControl buc =
new System.Web.UI.UserControl();
AvailableTemplates.Add(templateId,
buc.LoadControl("~/" + templateId + ".ascx"));
}
}
}
...
}
In the Global
class, I kept all template IDs in a string array AvailableTemplatesId
, and the AvailableTemplates
Hashtable
will hold the loaded templates.
Last thing to say
The class CustomizedUserControlTemplate
will work like a service provider to apply the requested template according to the specified template ID. Besides, it could be used to set the default template for all controls. The method ApplyTemplate
will verify the existence of the template, and load it from the cache and then apply the template through SetPlaceHolderControl
.
Consuming the templates
The web controls may tell what the template ID to be used is, by setting the property TemplateId
, or by setting this property to blank to apply the default template.
I provided an example to show two different templates, the page "TestControlsTemplates.aspx" contains one simple user control, check out this page to see how to switch between templates.