GridView Examples for ASP.NET 2.0: Working with TemplateFields

 

Click here to return to the TOC.

The GridView allows for a number of different types of columns, of which we've seen two so far:

  • The BoundField, which displays the value of specified DataSource field as text. (This is the type of column used by default when tying a GridView to a DataSource through the Smart Tag interface.)
  • The ImageField, which displays an image based on an image path in a specified DataSource field. (We saw an example of using this column type in the previous exercise.)

Another useful column type is the TemplateField, which allows for a mix of HTML markup, Web controls, and data-binding syntax. (If you are familiar with the ASP.NET 1.x DataGrid, TemplateFields are akin to the DataGrid's TemplateColumn.) One common use of a TemplateField is to perform custom logic to massage the output; another common use is to embed other Web controls within a GridView column. Let's look at examples of both.

Using Custom Logic to Alter the Output

For example, the Employees table in the Northwind database contains a field called HireDate. Rather than displaying the HireDate field, imagine that you were required to display a seniority level based on the number of days they had been employed. If an employee had been employed for 1,000 days or less they'd be labeled a "Newbie"; had they been employed between 1,000 and 4,000 days, they'd be labeled an "Associate"; those having been working between 4,000 and 8,000 days would be labeled "One of the Regulars," while those employed for more than 8,000 days would have earned the label "An Ol' Fogey."

To accomplish this you could use a TemplateField that called a method in the ASP.NET page that performed this logic and returned a string containing the appropriate seniority level. The first step would be to add a TemplateField to the GridView, which can be done through the Edit Columns option on the GridView's Smart Tag. Once the TemplateField has been added, switch back to the HTML view and create an <ItemTemplate> for the TemplateField. In this <ItemTemplate> we'll call the to-be-written method ComputeSeniorityLevel(numberOfDaysEmployed), which will return a string containing one of the valid seniority levels. The syntax for invoking a method from a TemplateField is:

<%# FunctionName(parameter1, parameter2, ..., parameterN) %>

So, to call our method in the syntax will look like:

' Visual Basic...
<%#ComputeSeniorityLevel(DateTime.Now - CType(Eval("HireDate"), DateTime))%>
// C#...
<%# ComputeSeniorityLevel(DateTime.Now - (DateTime)Eval("HireDate")) %>

To access a value from one of the DataSource fields we use the Eval(fieldName) method.

With this addition the GridView's declarative syntax looks as follows:

<asp:GridView ID="GridView1" Runat="server" 
    DataSourceID="employeeDataSource" AutoGenerateColumns="False"
    BorderWidth="1px" BackColor="White" GridLines="Vertical" 
    CellPadding="4" BorderStyle="None"
    BorderColor="#DEDFDE" ForeColor="Black">
    <FooterStyle BackColor="#CCCC99"></FooterStyle>
    <PagerStyle ForeColor="Black" HorizontalAlign="Right" 
       BackColor="#F7F7DE"></PagerStyle>
    <HeaderStyle ForeColor="White" Font-Bold="True" 
       BackColor="#6B696B"></HeaderStyle>
    <AlternatingRowStyle BackColor="White"></AlternatingRowStyle>
    <Columns>
        <asp:BoundField HeaderText="Last" DataField="LastName" 
           SortExpression="LastName"></asp:BoundField>
        <asp:BoundField HeaderText="First" DataField="FirstName" 
           SortExpression="FirstName"></asp:BoundField>
        <asp:BoundField HeaderText="Hire Date" DataField="HireDate" 
           SortExpression="HireDate"
            DataFormatString="{0:d}"></asp:BoundField>
        <asp:TemplateField HeaderText="Seniority">
            <ItemTemplate>
                <%# ComputeSeniorityLevel(DateTime.Now – 
                     (DateTime)Eval("HireDate")) %>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <SelectedRowStyle ForeColor="White" Font-Bold="True" 
         BackColor="#CE5D5A"></SelectedRowStyle>
    <RowStyle BackColor="#F7F7DE"></RowStyle>
</asp:GridView>

Note The declarative syntax shown here is for a Visual Basic ASP.NET page. If you are using C#, replace the method call in the TemplateField with the C# syntax shown before the declarative syntax.

Now all that remains is to write the ComputeSeniorityLevel() method. This method must accept an integer input—the number of days the employee has been employed—and must return a string—the seniority title. Figure 27 shows the output of the ASP.NET page when viewed through a browser.

ComputeSeniorityLevel() Method (Visual Basic)

Function ComputeSeniorityLevel(ByVal ts As TimeSpan) As String
    Dim numberOfDaysOnTheJob As Integer = ts.Days
    If numberOfDaysOnTheJob >= 0 AndAlso numberOfDaysOnTheJob <= 1000 Then
        Return "Newbie"
    ElseIf numberOfDaysOnTheJob > 1000 _
      AndAlso numberOfDaysOnTheJob <= 4000 Then
        Return "Associate"
    ElseIf numberOfDaysOnTheJob >= 4000 _
      AndAlso numberOfDaysOnTheJob <= 8000 Then
        Return "One of the Regulars"
    Else
        Return "An Ol' Fogey"
    End If
