How To: Create ASP.NET Web Server Control Templates Dynamically
When working with templated controls, you might not know until run time what templates you need or what text or controls the template should contain. In that case, you can create the templates dynamically in code.
Note
You can also create templates as user controls and bind them dynamically to controls on your page. For details, see How to: Create Templated ASP.NET User Controls.
You can create templates in code for all controls that use templates: the DataList, Repeater, GridView, FormView, DetailsView, and others. For the GridView control, you use templates to define columns instead of the row layout templates as for the other controls.
Note
There are a few differences when creating template columns for the GridView control as compared to other templated controls. For details, see Creating a Custom Column in a GridView Web Server Control.
Creating the Template Class
To create dynamic templates, you create a template class, which you then instantiate when needed.
To create a template class
Create a new class that implements the System.Web.UI..::.ITemplate interface.
Optionally, pass into the class's constructor a value that the class can use to determine what type of template to create (ItemTemplate, AlternatingItemTemplate, and so on).
Note
A type-safe way to pass the template type to the constructor is to add a parameter to the constructor with the type ListItemType. The ListItemType enumeration defines the possible template types for the Repeater, DataList, and other list controls.
In the class, implement the InstantiateIn method, which is the member of the ITemplate interface.
This method provides a way to insert an instance of text and controls into the specified container.
In the InstantiateIn method, create the controls for the template item, set their properties, and then add them to the parent's Controls collection.
You can access the parent control via the reference passed to the InstantiateIn method.
Note
You cannot directly add static text to the Controls collection, but you can create controls like the Literal control or the LiteralControl control, set their Text properties, and then add those controls to the parent collection.
For controls that require data binding, create and bind a method to handle the control's DataBinding event. This event is raised after the template item has been created with all its controls, and it provides you with an opportunity to fetch data and use it in a control.
Note
You cannot embed a data-binding expression as a string when creating controls in the template, as you do when defining templates at design time, because data-binding expressions are converted into code before your template is created.
In the handler for the DataBinding event, you have an opportunity to manipulate the contents of the control. Typically (but not necessarily), you fetch data from somewhere and assign it to the control's Text property (or other property).
Note
For information on data binding in ASP.NET Web pages, see Accessing Data with ASP.NET.
To add data binding to a dynamic template, you must do the following:
Add a data-binding event handler to the controls you create in the template.
Create the handler that you are binding to. In the handler, get the data that you want to bind to and assign it to the appropriate property of the control being bound.
Note
If you have multiple types of controls in your templates, you need to create a different data-binding event handler for each control type.
The following code example illustrates a template class named MyTemplate that implements the ITemplate interface. The MyTemplate class defines a constructor that accepts a ListItemType enumeration value to indicate what type of template is being created. Depending on the template type, the code creates different types of controls and adds them to a PlaceHolder control which is then added to the Controls collection of the parent control. For the ListItemType of Item and AlternatingItem, an event handler named Item_DataBinding is created.
The rendered Web page results in an HTML table with a different background color for the alternating item template.
PublicClass MyTemplate Implements System.Web.UI.ITemplate Dim templateType As ListItemType SubNew(ByVal type As ListItemType) templateType = type EndSubPublicSub InstantiateIn(ByVal container As System.Web.UI.Control) _ Implements System.Web.UI.ITemplate.InstantiateIn Dim ph AsNew PlaceHolder() Dim item1 AsNew Label() Dim item2 AsNew Label() item1.ID = "item1" item2.ID = "item2"SelectCase (templateType) Case ListItemType.Header ph.Controls.Add(New LiteralControl("<table border=""1"">" & _ "<tr><td><b>Category ID</b></td>" & _ "<td><b>Category Name</b></td></tr>")) Case ListItemType.Item ph.Controls.Add(New LiteralControl("<tr><td>")) ph.Controls.Add(item1) ph.Controls.Add(New LiteralControl("</td><td>")) ph.Controls.Add(item2) ph.Controls.Add(New LiteralControl("</td></tr>")) AddHandler ph.DataBinding, New EventHandler(AddressOf Item_DataBinding) Case ListItemType.AlternatingItem ph.Controls.Add(New LiteralControl("<tr bgcolor=""lightblue""><td>")) ph.Controls.Add(item1) ph.Controls.Add(New LiteralControl("</td><td>")) ph.Controls.Add(item2) ph.Controls.Add(New LiteralControl("</td></tr>")) AddHandler ph.DataBinding, New EventHandler(AddressOf Item_DataBinding) Case ListItemType.Footer ph.Controls.Add(New LiteralControl("</table>")) EndSelect container.Controls.Add(ph) EndSubEndClass
publicclass MyTemplate : System.Web.UI.ITemplate { System.Web.UI.WebControls.ListItemType templateType; public MyTemplate(System.Web.UI.WebControls.ListItemType type) { templateType = type; } publicvoid InstantiateIn(System.Web.UI.Control container) { PlaceHolder ph = new PlaceHolder(); Label item1 = new Label(); Label item2 = new Label(); item1.ID = "item1"; item2.ID = "item2"; switch (templateType) { case ListItemType.Header: ph.Controls.Add(new LiteralControl("<table border=\"1\">" + "<tr><td><b>Category ID</b></td>" + "<td><b>Category Name</b></td></tr>")); break; case ListItemType.Item: ph.Controls.Add(new LiteralControl("<tr><td>")); ph.Controls.Add(item1); ph.Controls.Add(new LiteralControl("</td><td>")); ph.Controls.Add(item2); ph.Controls.Add(new LiteralControl("</td></tr>")); ph.DataBinding += new EventHandler(Item_DataBinding); break; case ListItemType.AlternatingItem: ph.Controls.Add(new LiteralControl("<tr bgcolor=\"lightblue\"><td>")); ph.Controls.Add(item1); ph.Controls.Add(new LiteralControl("</td><td>")); ph.Controls.Add(item2); ph.Controls.Add(new LiteralControl("</td></tr>")); ph.DataBinding += new EventHandler(Item_DataBinding); break; case ListItemType.Footer: ph.Controls.Add(new LiteralControl("</table>")); break; } container.Controls.Add(ph); } }
To create the handler for the DataBinding event
Create a method that is either part of your template class and is a peer of the class's other methods (such as InstantiateIn) or a static (Shared in Visual Basic) method of the page. The handler's name must match the name you used when binding the event earlier.
Get a reference to the DataItem object that contains the data by doing the following:
Get a reference to the template item, which you can get from your control's NamingContainer property.
Use that reference to get the naming container's (the template item's) DataItem property.
Extract the individual data element from the DataItem object and use it to set a property of the control you are binding.
The following code example illustrates one way to perform data binding within a dynamic template. It shows a complete data-binding event handler for the PlaceHolder control and for the Literal and Label controls created in the previous procedure. The event handler is implemented as a static method of the page.
SharedSub Item_DataBinding(ByVal sender AsObject, ByVal e As System.EventArgs) Dim ph As PlaceHolder = CType(sender, PlaceHolder) Dim ri As RepeaterItem = CType(ph.NamingContainer, RepeaterItem) Dim item1Value AsInteger = _ Convert.ToInt32(DataBinder.Eval(ri.DataItem, "CategoryID")) Dim item2Value AsString = _ Convert.ToString(DataBinder.Eval(ri.DataItem, "CategoryName")) CType(ph.FindControl("item1"), Label).Text = item1Value.ToString() CType(ph.FindControl("item2"), Label).Text = item2Value EndSub
staticvoid Item_DataBinding(object sender, System.EventArgs e) { PlaceHolder ph = (PlaceHolder)sender; RepeaterItem ri = (RepeaterItem)ph.NamingContainer; Int32 item1Value = (Int32)DataBinder.Eval(ri.DataItem, "CategoryID"); String item2Value = (String)DataBinder.Eval(ri.DataItem, "CategoryName"); ((Label)ph.FindControl("item1")).Text = item1Value.ToString(); ((Label)ph.FindControl("item2")).Text = item2Value; }
Using the Dynamic Template
When you have a dynamic template available, you can instantiate it in code.
To use a dynamic template
Create an instance of your dynamic template, passing it an item type value if appropriate.
Assign the instance to one of the template properties of the templated control, such as the ItemTemplate, AlternatingItemTemplate, or HeaderTemplate properties.
The following code example shows how to use the dynamic template with a Repeater control. In this example, the templates are instantiated while the page is being loaded and before the control is bound to its data source.
The example below assumes that you can connect to the Northwind sample database on Microsoft SQL Server 7.0 or later. It returns a list of records from the Categories table.
ProtectedSub Page_Load(ByVal sender AsObject, _ ByVal e As System.EventArgs) HandlesMyBase.Load Dim conn AsNew System.Data.SqlClient.SqlConnection( _ ConfigurationManager.ConnectionStrings("Northwind").ConnectionString) Dim sqlDataAdapter1 As System.Data.SqlClient.SqlDataAdapter Dim dsCategories1 As System.Data.DataSet sqlDataAdapter1 = New System.Data.SqlClient.SqlDataAdapter( _ "SELECT [CategoryID], [CategoryName] FROM [Categories]", conn) dsCategories1 = New System.Data.DataSet() Repeater1.HeaderTemplate = New MyTemplate(ListItemType.Header) Repeater1.ItemTemplate = New MyTemplate(ListItemType.Item) Repeater1.AlternatingItemTemplate = New MyTemplate(ListItemType.AlternatingItem) Repeater1.FooterTemplate = New MyTemplate(ListItemType.Footer) sqlDataAdapter1.Fill(dsCategories1, "Categories") Repeater1.DataSource = dsCategories1.Tables("Categories") Repeater1.DataBind() EndSub
protectedvoid Page_Load(object sender, EventArgs e) { System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection( ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString); System.Data.SqlClient.SqlDataAdapter sqlDataAdapter1; System.Data.DataSet dsCategories1; sqlDataAdapter1 = new System.Data.SqlClient.SqlDataAdapter( "SELECT [CategoryID], [CategoryName] FROM [Categories]", conn); dsCategories1 = new System.Data.DataSet(); Repeater1.HeaderTemplate = new MyTemplate(ListItemType.Header); Repeater1.ItemTemplate = new MyTemplate(ListItemType.Item); Repeater1.AlternatingItemTemplate = new MyTemplate(ListItemType.AlternatingItem); Repeater1.FooterTemplate = new MyTemplate(ListItemType.Footer); sqlDataAdapter1.Fill(dsCategories1, "Categories"); Repeater1.DataSource = dsCategories1.Tables["Categories"]; Repeater1.DataBind(); }
Running the Complete Example
After creating all the components listed previously, add a Repeater control named Repeater1 to the page markup and run the page. The complete code and markup for the Web page (using the single-file code model) is shown below.
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
PublicClass MyTemplate
Implements System.Web.UI.ITemplate
Dim templateType As ListItemType
SubNew(ByVal type As ListItemType)
templateType = type
EndSubPublicSub InstantiateIn(ByVal container As System.Web.UI.Control) _
Implements System.Web.UI.ITemplate.InstantiateIn
Dim ph AsNew PlaceHolder()
Dim item1 AsNew Label()
Dim item2 AsNew Label()
item1.ID = "item1"
item2.ID = "item2"SelectCase (templateType)
Case ListItemType.Header
ph.Controls.Add(New LiteralControl("<table border=""1"">" & _
"<tr><td><b>Category ID</b></td>" & _
"<td><b>Category Name</b></td></tr>"))
Case ListItemType.Item
ph.Controls.Add(New LiteralControl("<tr><td>"))
ph.Controls.Add(item1)
ph.Controls.Add(New LiteralControl("</td><td>"))
ph.Controls.Add(item2)
ph.Controls.Add(New LiteralControl("</td></tr>"))
AddHandler ph.DataBinding, New EventHandler(AddressOf Item_DataBinding)
Case ListItemType.AlternatingItem
ph.Controls.Add(New LiteralControl("<tr bgcolor=""lightblue""><td>"))
ph.Controls.Add(item1)
ph.Controls.Add(New LiteralControl("</td><td>"))
ph.Controls.Add(item2)
ph.Controls.Add(New LiteralControl("</td></tr>"))
AddHandler ph.DataBinding, New EventHandler(AddressOf Item_DataBinding)
Case ListItemType.Footer
ph.Controls.Add(New LiteralControl("</table>"))
EndSelect
container.Controls.Add(ph)
EndSubEndClassSharedSub Item_DataBinding(ByVal sender AsObject, ByVal e As System.EventArgs)
Dim ph As PlaceHolder = CType(sender, PlaceHolder)
Dim ri As RepeaterItem = CType(ph.NamingContainer, RepeaterItem)
Dim item1Value AsInteger = _
Convert.ToInt32(DataBinder.Eval(ri.DataItem, "CategoryID"))
Dim item2Value AsString = _
Convert.ToString(DataBinder.Eval(ri.DataItem, "CategoryName"))
CType(ph.FindControl("item1"), Label).Text = item1Value.ToString()
CType(ph.FindControl("item2"), Label).Text = item2Value
EndSubProtectedSub Page_Load(ByVal sender AsObject, _
ByVal e As System.EventArgs) HandlesMyBase.Load
Dim conn AsNew System.Data.SqlClient.SqlConnection( _
ConfigurationManager.ConnectionStrings("Northwind").ConnectionString)
Dim sqlDataAdapter1 As System.Data.SqlClient.SqlDataAdapter
Dim dsCategories1 As System.Data.DataSet
sqlDataAdapter1 = New System.Data.SqlClient.SqlDataAdapter( _
"SELECT [CategoryID], [CategoryName] FROM [Categories]", conn)
dsCategories1 = New System.Data.DataSet()
Repeater1.HeaderTemplate = New MyTemplate(ListItemType.Header)
Repeater1.ItemTemplate = New MyTemplate(ListItemType.Item)
Repeater1.AlternatingItemTemplate = New MyTemplate(ListItemType.AlternatingItem)
Repeater1.FooterTemplate = New MyTemplate(ListItemType.Footer)
sqlDataAdapter1.Fill(dsCategories1, "Categories")
Repeater1.DataSource = dsCategories1.Tables("Categories")
Repeater1.DataBind()
EndSub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Dynamically Creating Templates</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Repeater id="Repeater1" runat="server"></asp:Repeater>
</div>
</form>
</body>
</html>
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
publicclass MyTemplate : System.Web.UI.ITemplate
{
System.Web.UI.WebControls.ListItemType templateType;
public MyTemplate(System.Web.UI.WebControls.ListItemType type)
{
templateType = type;
}
publicvoid InstantiateIn(System.Web.UI.Control container)
{
PlaceHolder ph = new PlaceHolder();
Label item1 = new Label();
Label item2 = new Label();
item1.ID = "item1";
item2.ID = "item2";
switch (templateType)
{
case ListItemType.Header:
ph.Controls.Add(new LiteralControl("<table border=\"1\">" +
"<tr><td><b>Category ID</b></td>" +
"<td><b>Category Name</b></td></tr>"));
break;
case ListItemType.Item:
ph.Controls.Add(new LiteralControl("<tr><td>"));
ph.Controls.Add(item1);
ph.Controls.Add(new LiteralControl("</td><td>"));
ph.Controls.Add(item2);
ph.Controls.Add(new LiteralControl("</td></tr>"));
ph.DataBinding += new EventHandler(Item_DataBinding);
break;
case ListItemType.AlternatingItem:
ph.Controls.Add(new LiteralControl("<tr bgcolor=\"lightblue\"><td>"));
ph.Controls.Add(item1);
ph.Controls.Add(new LiteralControl("</td><td>"));
ph.Controls.Add(item2);
ph.Controls.Add(new LiteralControl("</td></tr>"));
ph.DataBinding += new EventHandler(Item_DataBinding);
break;
case ListItemType.Footer:
ph.Controls.Add(new LiteralControl("</table>"));
break;
}
container.Controls.Add(ph);
}
}
staticvoid Item_DataBinding(object sender, System.EventArgs e)
{
PlaceHolder ph = (PlaceHolder)sender;
RepeaterItem ri = (RepeaterItem)ph.NamingContainer;
Int32 item1Value = (Int32)DataBinder.Eval(ri.DataItem, "CategoryID");
String item2Value = (String)DataBinder.Eval(ri.DataItem, "CategoryName");
((Label)ph.FindControl("item1")).Text = item1Value.ToString();
((Label)ph.FindControl("item2")).Text = item2Value;
}
protectedvoid Page_Load(object sender, EventArgs e)
{
System.Data.SqlClient.SqlConnection conn =
new System.Data.SqlClient.SqlConnection(
ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString);
System.Data.SqlClient.SqlDataAdapter sqlDataAdapter1;
System.Data.DataSet dsCategories1;
sqlDataAdapter1 = new System.Data.SqlClient.SqlDataAdapter(
"SELECT [CategoryID], [CategoryName] FROM [Categories]", conn);
dsCategories1 = new System.Data.DataSet();
Repeater1.HeaderTemplate = new MyTemplate(ListItemType.Header);
Repeater1.ItemTemplate = new MyTemplate(ListItemType.Item);
Repeater1.AlternatingItemTemplate =
new MyTemplate(ListItemType.AlternatingItem);
Repeater1.FooterTemplate = new MyTemplate(ListItemType.Footer);
sqlDataAdapter1.Fill(dsCategories1, "Categories");
Repeater1.DataSource = dsCategories1.Tables["Categories"];
Repeater1.DataBind();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Dynamically Creating Templates</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Repeater id="Repeater1" runat="server"></asp:Repeater>
</div>
</form>
</body>
</html>