Working with DataGrid Templates
By Doug Seven
Published: 1/10/2003
Reader Level: Beginner
Rated: 4.33 by 3 member(s).
Tell a Friend
Rate this Article
Printable Version
Discuss in the Forums

ASP.NET provides three data list controls for working with the output of a data store query, the DataGrid, DataList and Repeater. The DataGrid, in its most basic form, renders typical HTML table-grid output, the DataList renders one row and one cell per record, and the Repeater is the folding metal chair of data controls, which renders no specific output, and is fully customizable to render in the manner most appropriate for your application. While the Repeater seems to offer the most custom functionality, the DataGrid offers the most built-in functionality, such as paging, column sorting and filtering. The DataGrid may render in its basic for as a plain old table, but it also provides functionality for full customization of the output through the use of TemplateColumns.

In this tutorial you will learn how to customize the output of a DataGrid using a TemplateColumn, EditCommandColumn, HyperlinkColumn, ButtonColumn and BoundColumn.

It's Bound to be a Column

Typically when you bind data to a DataGrid, you get a basic table, one row for each record, and one cell for each column of data. While this is great for a lot of needs, it often does not address the specific layout you have in mind. While a DataList or a Repeater may seem like a quick solution, it means giving up some of the functionality that only the DataGrid offers, such as paging and column sorting. An alternate solution is to use the DataGrid, and provide custom layout options. These options exist in the form of different types of data-bound columns and custom templates. To make use of these features, the first thing to do is stop the DataGrid from automatically generating the columns based on the data in your table. This is done with the AutoGenerateColumns property of the DataGrid. Setting this property to False will stop the DataGrid from generating the columns and enable you to generate a custom layout. Listing 1 shows how to set the AutoGenerateColumns property of the DataGrid.

Listing 1 - Turning AutoGenerateColumns Off

 <asp:DataGrid runat= "server"id="myDataGrid" AutoGenerateColumns="False">
 </asp:DataGrid>

The next step is to define the layout you desire. One of the simplest ways to control output is to work with the various data-bound column types available with the DataGrid. Any custom column controls must be embedded inside a <Columns></Columns> tag pair. I'll start by showing you the BoundColumn.

A BoundColumn is nothing more that a DataGrid column bound to a specific data source field specified by you. To use a BoundColumn, place an <asp:BoundColumn> object after the <Columns> opening tag, and before the </Columns> closing tag. The BoundColumn can specify the DataField in the source it is bound to, a DataFormatString, FooterText, HeaderText, a HeaderImageUrl and a SortField. Listing 2 shows a DataGrid that uses BoundColumns to render custom output.

Listing 2 - Creating Custom Output with BoundColumns

Demo1.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Demo1.aspx.vb" Inherits="DataGridTemplates01.Demo1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <title>Demo1</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  <body>
    <form id="Form1" method="post" runat="server">
      <asp:DataGrid runat="server" id="myDataGrid" AutoGenerateColumns="False" BorderWidth="1px" Font-Names="Verdana,Arial,sans-serif"
        Font-Size="12px" BorderColor="#404040" GridLines="Horizontal" CellPadding="4">
        <AlternatingItemStyle BackColor="#E0E0E0"></AlternatingItemStyle>
        <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="Teal"></HeaderStyle>
        <Columns>
          <asp:BoundColumn DataField="CustomerID" HeaderText="ID"></asp:BoundColumn>
          <asp:BoundColumn DataField="CompanyName" HeaderText="Company Name"></asp:BoundColumn>
          <asp:BoundColumn DataField="ContactName" HeaderText="Contact Name"></asp:BoundColumn>
          <asp:BoundColumn DataField="Address" HeaderText="Address"></asp:BoundColumn>
          <asp:BoundColumn DataField="City" HeaderText="City"></asp:BoundColumn>
          <asp:BoundColumn DataField="Region" HeaderText="Region"></asp:BoundColumn>
          <asp:BoundColumn DataField="PostalCode" HeaderText="Postal Code">
            <HeaderStyle Wrap="False"></HeaderStyle>
          </asp:BoundColumn>
        </Columns>
      </asp:DataGrid>
    </form>
  </body>
</HTML>

Demo1.aspx.vb

Imports System.Data.SqlClient

