Contents

This paper is divided into the sections that illustrate increasingly sophisticated ways to create templates. In the initial section, for example, you will learn how to create the simplest possible template in code. Subsequent sections build on this simple template to add functionality to it. By the end of the paper, you will have learned how to create a fully functional template that includes data binding.

Requirements

This paper assumes that you are generally familiar with creating Web Forms pages and using ASP.NET server controls. It also assumes that you have at least experimented in the Web Forms Designer with creating templates for the Repeater or DataList ASP.NET server control. For background on templates, see Web Server Controls Templates.

Finally, the paper assumes that you know how to data bind controls in Web Forms pages. For background information on data binding, see Web Forms Data Binding in the Microsoft® Visual Studio® .NET documentation.

Introduction

The Repeater, DataList, and DataGrid ASP.NET server controls are designed to be data bound. These controls produce one item (for example, a row) for each element in the data source they are bound to. To define how the data is laid out, you use templates. A template is a container for other controls: the template itself is not an object with its own UI. Instead, to display information, you add controls to the template and then set the display properties of those controls (usually via data binding). When the control runs, it iterates through the data source and for each data element it creates an item — a merger of the controls in the template with the information in the data element.

The Repeater and DataList controls allow you to create more free-form output — for example, the items can be rows in a table, bullet list items, elements in a comma-delimited list, or whatever you like. The DataGrid control uses a predefined format that looks (not surprisingly) like a grid. Each data element produces a row in the grid. When the control is rendered, each item is output as a table row.

Defining Templates

If you are working with the Repeater, DataList, or DataGrid control in a Web Forms page, it is easy enough to create templates at design time. The DataList control supports a WYSIWYG-like editing window. For the Repeater control, you can create templates in HTML view as special elements nested inside the control. In the DataGrid control, you can use the Properties window to create a new template column.

But that supposes that you want to create the templates at design time. For various reasons, you might not know until run time what templates you need or what text or controls should be in them. In that case, you need to be able to create the templates on the fly. As long as you understand a few basics about creating classes and about Web Forms data binding, you can create templates in code. This paper shows how.

Note You can also create templates as Web Forms user controls and bind them dynamically to controls on your page. For details, see Creating a Templated User Control in the .NET Framework SDK documentation.

Basics of Creating Templates Programmatically

To begin, this section will show you how to create the simplest possible template in code. What you want to be able to do is to run a statement such as the following:

You can see that this creates an instance of the MyTemplate class. Where does that template class come from? You create it.

Note Creating templates in code is slightly different for DataGrid controls, since you are creating a column. You can find more about that at the end of this paper. Everything you read until then, however, is largely the same for DataGrid controls as it is for DataList and Repeater controls.

A Basic Template Class

A template class is relatively simple. At a minimum, it must do the following:

Declare that your class implements the ITemplate interface of the System.Web.UI namespace.

Implement the InstantiateIn method (the only member of the ITemplate interface). This method provides a way to insert an instance of text and controls into the specified container.

Note For background on creating classes in Microsoft Visual Basic® .NET, see Understanding Classes in the Visual Studio .NET documentation. For similar information for Microsoft Visual C#™ .NET, see class.

The point of the template, of course, is to be able to display text and controls for each item in the data source. You do this in the InstantiateIn method of your class, where you create the UI for the template.

The normal strategy for working with the InstantiateIn method is to create a new control, set the properties you want, and then add the control to the parent's Controls collection. You cannot directly add static text to the Controls collection, but you can create Literal or LiteralControl controls, set their Text properties, and then add those controls to the parent collection. The InstantiateIn method passes you a reference to the parent control (the Repeater, DataList, or DataGrid control), which makes this last step easy.

The following example shows a complete template class that displays some static text ("Item number:") and a counter. The counter is maintained as a shared or static value (depending on what language you are using) called itemcount for the class and incremented each time a new item is created.

You will see a list marching down the page, one row per data-source element.

Adding Different Types of Templates

The examples above illustrate only how to work with a single template, namely the item template. In this section, you will learn how to create a more sophisticated version of your template class, one that allows you to define a header template, footer template, an alternating item template (for the Repeater and DataList controls), and so on.

One possibility is to create a separate template class for each type of template, or a base template class and derived classes for different types of templates. This might be practical if each template is very complex or if each template is quite different.

An alternative approach is to use a single template class for all of them and specify what type of template you are creating. You can pass into your class a value that the class can use to determine what type of template to create. In fact, you can use an enumeration (ListItemType) already defined for template types in Repeater, DataList, and DataGrid controls.

