In Depth ASP.NET using ADO.NET

Developing a Templated DataBound Control

Using the ASP.NET data binding syntax, it is easy to bind a property of a control to a single data item (or expression). In this section we will address a more complex scenario: developing a control that has Templated properties which bind to a data source and is a collection type (System.Collections.ICollection or System.Collections.IEnumerable). Templates enable a page developer to customize the presentation of data that is bound to the control. The Repeater and DataList controls are examples of Templated DataBound controls.

A templated databound control has a data source property of type ICollection or IEnumerable and one or more properties of type ITemplate. The logical container for one of the template properties defines a property to bind data to. The control implements its databinding logic in the DataBind method that inherits from Control. It overrides ChildControlsCreated to re-create the hierarchy of child controls upon postback. These steps are explained in greater detail in the following discussion.

To develop a templated databound control, define a control that implements System.Web.UI.INamingContainer.

public class TemplatedList : WebControl, INamingContainer {…}

Define a property of type System.Web.UI.ITemplate.

[TemplateContainer(typeof(ListItemTemplated))]
public virtual ITemplate ItemTemplate {
    get {
return itemTemplate;
    }
    set {
        itemTemplate = value;
    }
}


The logical container for the template (specified in the TemplateContainerAttribute attribute) must have a property to bind data to. This property is named DataItem by convention. See Developing a Templated Control for details about logical containers for a template property. The following example defines a container for the template property.

public class ListItemTemplated : TableRow, INamingContainer {
private object dataItem;
public virtual object DataItem {
    get {
        return dataItem;
    }
    set {
        dataItem = value;
    }
}


To provide databinding logic, override the DataBind method (inherited from Control). We need have the following steps:

  • To invoke the handlers (attached by the page) that evaluate databinding expressions on our control, invoke the OnDataBinding method of the base class.
  • Clear the Controls collection.
  • Clear the ViewState of the child controls.
  • Create the child controls using the data source.
  • Signal to the ASP.NET page framework to track the ViewState for our control.

The following example performs these steps. To perform the actual work of creating the child controls ChildControlsCreatedHierarchy is a helper method. See step 5 for details.

public override void DataBind() {
    // Controls with a data source property perform their
    // custom databinding by overriding DataBind to
    // evaluate any data-binding expressions on the control   
    // itself.
    base.OnDataBinding(EventArgs.Empty);
    // Reset the control's state.
    Controls.Clear();
    ClearChildViewState();
    // Create the control hierarchy using the data source.
    CreateControlHierarchy(true);
    ChildControlsCreated = true;
    TrackViewState();
}


To recreate the child controls in a postback scenario, override ChildControlsCreated. This involves creating the control hierarchy using the view state instead of the data source and clearing the Controls collection. The actual work of creating the child controls is hidden in the CreateControlHierarchy method described in step 5.

protected override void ChildControlsCreated() {
    Controls.Clear();
    if (ViewState["ItemCount"] != null) {
        // Create the control hierarchy using the view state
        // (not the data source).
        CreateControlHierarchy(false);
    }
}

Identify a data source that has null elements and when creating the control hierarchy on postback use this data source instead of the real data source. Steps 3 and 4 create the controls hierarchy using the data source and the saved view state, respectively. For the common elements of these two steps a simple data source enables a control to implement a single code path.

Please keep in mind that this step (step 5) describes implementation details utilized by the databound ASP.NET controls in the .NET Framework. The BasicDataSource class and the CreateControlHierarchy method discussed below are not in the .NET Framework but have to be defined by a control developer. We are not required to implement these elements; however, it is recommended that we use this or a similar technique to provide a common code path for creating the control hierarchy.

The following code fragment defines a simple data source.

internal sealed class BasicDataSource : ICollection {
private int dataItemCount;
public BasicDataSource(int dataItemCount) {
    this.dataItemCount = dataItemCount;
}
// Implement other methods of the ICollection interface.
. . .
public IEnumerator GetEnumerator() {
    return new BasicDataSourceEnumerator(dataItemCount);
}

private class BasicDataSourceEnumerator : IEnumerator {
    private int count;
    private int index;
    public BasicDataSourceEnumerator(int count) {
this.count = count;
this.index = -1;
    }