Public Class Demo1
 Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

 'This call is required by the Web Form Designer.
 <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

 End Sub

 Protected WithEvents myDataGrid As System.Web.UI.WebControls.DataGrid

 'NOTE: The following placeholder declaration is required by the Web Form Designer.
 'Do not delete or move it.
 Private designerPlaceholderDeclaration As System.Object

 Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
  'CODEGEN: This method call is required by the Web Form Designer
  'Do not modify it using the code editor.
  InitializeComponent()
 End Sub

#End Region

 Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  If Not Page.IsPostBack Then
   BindData()
  End If
 End Sub

 Public SubBindData()
  DimconAsNewSqlConnection("server=localhost;database=Northwind;uid=sa;pwd=;")
  Dim cmd As New SqlCommand("SELECT * FROM Customers", con)
  Try
   con.Open()
   myDataGrid.DataSource = cmd.ExecuteReader()
   myDataGrid.DataBind()
   con.Close()
  Catch ex As Exception
   If (Not con Is Nothing) AndAlso (con.State = ConnectionState.Open) Then
    con.Close()
   End If
  End Try
 End Sub
End Class

In Listing 2 you created an ASP.NET Web Form, as seen in Figure 1, that renders a grid with a column for each DataTable column you need to display.

Figure 1 - The Output of a DataGrid Using BoundColumns

More Columns In a Bind

The other column controls, except the TemplateColumn and the EditCommandColumn, work in a similar fashion. With each one you specify a DataField in some manner (DataTextField, DataNavigateUrlField, etc.).

When you are using the HyperlinkColumn, which renders HTML hyperlink text, you can specify the DataTextField - the text displayed on the screen, the DataNavigateUrlField - the URL to link to, and a DataNavigateUrlFormatString - a format expression to apply to the text output of the control.

Like the HyperlinkColumn, the ButtonColumn also provides a DataTextField property, and a DataTextFormatString property. It also provides a CommandName property to specify which server-side method to call when the button is clicked and the form is posted. Using the CommandName property requires that the DataGrid's OnItemCommand property points to a method. The CommandName property is passed to that method and can be evaluated. This enables you to have multiple ButtonColumns in one row. The ButtonColumn can render either a LinkButton (the default) or a PushButton - an HTML <input type="button"> element. Listing 3 shows a DataGrid that has a custom layout using the different types of data-bound columns.

Listing 3 - Creating Custom Output with the Data-Bound Columns

Demo2.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Demo2.aspx.vb" Inherits="DataGridTemplates01.Demo2"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <title>Demo2</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  <body MS_POSITIONING="FlowLayout">
    <form id="Form1" method="post" runat="server">
      <asp:DataGrid id="myDataGrid" runat="server" OnItemCommand="myDataGrid_CommandItem" HeaderStyle-Font-Bold="True"
        Cellpadding="4" BorderWidth="1px" AutoGenerateColumns="False" GridLines="Horizontal" Font-Names="Verdana,Arial,sans-serif"
        Font-Size="12px" BorderStyle="Solid">
        <AlternatingItemStyle BackColor="#EFEFEF"></AlternatingItemStyle>
        <ItemStyle Font-Size="X-Small"></ItemStyle>
        <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="Teal"></HeaderStyle>
        <Columns>
          <asp:BoundColumn DataField="CustomerID" HeaderText="ID"></asp:BoundColumn>
          <asp:HyperLinkColumn DataNavigateUrlField="Url" DataTextField="CompanyName" HeaderText="Comapny Name"></asp:HyperLinkColumn>
          <asp:ButtonColumn Text="Get Details" ButtonType="PushButton" CommandName="GetDetails"></asp:ButtonColumn>
        </Columns>
      </asp:DataGrid>
    </form>
  </body>
</HTML>

Demo2.aspx.vb

Imports System.Data.SqlClient
Imports System.Web.UI.WebControls

Public Class Demo2
  Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

  End Sub
  Protected WithEvents myDataGrid As System.Web.UI.WebControls.DataGrid

  'NOTE: The following placeholder declaration is required by the Web Form Designer.
  'Do not delete or move it.
  Private designerPlaceholderDeclaration As System.Object

  Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub

