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

Customizing user control layout

0.00/5 (No votes)
23 Feb 2006 1  
An article on customizing user controls, through special templates. Basically, it is a simple technique to wrap user controls.

Sample Image

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)
    {
        //Apply template only when _templateId is not null.

        if( _templateId != null)
        {
            CustomizedControlsTemplates.Global.
              UserControlTemplate.ApplyTemplate(this, 
              writer, _templateId);
        }
        else
        {
            base.Render (writer);
        }
    }
    
    
    //The basic render method, to be called from 

    //the template services, without causing cyclic calls

    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
{
    //the place holder to represent the user control 

    //location in the template.

    public System.Web.UI.WebControls.PlaceHolder 
            MainPlaceHolder = new PlaceHolder();

    //the LoadedBaseControl will be accessible 

    //from the templates. 

    public BaseUserControl LoadedBaseControl;
    
    public MyTemplate()
    {
    }

    protected override void Render(HtmlTextWriter writer)
    {
        //nothing specific here.

        base.Render (writer);
    }

    private void BaseControlRenderMethod(HtmlTextWriter 
                              output, Control container)
    {
        //just render the user control.

        LoadedBaseControl.RenderUserControl(output);
    }

    //the following virtual method, allow 

    //flixible access to the controls.

    virtual public void Control_Loaded()
    {

    }

    virtual public void SetPlaceHolderControl(BaseUserControl 
                           userControl, HtmlTextWriter writer)
    {
        LoadedBaseControl = userControl;
        
        //call back to the template, to allow 

        //developers to have flixible access

        //to the rendered control.

        Control_Loaded();

        //specify the rendering method to the place holder.

        MainPlaceHolder.SetRenderMethodDelegate( new 
                RenderMethod(BaseControlRenderMethod));

        //and now, render the place holder.

        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
{
    //the folloing is just a pointer 

    //to the UserControlTemplate service.

    public static IUserControlTemplate UserControlTemplate = 
                        new CustomizedUserControlTemplate();

    //define the default template id for the applicarion.

    public static string DefaultTemplateId = "SampleTemplate1";
    //the following string array holds all defined templates id.

    public static string [] AvailableTemplatesId = 
           new string[] {"SampleTemplate1","SampleTemplate2"};
    //the folowing hash table used to hold the templates objects.

    public static Hashtable AvailableTemplates = 
          new Hashtable(AvailableTemplatesId.Length);

    ...

    protected void Application_Start(Object sender, EventArgs e)
    {
        //load all templates in the AvailableTemplates.

        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.

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