    public object Current {
    get {
        return null;
      }
    }
// Define other methods of the IEnumerator interface.
  }
}

BasicDataSource can be used to define the CreateControlHierarchy method, as follows.

private void CreateControlHierarchy(bool useDataSource) {
    IEnumerable dataSource = null;
    int count = -1;
    if (useDataSource == false) {
// ViewState must have a non-null value for ItemCount because this is checked
// in ChildControlsCreated.
count = (int)ViewState["ItemCount"];
if (count != -1) {
    dataSource = new BasicDataSource(count);
}
}
  else {
      dataSource = this.dataSource;
  }
  if (dataSource != null) {
      int index = 0;
      count = 0;
      foreach (object dataItem in dataSource) {


// Invoke a private helper method to create each item.
CreateItem(…);
count++;
index++;
      }
  }
  if (useDataSource) {
      // Save the number of items contained for use in round trips.
      ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
  }
}

The actual work of instantiating the template and binding the DataItem property to the data source is done by the CreateItem method. How the CreateItem method is implemented in the Templated Databound Control Sample is shown in the following code fragment. Note that the CreateItem method is an implementation detail and is not defined in the .NET Framework.

private ListItemTemplated CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) {
  ListItemTemplated item = new ListItemTemplated(itemIndex, itemType);
  ListItemTemplatedEventArgs e = new ListItemTemplatedEventArgs(item);
  if (itemTemplate != null) {
      itemTemplate.InstantiateIn(item.Cells[0]);
  }
  if (dataBind) {
      item.DataItem = dataItem;
  }
  OnItemCreated(e);
  table.Rows.Add(item);
  if (dataBind) {
      item.DataBind();
      OnItemDataBound(e);
      item.DataItem = null;
  }
  return item;
}

The full source code is available in the accompanying source code download with this article.

Using the Databound Control on a Page

The following sample shows an ASP.NET page that uses the TemplatedList control.

<%@ Page language="C#" %>
<%@ Register TagPrefix="custom" Namespace="CustomControls" Assembly="CustomControls"%>
<html>
<head>
  <title>Databound Control Sample</title>
</head>
<body>
<form runat="server">
<h3>TemplatedList Control Sample</h3>
<hr>
<custom:TemplatedList runat="server" id="FirstList"
    Font-Name="Verdana" Font-Size="16pt"
    BorderColor="Gray" BorderWidth="1px"
    CellSpacing="0" CellPadding="2" GridLines="Both"
    onItemCreated="FirstList_ItemCreated"
    onSelectedIndexChanged="FirstList_SelectedIndexChanged">
  <ItemStyle ForeColor="Black" BackColor="#EEEEEE"/>
  <AlternatingItemStyle BackColor="#DCDCDC"/>
  <SelectedItemStyle ForeColor="White" BackColor="#000084"/>
 
  <ItemTemplate>
    <asp:Button runat="server" id="selectButton" CommandName="Select"
        Text="Select" ForeColor="Blue"></asp:Button>&nbsp;&nbsp;
    <asp:Label runat="server" Text='<%# Container.DataItem %>'/>
  </ItemTemplate>
</custom:TemplatedList>
<hr>
<asp:Label runat="server" id="infoLabel"></asp:Label>
<hr>
</form>
<script runat="server">
private int selectedIndex = -1;
private void LoadData() {
    ArrayList data = new ArrayList();
    for (int i = 0; i < 10; i++) {
        data.Add("Item " + i);
    }
    FirstList.DataSource = data;
    FirstList.DataBind();
}
protected override void OnLoad(EventArgs e) {
    base.OnLoad(e);
    if (!IsPostBack) {
        LoadData();
    }
}
protected void FirstList_ItemCreated(object sender, ListItemTemplatedEventArgs e) {
    if (e.Item.ItemType == ListItemType.SelectedItem) {
        selectedIndex = e.Item.ItemIndex;
        Button selectButton = (Button)e.Item.FindControl("selectButton");
        selectButton.Enabled = false;
    }
}
protected void FirstList_SelectedIndexChanged(object sender,EventArgs e) {
    if (selectedIndex != -1) {
        Control item = FirstList.Controls[0].Controls[selectedIndex];
        Button selectButton = (Button)item.FindControl("selectButton");
        selectButton.Enabled = true;
    }
    selectedIndex = FirstList.SelectedIndex;
    infoLabel.Text = "SelectedIndex: " + selectedIndex;
    if (selectedIndex != -1) {
        Control item = FirstList.Controls[0].Controls[selectedIndex];
        Button selectButton = (Button)item.FindControl("selectButton");
        selectButton.Enabled = false;
    }
}
</script>
</body>
</html>

You might also like...

Comments

About the author

John Godel United States

John H. GODEL has an experience more than 22 years in the area of software development. He is a software engineer and architect. His interests include object-oriented and distributed computin...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” - Rick Osborne