#End Region

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    If Not Page.IsPostBack Then
      BindData()
    End If
  End Sub

  Public SubBindData()
    DimconAsNewSqlConnection("server=localhost;database=Northwind;uid=sa;pwd=;")
    Dim cmd As New SqlCommand("SELECT *, 'http://www.' + CustomerID + '.com' As Url FROM Customers", con)
    Try
      con.Open()
      myDataGrid.DataSource = cmd.ExecuteReader()
      myDataGrid.DataBind()
      con.Close()
    Catch ex As Exception
      If (Not con Is Nothing) AndAlso (con.State = ConnectionState.Open) Then
        con.Close()
      End If
    End Try
  End Sub

  Public Sub myDataGrid_CommandItem(ByVal Source As Object, ByVal E As DataGridCommandEventArgs)
    If E.CommandName = "GetDetails" Then
      Response.Redirect("Demo3.aspx?id=" & E.Item.Cells(0).Text)
    End If
  End Sub

End Class

In Listing 3 you  retreive Customer information from the Northwind database. Since the Northwind database does not have any URL information in it, you faked it by creating a column named Url, using the CustomerID column as a domain name. This is just for example purposes, but you can see how a HyperlinkColumn works with this.

You also created a myDataGrid_CommandItem() event handler. The DataGrid's OnItemCommand property specifies this event handler as the method to call when an item command is executed. Using a ButtonColumn, you specify the CommandName "GetDetails". In the event handler you evaluate for this CommandName and react based on the outcome of the evaluation. If the CommandName (E.CommandName) passed to the event handler is "GetDetail" then you redirect the request to a new Web Form, Demo3.aspx with a, "id" name/value pair in the QueryString. I'll show you how to build that page in a few moments. Figure 1.2 shows the output of Listing 3

Figure 2 - Using the BoundColumn, HyperlinkColumn and ButtonColumn with a DataGrid

In Figure 2 you can see that the first column, ID, is rendered as basic text. You used a BoundColumn for this field, so text is the desired output. The second column, Company Name, is an HTML hyperlink. The text displayed is the CompanyName field from the database, and the hyperlink URL is the CustomerID field from the database surrounded with http://www. and .com. Lastly, the third column is a button that will invoke the myDataGrid_CommandItem event handler, and pass "GetDetail" as the CommandName.

Templates Are the Key to True Freedom

ASP.NET TemplateColumns allow you to create fully customized output within the DataGrid. The TemplateColumn contains up to four different templates. The four template types are:

  • HeaderTemplate
  • ItemTemplate
  • EditItemTemplate
  • FooterTemplate

The HeaderTemplate is used for displaying text, images, or bound data in the header row for this column. The FooterTemplate is the same as the HeaderTemplate, but for the footer row of the column. The EditItemTemplate is an alternate display of the data in the template for use with DataGrid editing (see Editing with the ASP.NET DataGrid).

The ItemTemplate allows you to create a custom layout for the data you want to display. Within the template you can create a fully custom layout. Each record in the data source will render one iteration of the ItemTemplate. Using the data binding syntax <%# Container.DataItem("[FieldName]") %> or <%# DataBinder.Eval(Container.DataItem, "[FieldName]", "{0:[FormatString]}") %> you can embed bound data from a record in the ItemTemplate.

Listing 4 shows the Web Form you redirected to in the myDataCommand_CommandItem event handler in Listing 3. This Web Form checks the Request.QueryString object before creating the data source. If there is an "id" name/value pair in the QueryString the SQL statement is modified to select only a single record based on the "id" value. If there is no "id" name/value pair, the BindData() method select all records from the Customers table in the Northwind database.

Listing 4 - Using the TemplateColumn with the DataGrid