End Function
ComputeSeniorityLevel() Method (C#)
string ComputeSeniorityLevel(TimeSpan ts)
{
    int numberOfDaysOnTheJob = ts.Days;
    if (numberOfDaysOnTheJob >= 0 && numberOfDaysOnTheJob <= 1000)
        return "Newbie";
    else if (numberOfDaysOnTheJob > 1000 && numberOfDaysOnTheJob <= 4000)
        return "Associate";
    else if (numberOfDaysOnTheJob >= 4000 && numberOfDaysOnTheJob <= 8000)
        return "One of the Regulars";
    else
        return "An Ol' Fogey";
}

Aa479353.gridview_fg27(en-us,MSDN.10).gif

Figure 27

Embedding Web Controls within a GridView Column

There are many situations in which you might want one or more Web controls embedded within a GridView column. For example, if you are displaying entities from a database in a GridView that participate as the "one" in a one-to-many relationship you might want to also display the related "many" records for each row. One way to accomplish this would to use a TemplateField with a BulletedList control embedded within it. Let's look at how to accomplish exactly this with the GridView.

The Northwind database's Employees table participates in a one-to-many relationship with the Terroritories table—each employee is assigned an arbitrary number of territories. We can display the employee's associated territories in a BulletedList by adding a TemplateField to the GridView and then adding a BulletedList to the TemplateField. The resulting declarative syntax for the DataGrid would look like:

<asp:GridView ID="employeesGridView" Runat="server" 
    DataSourceID="employeesDataSource"
    AutoGenerateColumns="False" BorderWidth="1px" 
    BackColor="White" GridLines="Horizontal"
    CellPadding="3" BorderStyle="None" BorderColor="#E7E7FF" 
    OnRowDataBound="employeesGridView_RowDataBound">
    <FooterStyle ForeColor="#4A3C8C" BackColor="#B5C7DE"></FooterStyle>
    <PagerStyle ForeColor="#4A3C8C" HorizontalAlign="Right" 
       BackColor="#E7E7FF"></PagerStyle>
    <HeaderStyle ForeColor="#F7F7F7" Font-Bold="True" 
       BackColor="#4A3C8C"></HeaderStyle>
    <AlternatingRowStyle BackColor="#F7F7F7"></AlternatingRowStyle>
    <Columns>
        <asp:BoundField HeaderText="LastName" DataField="LastName" 
           SortExpression="LastName"></asp:BoundField>
        <asp:BoundField HeaderText="FirstName" DataField="FirstName" 
           SortExpression="FirstName"></asp:BoundField>
        <asp:TemplateField HeaderText="Territories"><ItemTemplate>
            <asp:BulletedList ID="bltTerritories" Runat="server" 
                DataTextField="TerritoryDescription"
                DataValueField="TerritoryDescription">
            </asp:BulletedList>
        </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <SelectedRowStyle ForeColor="#F7F7F7" Font-Bold="True" 
       BackColor="#738A9C"></SelectedRowStyle>
    <RowStyle ForeColor="#4A3C8C" BackColor="#E7E7FF"></RowStyle>
</asp:GridView>

Note that the BulletedList's DataTextField property has been set to TerritoryDescription, which is the field name in the Northwind's Territories table that provides a description of the territory. The last step is specifying the BulletedList's DataSource. There are a couple of ways to accomplish this:

  1. Set the DataSource declaratively, much like was done in the ImageField example examined earlier.
  2. Create an event handler for the GridView's RowDataBound event, which fires once for each row as its databound. In this event handler we'd programmatically reference the BulletedList, assign its DataSource, and call its DataBind() method.

The example included in this article's download uses the second approach. Specifically it loads all territories into a DataView in the Page_Load event handler; in the RowDataBound event handler the current record's EmployeeID is read and the DataView is filtered on this value. This filtered DataView is then bound to the BulletedList.

The RowDataBound and Page_Load Event Handlers (Visual Basic)

Sub employeesGridView_RowDataBound(ByVal sender As Object, _
  ByVal e As GridViewRowEventArgs)
    ' For each DataRow in the GridView, 
    ' programmatically access the BulletedList, filter
    ' the DataView based on the GridView row's 
    ' EmployeeID value and bind the filtered DataView
    ' to the BulletedList
    If e.Row.RowType = DataControlRowType.DataRow Then
        Dim bl As BulletedList = _
          CType(e.Row.FindControl("bltTerritories"), BulletedList)
        territoryData.RowFilter = "EmployeeID = " & _
          CType(e.Row.DataItem, DataRowView)("EmployeeID").ToString()
        bl.DataSource = territoryData
        bl.DataBind()
    End If
End Sub
Dim territoryData As DataView     
' this DataView will hold all of the Territories, loaded at Page_Load
Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
    ' Load all of the territories into a DataView from the SqlDataSource
    territoryData = _
      CType(territoriesDataSource.Select(DataSourceSelectArguments.Empty), 
      DataView)
End Sub

The RowDataBound and Page_Load Event Handlers (C#)

void employeesGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // For each DataRow in the GridView, 
    // programmatically access the BulletedList, filter
    // the DataView based on the GridView row's 
    // EmployeeID value and bind the filtered DataView
    // to the BulletedList
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        BulletedList bl = 
         (BulletedList)e.Row.FindControl("bltTerritories");
        territoryData.RowFilter = "EmployeeID = " + 
          ((DataRowView) e.Row.DataItem)["EmployeeID"].ToString();
        bl.DataSource = territoryData;
        bl.DataBind();
    }
}
DataView territoryData;     
// this DataView will hold all of the Territories, loaded at Page_Load
void Page_Load(object sender, EventArgs e)
{
    // Load all of the territories into a DataView from the SqlDataSource
    territoryData = 
(DataView)territoriesDataSource.Select(DataSourceSelectArguments.Empty);
}