The example below illustrates a more elaborate version of the examples above. The class code includes an explicit constructor this time, which accepts a single string value indicating the template type. In the InstantiateIn method, you use a Select Case or switch statement (depending on your language) to determine what type of template is being created. To show the advantages of creating different types of templates, the example creates a table with a different background color for the alternating item template.

Adding Data Binding to Templates

The template class you have defined so far displays only static data. In this section you will therefore complete the template class you have been defining by adding to it the ability to incorporate data from the control's data source.

There are various ways to get access to data from within a template class, depending on how you have created the class. A good way is the one in which the page architecture itself implements data binding. When you add controls to the template, you also add a handler for their DataBinding event. This event will be raised after the template item has been created with all its controls.

Note You might think that you could simply embed a data-binding expression as a string when creating controls in the template, as you do when defining templates at design time. However, that does not work — it turns out that data-binding expressions are turned into code at a stage of page processing that occurs before your template is being 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.

Binding an Event Handler to the Controls

Start by binding an event handler to the controls you create in the template. Using a snippet out of the example above, the code to bind a control in a template to a handler called TemplateControl_DataBinding might look like the following. (You will add the code for the handler later.)

Note that in this example the text you add to the literal control's Text property is different than in the previous example. It contains only the beginning of the table row and cell for the item template. You will complete the cell and row in the data binding event handler.

Creating the Event Handler

Now you need a handler that will be called when the control is data bound. The handler is part of your template class; you create it as a peer of the class's other methods (such as InstantiateIn).

The signature of the handler is shown in the following example. The name of this handler must match the name you used when binding the event earlier.

When your method is called, you are passed a reference to the control in the sender argument, so you can set its properties. Because the sender argument is passed as an object, you need to cast it to the right type of control — in this example, a Literal.

How do you get to data? For each element in a data source, the Repeater, DataList, or DataGrid control produces a single item (whose contents are based on the template). Each item has a property called DataItem that provides access to the data for that item — in effect, the corresponding data row.

When the data-binding event handler is called, however, you are working with a specific control, not with the template item. You need to climb up the control hierarchy from your control to the template item. The best way to do this is via your control's NamingContainer property, which returns a reference to the template item in which you created the control. (In some cases, you could also use your control's Parent property to get a reference to the container. However, if your control is nested inside another control — for instance, if your control is inside a Panel control or a Table control — then the Parent property does not return a reference to the template item.)

To bind a control in a template to specific data, therefore, you do the following:

Get a reference to the template item. You create a variable to hold the reference and then assign it the value you 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 (data column, for example) from the DataItem object and use it to set a property of the control you are binding.

The following code illustrates one way to do this, based on the examples you have been working with. It is a complete data-binding event handler for the Literal controls being created for your templates.

The code creates a variable called container of type RepeaterItem (since you have been working with the Repeater control) and then sets this variable to the NamingContainer property of the control being data bound. You can then use the reference to get to the DataItem of the container, which is an object. An easy way to do so is to call the Eval method of the DataBinder object (the same method often used in data-binding expressions), passing it a reference to the container's DataItem object and the name of the field to get.

If you have multiple types of controls in your templates, you would need to create a different data-binding event handler for each of the control types.

Creating Templates Programmatically in the DataGrid Control

The procedures described above work for both the Repeater and the DataList controls. You can also create templates programmatically for the DataGrid control. The process for creating the template itself is the same. But there are a few differences in how you use the templates in a DataGrid control:

You do not create item templates for the grid itself; instead, you create them for a column in the grid.

There are slightly different templates for a DataGrid column than for a Repeater or DataList control. A DataGrid column does not include an alternating item or separator template. However, like the DataList control, it does include an EditItem template.

A quick review: in a DataGrid control, you can have these types of columns:

Hyperlink columns — columns in which each item is a hyperlink to a URL you specify at design time.

Template columns — columns that are based on a template you specify.

As you might guess, the last type of column is the one you use programmatically created templates for. To put it another way: if you want to change the controls of a DataGrid column at run time, you should use a template column and create its template on the fly.

The following example shows how to create two template columns for a DataGrid control and then assign these templates to the columns. The first part is the class to create the templates. It is almost identical to the earlier example, except for two small changes. One is that the template class constructor accepts two parameters; the second parameter allows you to pass in the name of the column you are creating. The second change is that this example includes code to create an EditItem template, which creates a Textbox control.

Wrapping Up

The examples in this paper, although simple, show you all the basics you need for creating templates in code. Of course, the templates you create in your own applications are likely to be more complex — everything you have seen in this paper could easily be done at design time. But for now, you have all the tools you need to create templates on the fly.

Show:
Inherited
Protected

Was this page helpful?

Your feedback about this content is important.Let us know what you think.