Demo3.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Demo3.aspx.vb" Inherits="DataGridTemplates01.Demo3"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <title>Demo3</title>
    <meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
    <meta content="Visual Basic .NET 7.1" name="CODE_LANGUAGE">
    <meta content="JavaScript" name="vs_defaultClientScript">
    <meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
  </HEAD>
  <body MS_POSITIONING="FlowLayout">
    <form id="Form1" method="post" runat="server">
      <asp:datagrid id="myDataGrid" runat="server" ItemStyle-Font-Size="x-small" HeaderStyle-Font-Bold="True"
        HeaderStyle-Font-Size="x-small" AlternatingItemStyle-BackColor="#EFEFEF" Cellpadding="4" BorderWidth="1"
        AutoGenerateColumns="False" BorderStyle="Solid" GridLines="Horizontal" BorderColor="#404040"
        Font-Names="Verdana,Arial,sans-serif" Font-Size="11px">
        <AlternatingItemStyle BackColor="#E0E0E0"></AlternatingItemStyle>
        <ItemStyle Font-Size="X-Small"></ItemStyle>
        <HeaderStyle Font-Size="X-Small" Font-Bold="True" ForeColor="White" BackColor="Teal"></HeaderStyle>
        <Columns>
          <asp:TemplateColumn>
            <HeaderTemplate>
              <b>Company Detail</b>
            </HeaderTemplate>
            <ItemTemplate>
              <table border="0" Cellpadding="4" Cellspacing="0" Width="100%" style="FONT-SIZE: 11px; FONT-FAMILY: Verdana, Arial, sans-serif">
                <tr>
                  <td colspan="4">
                    <b>
                      <%# Container.DataItem("CompanyName") %>
                    </b>
                  </td>
                </tr>
                <tr>
                  <td width="25%" valign="top"><b>Contact:</b></td>
                  <td width="25%" valign="top" nowrap>
                    <%# Container.DataItem("ContactName") %>
                  </td>
                  <td width="25%" valign="top"><b>Phone:</b></td>
                  <td width="25%" valign="top" nowrap>
                    <%# Container.DataItem("Phone") %>
                  </td>
                </tr>
                <tr>
                  <td width="25%" valign="top"><b>Title:</b></td>
                  <td width="25%" valign="top">
                    <%# Container.DataItem("ContactTitle") %>
                  </td>
                  <td width="25%" valign="top"><b>Fax:</b></td>
                  <td width="25%" valign="top" nowrap>
                    <%# Container.DataItem("Fax") %>
                  </td>
                </tr>
                <tr>
                  <td width="25%" valign="top"><b>Address:</b></td>
                  <td width="25%" valign="top" colspan="3">
                    <%# Container.DataItem("Address") %>
                    <br>
                    <%# Container.DataItem("City") %>
                    ,
                    <%# Container.DataItem("Region") %>
                    <%# Container.DataItem("PostalCode") %>
                    <br>
                    <%# Container.DataItem("Country") %>
                  </td>
                </tr>
              </table>
            </ItemTemplate>
          </asp:TemplateColumn>
        </Columns>
      </asp:datagrid></form>
  </body>
</HTML>

Demo3.aspx.vb

Imports System.Data.SqlClient

Public Class Demo3
  Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

  'This call is required by the Web Form Designer.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

  End Sub
  Protected WithEvents myDataGrid As System.Web.UI.WebControls.DataGrid

  'NOTE: The following placeholder declaration is required by the Web Form Designer.
  'Do not delete or move it.
  Private designerPlaceholderDeclaration As System.Object

  Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()
  End Sub

#End Region

  Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    If Not Page.IsPostBack Then
      BindData()
    End If
  End Sub

  Public Sub BindData()
    Dim ds As New DataSet
    Dim sda As SqlDataAdapter
    Dim strSQL As String

    If Request.QueryString.Item("id") Is Nothing Then
      strSQL = "SELECT * FROM Customers"
    Else
      strSQL = "SELECT * FROM Customers WHERE CustomerID = '" & _
      Request.QueryString.Item("id").ToString() & "'"
    End If
    sda = New SqlDataAdapter(strSQL, _
    "server=localhost;database=Northwind;uid=sa;pwd=;")
    sda.Fill(ds, "Customers")
    myDataGrid.DataSource = ds.Tables("Customers").DefaultView
    myDataGrid.DataBind()
  End Sub
End Class

In Listing 4 you create a Web Form that uses a customized DataGrid. The DataGrid has a HeaderTemplate to give the column a name, and an ItemTemplate to create a custom layout for the data. Using a TemplateColumn does not prevent you from using BoundColumns, HyperlinkColumns, ButtonColumns or EditCommandColumns in the DataGrid. All of the different data-bound columns may be used together, if the situation calls for it. Figure 3 shows the output of Listing 4 without an "id" name/value pair passed in.

Figure 3 - Using the ItemTemplate with a DataGrid

Summary

While at first it seems that only the DataList or the Repeater controls are adequate solutions when custom layout is needed, the fact is the DataGrid is truly the powerhouse data control. Not only does it include paging and sorting functionality that the other controls do not have, but it also has the framework to enable rich customization of the output. Having learned from this article, take a look around DotNetJunkies.com...you'll see a lot of the DataGrid at work.



Marketplace
(Sponsored Links)
What are the green links?
   



 
Copyright © 2007 CMP Tech LLC |
Privacy Policy (4/10/06) | Your California Privacy Rights (4/10/06) | Terms of Service | Advertising Info | About Us | Help