Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

A Beginner's Tutorial for Understanding Templated User Controls

5.00/5 (13 votes)
6 Jun 2012CPOL5 min read 85.7K   932  
Understanding templated web user controls from a beginner's perspective.

Introduction

This article aims at understanding templated web user controls from a beginner's perspective. We will try to understand why we might need to have a templated user control and what the benefits of having a templated user control are.

Background

Whenever there is a scenario when we need to use a group of controls with some common functionality in multiple places, we resort ourselves to User Controls. User Controls combine a set of controls and functionality for reuse in multiple places of a site.

Now let's we think about a scenario where we might need some data to be represented on the UI. We need to represent this data on multiple pages and the layout/appearance of this data could be different on different locations. Can we use a user control in such a scenario? The answer to this question is - Yes, we can. What we need to do to facilitate such a reuse of data with the possibility/flexibility of controlling the layout is to have ourselves a templated user control.

Templated user controls encapsulate data inside and provide the facility to have a custom layout for the data for each instance of the templated user control. Sometimes it may also provide the default representation with the possibility to customize the representation. Let us try to work out an example to understand and implement the same.

Using the code

Let us think about a hypothetical scenario where we need to keep a Person's First Name, Last Name, and Age. We need to show this information in many places of our website but the layout might be different for each occurrence.

Implementing the Templated User Control

The first thing we need to do is create a User Control. Once we have the User Control with us we have to put a placeholder control on this user control. This placeholder control, as the name suggests, will be a placeholder for the controls providing the default representation and also for the custom layout that the developers might want to use.

Now before jumping on how to implement this user control lets do the required things first. We will be needing an object for representing the data. In our case we will have to create a Person class to hold the person's first name, last name and age. Let us go ahead and create this class.

C#
public class PersonItem
{
    private string firstName;
    private string lastName;
    private int age;

    public PersonItem(string fName, string lName, int aGe)
    {
        this.firstName = fName;
        this.lastName = lName;
        this.age = aGe;
    }

    public string FirstName
    {
        get
        {
            return firstName;
        }
        set
        {
            firstName = value;
        }
    }

    public string LastName
    {
        get
        {
            return lastName;
        }
        set
        {
            lastName = value;
        }
    }

    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
}

Now we have the object ready with us, next thing we need to do is to define a class that will be used as the container for all the controls that user might add in his custom layout. The main purpose of having this container class is to provide the FindControl functionality with the custom layout in place. so we can call this class a naming Container for this control. This can be done by inheriting this class from Control class and implementing INamingContainer interface. Also, since the controls inside this container will use the data of Person class. this class will also be needing a reference to the person class. Let us create this class now.

C#
public class PersonItemContainer : Control, INamingContainer
{
    private PersonItem person = null;

    public PersonItemContainer(PersonItem item)
    {
        this.person = item;
    }

    public PersonItem Person
    {
        get
        {
            return person;
        }
        set
        {
            person = value;
        }
    }
}

Now looking at this class it is pretty evident that it contains a reference to the Person object but how will this class serve as the naming container for our user control that part is not clear. To make this class serve as the NamingContainer for our object we need to hook this class with the user control we have just created. To do that we need to create a class member of type ITemplate and a public property for this inside our user control's code behind.

C#
private ITemplate personTemplate;

public ITemplate PersonTemplate
{
    get
    {
        return personTemplate;
    }
    set
    {
        personTemplate = value;
    }
}

Still, How is it related to the NamingContainer class is not specified. to specify that we need to decorate this property with an attribute as [TemplateContainer(typeof(PersonItemContainer))] i.e.:

C#
private ITemplate personTemplate;

[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(PersonItemContainer))]
public ITemplate PersonTemplate
{
    get
    {
        return personTemplate;
    }
    set
    {
        personTemplate = value;
    }
}

The other attribute PersistenceMode is to specify that this user control will have this property an the nested element in the ASPX markup. having done this, now we need to write the code to provide the default representation of the control and hooking the controls data with the user supplied template/layout.

C#
protected void Page_Init(object sender, EventArgs e)
{
    //lets first clear the placeholders to attach then to new data
    this.phdPerson.Controls.Clear();     

    //but what if the user forgot to send the user information   
    if (Person == null)
    {  
        phdPerson.Controls.Add(new LiteralControl("Please attach the control with a person object."));         
    }
    else
    {
        //check if the templates are null we want to use simple literal
        //The user wants the default representation of the control
        if (PersonTemplate == null)
        {
            //Lets show the default representation of the control
            phdPerson.Controls.Add(new LiteralControl("First Name: "));
            phdPerson.Controls.Add(new LiteralControl(Person.FirstName));
            phdPerson.Controls.Add(new LiteralControl("<br/>"));
            phdPerson.Controls.Add(new LiteralControl("Last Name:"));
            phdPerson.Controls.Add(new LiteralControl(Person.LastName));
            phdPerson.Controls.Add(new LiteralControl("<br/>"));
            phdPerson.Controls.Add(new LiteralControl("Age: "));
            phdPerson.Controls.Add(new LiteralControl(Person.Age.ToString()));
        }
        else
        {
            PersonItemContainer container = new PersonItemContainer(this.Person);
            this.PersonTemplate.InstantiateIn(container);
            phdPerson.Controls.Add(container);
        }
    }
}