The data is loaded in the Page_Load event handler by calling the territoriesDataSource SqlDataSource's Select() method. This returns a DataView containing the data encapsulated by the SqlDataSource control. (See the following ASP.NET page's declarative syntax to see the details for the territoriesDataSource SqlDataSource.) The RowDataBound event handler is fired for each row, including non-DataRows, such as the Header and Footer. Since we are only interested in dealing with DataRows, an If statement is used to ensure that we're dealing with a DataRow. In that case, the BulletedList is programmatically referenced and its DataSource property is set to the filtered territoryData DataView.

Figure 28 contains a screenshot of the output when viewed through a browser.

Aa479353.gridview_fg28(en-us,MSDN.10).gif

Figure 28

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:SqlDataSource ID="employeesDataSource" Runat="server" 
          SelectCommand= "SELECT [EmployeeID], [LastName], [FirstName] 
          FROM [Employees] ORDER BY [LastName], [FirstName]"
            ConnectionString="<%$ ConnectionStrings:NWConnectionString %>">
        </asp:SqlDataSource>
        <asp:SqlDataSource ID="territoriesDataSource" Runat="server" 
SelectCommand="SELECT dbo.EmployeeTerritories.EmployeeID, 
dbo.Territories.TerritoryDescription FROM dbo.Territories INNER JOIN 
dbo.EmployeeTerritories ON dbo.Territories.TerritoryID = 
dbo.EmployeeTerritories.TerritoryID"
            ConnectionString="<%$ ConnectionStrings:NWConnectionString %>">
        </asp:SqlDataSource>
        <asp:GridView ID="employeesGridView" Runat="server" 
            DataSourceID="employeesDataSource"
            AutoGenerateColumns="False" BorderWidth="1px" 
            BackColor="White" GridLines="Horizontal"
            CellPadding="3" BorderStyle="None" BorderColor="#E7E7FF" 
            OnRowDataBound="employeesGridView_RowDataBound">
            <FooterStyle ForeColor="#4A3C8C" 
             BackColor="#B5C7DE"></FooterStyle>
            <PagerStyle ForeColor="#4A3C8C" HorizontalAlign="Right" 
             BackColor="#E7E7FF"></PagerStyle>
            <HeaderStyle ForeColor="#F7F7F7" Font-Bold="True" 
             BackColor="#4A3C8C"></HeaderStyle>
            <AlternatingRowStyle BackColor="#F7F7F7"></AlternatingRowStyle>
            <Columns>
                <asp:BoundField HeaderText="LastName" DataField="LastName" 
                 SortExpression="LastName"></asp:BoundField>
                <asp:BoundField HeaderText="FirstName" 
                 DataField="FirstName" 
                 SortExpression="FirstName"></asp:BoundField>
                <asp:TemplateField HeaderText="Territories"><ItemTemplate>
                    <asp:BulletedList ID="bltTerritories" 
                      Runat="server" DataTextField="TerritoryDescription"
                        DataValueField="TerritoryDescription">
                    </asp:BulletedList>
                </ItemTemplate>
                </asp:TemplateField>
            </Columns>
            <SelectedRowStyle ForeColor="#F7F7F7" Font-Bold="True" 
             BackColor="#738A9C"></SelectedRowStyle>
            <RowStyle ForeColor="#4A3C8C" BackColor="#E7E7FF"></RowStyle>
        </asp:GridView>
    
    </div>
    </form>
</body>
</html>

Next Section: Drilling Down into Detailed Data

© Microsoft Corporation. All rights reserved.