Let us now look at the complete code-behind of the user control to understand the complete logic:

C#
public partial class TemplateUCtrl : System.Web.UI.UserControl 
{
    //the template control specific stuff
    private ITemplate personTemplate;
 
    //the controls data specific stuff
    private PersonItem personItem = null;

    protected void Page_Init(object sender, EventArgs e)
    {
        //lets first clear the placeholders to attach then to new data
        this.phdPerson.Controls.Clear();     

        //but what if the user forgot to send the user information   
        if (Person == null)
        {  
            phdPerson.Controls.Add(new LiteralControl("Please attach the control with a person object."));         
        }
        else
        {
            //check if the templates are null we want to use simple literal
            //The user wants the default representation of the control
            if (PersonTemplate == null)
            {
                //Lets show the default representation of the control
                phdPerson.Controls.Add(new LiteralControl("First Name: "));
                phdPerson.Controls.Add(new LiteralControl(Person.FirstName));
                phdPerson.Controls.Add(new LiteralControl("<br/>"));
                phdPerson.Controls.Add(new LiteralControl("Last Name: "));
                phdPerson.Controls.Add(new LiteralControl(Person.LastName));
                phdPerson.Controls.Add(new LiteralControl("<br/>"));
                phdPerson.Controls.Add(new LiteralControl("Age: "));
                phdPerson.Controls.Add(new LiteralControl(Person.Age.ToString()));
            }
            else
            {
                PersonItemContainer container = new PersonItemContainer(this.Person);
                this.PersonTemplate.InstantiateIn(container);
                phdPerson.Controls.Add(container);
            }
        }
    }

    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(PersonItemContainer))]
    public ITemplate PersonTemplate
    {
        get
        {
            return personTemplate;
        }
        set
        {
            personTemplate = value;
        }
    }

    public PersonItem Person
    {
        get
        {
            return personItem;
        }
        set
        {
            personItem = value;
        }
    }    
}

Using the Templated user control

Let us see how we can use this user control now. let us drag this user control on a page and provide the Person details on the code behind as

C#
protected void Page_PreInit(object sender, EventArgs e)
{
    PersonItem item = new PersonItem("one", "two", 23);
    TemplateUCtrl1.Person = item;
}

This will show the default representation of the user control on the page.

templated user control article image

What if the user fails to provide the Person details. To check that let us drag another user control on the page and lets not assign it with a Person object.

templated user control article image

So we have used the default user control. Now if we want to customize the layout of the user control then we can do that by providing the PersonTemplate inside the user control. Lets do that for the second user control(and also supply the person object on code behind. The resulting markup and code will look as follows

XML
<body>
    <form id="form1" runat="server">
        <div>
            <uc1:TemplateUCtrl ID="TemplateUCtrl1" runat="server" />
            <br />
            <br />
            <br />
            <uc1:TemplateUCtrl ID="TemplateUCtrl2" runat="server">
                <PersonTemplate>
                    Welcome Mr. <strong>'<%# Container.Person.FirstName %>'</strong>
                    <table>
                        <tr>
                            <td>
                                Person's first Name:
                            </td>
                            <td>
                                <asp:TextBox ID="TextBox1" runat="server" 
                                  Text="<%# Container.Person.FirstName %>"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Person's Last Name:
                            </td>
                            <td>
                                <asp:TextBox ID="TextBox2" runat="server" 
                                   Text="<%# Container.Person.LastName %>"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td>
                                Person's Age:
                            </td>
                            <td>
                                <asp:TextBox ID="TextBox3" runat="server" 
                                   Text="<%# Container.Person.Age %>"></asp:TextBox>
                            </td>
                        </tr>
                    </table>
                    Give our best wishes to Mrs. <strong>'<%# Container.Person.LastName %>'</strong>
                </PersonTemplate>
            </uc1:TemplateUCtrl>
        </div>
    </form>
</body>

Code behind of the page:

C#
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_PreInit(object sender, EventArgs e)
    {
        PersonItem item = new PersonItem("one", "two", 23);

        TemplateUCtrl1.Person = item;
        TemplateUCtrl2.Person = item;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        Page.DataBind();
    }
}

Now when we run this application:

templated user control article image

Before summing up, let us look at the page and controls life cycle.

  1. The Page's Pre_Init gets called and it assigns the user control's Person object.
  2. The Control's Pre_Init gets called for the first user control we added and it renders the default representation.
  3. The Control's Pre_Init gets called for the second user control we added and it takes into account that the user has supplied some custom template and render the page accordingly.
  4. Page's Page_Load gets called which will do a Page.Bind() which will tell the templated user control to bind the data with the custom layout.

Points of Interest

Templated user controls provide a very easy way to have a control that can be reused along with some common functionality. And it doed that with the possibility for the user to define his own layout for the control. We will perhaps look into templated server controls in a separate article.

History

  • 07 June 2012: Fixed some Code formatting issues. 
  • 01 June 2012: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)