Chapter 11. Using the GridView Control

<feature><title>In this Chapter</title> <objective>

GridView Control Fundamentals

</objective>
<objective>

Using Fields with the GridView Control

</objective>
<objective>

Working with GridView Control Events

</objective>
<objective>

Extending the GridView Control

</objective>
<objective>

Summary

</objective>
</feature>

The GridView control is the workhorse of the ASP.NET 2.0 Framework. It is one of the most feature-rich and complicated of all the ASP.NET controls. The GridView control enables you to display, select, sort, page, and edit data items such as database records.

Note

The GridView control supersedes the DataGrid control included in the ASP.NET 1.x Framework. The DataGrid control is still included in ASP.NET 2.0 for backward compatibility, but you should use the GridView instead because it is a more powerful control.

In this chapter, you learn everything you ever wanted to know about the GridView control. You learn how to use all the basic features of the GridView control. For example, you learn how to use this control to display, select, sort, page, and edit database records. You also learn how to use AJAX with the GridView control when sorting and paging records.

You also get the chance to tackle several advanced topics. For example, you learn how to highlight certain rows in a GridView depending on the data the row represents. You also learn how to display column summaries.

Finally, you learn how to extend the GridView control by building custom GridView fields. At the end of this chapter, we build a LongTextField, a DeleteButtonField, and a ValidatedField.

GridView Control Fundamentals

In this section, you learn how to take advantage of all the basic features of the GridView control. In particular, you learn how to display, select, sort, page, and edit database data with a GridView control. We also discuss GridView formatting options.

Displaying Data

The GridView renders its data items in an HTML table. Each data item is rendered in a distinct HTML table row. For example, the page in Listing 11.1 demonstrates how you can use the GridView to display the contents of the Movies database table (see Figure 11.1).

Displaying data with the GridView control.

Figure 11.1. Displaying data with the GridView control.

Example 11.1. ShowMovies.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show Movies</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director,InTheaters,DateReleased
           FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

In Listing 11.1, the GridView control is bound to a SqlDataSource control, which represents the Movies database table. The GridView is associated with its data source through its DataSourceID property.

Notice that the GridView control automatically renders a check box for any Boolean fields. In the case of Listing 11.1, the GridView renders a check box for the InTheaters database column. For all other types of fields, the GridView simply renders the contents of the field.

Web Standards Note

The GridView control was designed to meet XHTML and accessibility guidelines. For example, the control uses the <th> tag to render its headers. Furthermore, each header tag includes a scope="col" attribute.

Visual Web Developer Note

You can add a GridView and SqlDataSource control to a page quickly by dragging a database table from the Database Explorer window onto a page in Design view. When you drag a database table onto the page, a SqlDataSource is automatically created, which retrieves all the rows and all the columns from a database table.

The GridView control also supports programmatic databinding. In Listing 11.2, the GridView control is used to display a list of shopping list items represented by a Generic List collection.

Example 11.2. ShowShoppingList.aspx

<%@ Page Language="VB" %>
<%@ Import Namespace="System.Collections.Generic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Sub Page_Load()
        ' Build shopping list
        Dim shoppingList As New List(Of String)()
        shoppingList.Add("Bread")
        shoppingList.Add("Milk")
        shoppingList.Add("Beer")
        shoppingList.Add("Waffles")

        ' Bind to GridView
        grdShoppingList.DataSource = shoppingList
        grdShoppingList.DataBind()
    End Sub

</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show Shopping List</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdShoppingList"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Notice that the GridView is bound to the shopping list in the Page_Load() method. Its DataSource property points to the List collection, and its DataBind() method is called to load the items from the List collection and display them.

Selecting Data

You can enable a user to select a particular row in a GridView control. This is useful when you want to build single-page Master/Details forms. For example, the page in Listing 11.3 contains two GridView controls. The first GridView displays a list of movie categories. When you select a category, the second GridView displays a list of matching movies (see Figure 11.2).

Selecting a GridView row.

Figure 11.2. Selecting a GridView row.

Example 11.3. SelectGridView.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        html
        {
            background-color:silver;
            font-family:Georgia, Serif;
        }
        .gridView
        {
            float:left;
            margin-right:20px;
            background-color:white;
        }
        .gridView td, .gridView th
        {
            padding:10px;
        }
        .selectedRow
        {
            background-color:yellow;
        }
    </style>
    <title>Select GridView</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovieCategories"
        DataKeyNames="Id"
        DataSourceID="srcMovieCategories"
        AutoGenerateSelectButton="true"
        SelectedRowStyle-CssClass="selectedRow"
        CssClass="gridView"
        Runat="server" />

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        CssClass="gridView"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovieCategories"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id, Name FROM MovieCategories"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Title,Director FROM Movies
            WHERE CategoryId=@CategoryId"
        Runat="server">
        <SelectParameters>
        <asp:ControlParameter
            Name="CategoryId"
            ControlID="grdMovieCategories"
            PropertyName="SelectedValue" />
        </SelectParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>

Notice that the first GridView has its AutoGenerateSelectButton property enabled. When this property has the value True, a Select link is displayed next to each row.

You can determine which row is selected in a GridView control by using any of the following properties:

  • SelectedDataKeyReturns the DataKey object associated with the selected row (useful when there are multiple data keys).

  • SelectedIndexReturns the (zero-based) index of the selected row.

  • SelectedValueReturns the data key associated with the selected row.

  • SelectedRowReturns the actual row (GridViewRow object) associated with the selected row.

In most cases, you use the SelectedValue property to determine the value associated with a particular row. The SelectedValue property returns the data key associated with a row. The following section discusses data keys.

Note

If you want to customize the appearance of the Select link, then you can use a CommandField control instead of using the AutoGenerateSelectButton property. The CommandField control is discussed later in this chapter in the section entitled “Using Fields with the GridView Control.”

Using Data Keys

You associate a value with each row in a GridView by providing a value for the GridView control’s DataKeyNames property. You can assign the name of a single database column to this property or you can assign a comma-separated list of column names to this property.

For example, the Employees database table uses two columns—the employee first and last name—as a primary key. The page in Listing 11.4 displays employee details when you select a particular employee (see Figure 11.3).

Displaying employee details.

Figure 11.3. Displaying employee details.

Example 11.4. SelectDataKeys.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        html
        {
            background-color:silver;
        }
        .content
        {
            width:600px;
            margin:auto;
            background-color:white;
        }
        .column
        {
            float:left;
            padding:10px;
            width:265px;
        }
        .column td,.column th
        {
            padding:5px;
            font:14px Georgia, Serif
        }
        .selectedRow
        {
            background-color:yellow;
        }
    </style>
    <title>Select Data Keys</title>
</head>
<body>
    <form id="form1" runat="server">
    <div class="content">
    <div class="column">

    <asp:GridView
        id="grdEmployees"
        DataSourceID="srcEmployees"
        DataKeyNames="LastName,FirstName"
        AutoGenerateSelectButton="true"
        SelectedRowStyle-CssClass="selectedRow"
        Runat="server" />

    </div>
    <div class="column">

    <asp:DetailsView
        id="dtlEmployees"
        DataSourceID="srcEmployeeDetails"
        Runat="server" />

    </div>

    <br style="clear:both" />
    </div>

    <asp:SqlDataSource
        id="srcEmployees"
        ConnectionString="<%$ ConnectionStrings:Employees %>"
        SelectCommand="SELECT LastName,FirstName
            FROM Employees"
        Runat="server" />

    <asp:SqlDataSource
        id="srcEmployeeDetails"
        ConnectionString="<%$ ConnectionStrings:Employees %>"
        SelectCommand="SELECT * FROM Employees
            WHERE FirstName=@FirstName AND LastName=@LastName"
        Runat="server">
        <SelectParameters>
        <asp:ControlParameter
            Name="FirstName"
            ControlID="grdEmployees"
            PropertyName='SelectedDataKey("FirstName")' />
        <asp:ControlParameter
            Name="LastName"
            ControlID="grdEmployees"
            PropertyName='SelectedDataKey("LastName")' />
        </SelectParameters>
     </asp:SqlDataSource>

    </form>
</body>
</html>

In Listing 11.4, notice that the SelectedDataKey property is used to retrieve the primary key of the selected employee. The SelectedDataKey property is used in both of the ControlParameters contained in the second SqlDataSource control. If you use SelectedValue instead of SelectedDataKey, then you can return only the value of the first data key and not both values.

A GridView stores data keys in a collection called the DataKeys collection. This collection is exposed by the GridView control’s DataKeys property. You can retrieve the data key associated with any row by using a statement that looks like this:

DIM key As Object = Gridview1.DataKeys(6).Value

This statement returns the value of the data key associated with the seventh row in the GridView (remember that the rows collection is zero based).

If you have assigned multiple data keys to each row, then you can use a statement that looks like this:

DIM key As Object = GridView1.DataKeys(6).Values("LastName")

This statement retrieves the value of the LastName key for the seventh row in the GridView.

Sorting Data

You can sort the rows rendered by a GridView control by enabling the AllowSorting property. For example, the page in Listing 11.5 illustrates how you can sort the contents of the Movies database table.

Example 11.5. SortGrid.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Sort Grid</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowSorting="true"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,DateReleased FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

When AllowSorting has the value True, column headers are rendered as links. When you click a column header, you can sort the rows contained in the GridView in the order of the selected column.

Note

When using explicitly specified fields with a GridView, such as BoundFields, you need to specify values for the fields’s SortExpression properties. Otherwise, nothing happens when you click a header.

Notice that the GridView supports ascending and descending sorts. In other words, if you click a column header more than once, the rows toggle between being sorted in ascending and descending order.

Sorting with AJAX

By default, whenever you click a column header to sort the rows contained in a GridView, the page containing the GridView is posted back to the server. When sorting records with the GridView control, you can avoid posting the entire page back to the server by taking advantage of AJAX (Asynchronous JavaScript and XML).

You enable AJAX by including the EnableSortingAndPagingCallbacks property when declaring the GridView. The page in Listing 11.6 illustrates how you can take advantage of AJAX when sorting records.

Example 11.6. AjaxSorting.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>AJAX Sorting</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <%= DateTime.Now.ToString("T") %>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowSorting="true"
        EnableSortingAndPagingCallbacks="true"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,DateReleased FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

The page in Listing 11.6 displays the current time at the top of the page. Notice that the time is not updated when you sort the records in the GridView. The entire page is not posted back to the server; only the content of the GridView control is updated.

Warning

When using AJAX with the GridView control, you cannot use TemplateFields. Furthermore, you cannot display a Select button when AJAX is enabled.

Customizing the Sorting Interface

You can customize the appearance of the sort links by handling the GridView control’s RowDataBound event. This event is raised for each row rendered by the GridView after the GridView is bound to its data source.

For example, the page in Listing 11.7 displays an image that represents whether a column is sorted in ascending or descending order (see Figure 11.4).

Example 11.7. ImageSorting.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub grdMovies_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
        If e.Row.RowType = DataControlRowType.Header Then
            For Each cell As TableCell In e.Row.Cells
                Dim sortLink As LinkButton = CType(cell.Controls(0), LinkButton)
                If sortLink.Text = grdMovies.SortExpression Then
                    If grdMovies.SortDirection = SortDirection.Ascending Then
                        sortLink.Text += " <img src='asc.gif' title='Sort ascending' />"
                    Else
                        sortLink.Text += " <img src='desc.gif' title='Sort descending' />"
                    End If
                End If
            Next
        End If
    End Sub

</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        img
        {
            border:0px;
        }
    </style>
    <title>Image Sorting</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowSorting="true"
        Runat="server" OnRowDataBound="grdMovies_RowDataBound" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Displaying an image when sorting.

Figure 11.4. Displaying an image when sorting.

In Listing 11.7, the image is added to the header row in the grdMovies_RowDataBound() method. The current row’s RowType property is checked to verify that the row is a header row. Next, an HTML <img> tag is added to the LinkButton that matches the column that is currently selected for sorting.

If you need to completely customize the appearance of the sorting user interface, then you can call the GridView control’s Sort() method programmatically. This approach is illustrated in the page in Listing 11.8 (see Figure 11.5).

Example 11.8. CustomSorting.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Sub btnSort_Click(s As object, e As EventArgs)
        grdMovies.Sort(ddlSort.Text, SortDirection.Ascending)
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Custom Sorting</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:DropDownList
        id="ddlSort"
        Runat="server">
        <asp:ListItem Text="Id" />
        <asp:ListItem Text="Title" />
        <asp:ListItem Text="Director" />
    </asp:DropDownList>
    <asp:Button
        id="btnSort"
        Text="Sort"
        Runat="server" OnClick="btnSort_Click" />

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Displaying a custom sorting interface.

Figure 11.5. Displaying a custom sorting interface.

The page in Listing 11.8 includes a DropDownList control, which you can use to sort the contents of the GridView. When a list item is selected from the DropDownList control and the Sort button is clicked, the btnSort_Click() method executes. This method calls the Sort() method of the GridView control to sort the contents of the GridView.

Paging Through Data

When working with a large number of database rows, it is useful to be able to display the rows in different pages. You can enable paging with the GridView control by enabling its AllowPaging property.

For example, the page in Listing 11.9 enables you to page through the records in the Movies database table (see Figure 11.6).

Example 11.9. PageGrid.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Page Grid</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowPaging="true"
        PageSize="3"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Paging through records in a GridView control.

Figure 11.6. Paging through records in a GridView control.

The GridView in Listing 11.9 displays three database records per page. You can modify the number of records displayed per page by modifying the GridView control’s PageSize property. (If you don’t specify a value for PageSize, then the GridView defaults to displaying 10 records per page.)

Warning

This section describes how you can enable user interface paging with the GridView control. When you use user interface paging, all the database records are loaded into memory and divided into separate pages. For example, when paging through a database table that contains three billion database records, all three billion records are loaded into memory even when you display only three records in a single page. You should not use user interface paging when working with large sets of data. Instead, use the ObjectDataSource control’s support for data source paging. This option is discussed in Chapter 15, “Using the ObjectDataSource Control.”

Paging with AJAX

The default behavior of the GridView control is to post back to the server each and every time you navigate to a new page of records. However, there is an alternative. You can take advantage of AJAX (Asynchronous JavaScript and XML) when paging through records with the GridView control.

The page in Listing 11.10 illustrates how you can use AJAX with the GridView control.

Example 11.10. AjaxPaging.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>AJAX Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <%= DateTime.Now.ToString("T") %>
    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowPaging="true"
        EnableSortingAndPagingCallbacks="true"
        PageSize="3"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Notice that the GridView in Listing 11.10 includes an EnableSortingAndPagingCallbacks property that is assigned the value True. This property enables AJAX.

The page in Listing 11.10 displays the current time at the top of the page. When you page through the records rendered by the GridView control, notice that the time does not change. Only the contents of the GridView control are modified.

Warning

When using AJAX with the GridView control, you cannot use TemplateFields. Furthermore, you cannot display a Select button when AJAX is enabled.

Customizing the Paging Interface

By default, when paging is enabled, the GridView renders a list of page numbers at the bottom of the grid. You can modify the user interface for paging through records by modifying the GridView control’s PagerSettings property. For example, the page in Listing 11.11 contains a GridView that renders First, Previous, Next, and Last links at both the top and bottom of the GridView (see Figure 11.7).

Example 11.11. PageGridPreviousNext.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Page Grid Previous Next</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowPaging="true"
        PageSize="3"
        PagerSettings-Mode="NextPreviousFirstLast"
        PagerSettings-Position="TopAndBottom"
        PagerStyle-HorizontalAlign="Center"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Modifying pager settings.

Figure 11.7. Modifying pager settings.

The PagerSettings class supports the following properties:

  • FirstPageImageUrlEnables you to display an image for the first page link.

  • FirstPageTextEnables you to specify the text for the first page link.

  • LastPageImageUrlEnables you to display an image for the last page link.

  • LastPageTextEnables you to specify the text for the last page link.

  • ModeEnables you to select a display mode for the pager user interface. Possible values are NextPrevious, NextPreviousFirstLast, Numeric, and NumericFirstLast.

  • NextPageImageUrlEnables you to display an image for the next page link.

  • NextPageTextEnables you to specify the text for the next page link.

  • PageButtonCountEnables you to specify the number of page number links to display.

  • PositionEnables you to specify the position of the paging user interface. Possible values are Bottom, Top, TopAndBottom.

  • PreviousPageImageUrlEnables you to display an image for the previous page link.

  • PreviousPageTextEnables you to specify the text for the previous page link.

  • VisibleEnables you to hide the paging user interface.

The PageButtonCount requires more explanation. Imagine that you are displaying the contents of a database table that contains 3 billion records and you are displaying two records per page. In that case, you would need to render an overwhelming number of page numbers. The PageButtonCount property enables you to limit the number of page numbers displayed at once. When PageButtonCount has a value less than the number of page numbers, the GridView renders ellipsis, which enables a user to move between ranges of page numbers.

The GridView control includes a PagerTemplate, which enables you to completely customize the appearance of the paging user interface. For example, the page in Listing 11.12 uses a Menu control in a PagerTemplate to display a list of page numbers. The PagerTemplate also includes two LinkButton controls, which represent a Previous and Next link (see Figure 11.8).

Example 11.12. PageTemplateGrid.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub grdMovies_DataBound(ByVal sender As Object, ByVal e As EventArgs)
        Dim menuPager As Menu = CType(grdMovies.BottomPagerRow.FindControl("menuPager"), Menu)
        For i As Integer = 0 To grdMovies.PageCount - 1
            Dim item As New MenuItem()
            item.Text = String.Format("[{0}]", i + 1)
            item.Value = i.ToString()
            If grdMovies.PageIndex = i Then
                item.Selected = True
            End If
            menuPager.Items.Add(item)
        Next
    End Sub

    Protected Sub menuPager_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs)
        grdMovies.PageIndex = Int32.Parse(e.Item.Value)
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        .menu td
        {
            padding:5px 0px;
        }
        .selectedPage a
        {
            font-weight:bold;
            color:red;
        }
    </style>
    <title>Page Template Grid</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AllowPaging="true"
        PageSize="3"
        Runat="server" OnDataBound="grdMovies_DataBound">
        <PagerTemplate>
        <table>
        <tr><td>
        <asp:LinkButton
            id="lnkPrevious"
            Text="&lt; Prev"
            CommandName="Page"
            CommandArgument="Prev"
            ToolTip="Previous Page"
            Runat="server" />
        </td><td>
        <asp:Menu
            id="menuPager"
            Orientation="Horizontal"
            OnMenuItemClick="menuPager_MenuItemClick"
            StaticSelectedStyle-CssClass="selectedPage"
            CssClass="menu"
            Runat="server" />
        </td><td>
        <asp:LinkButton
            id="lnkNext"
            Text="Next &gt;"
            CommandName="Page"
            CommandArgument="Next"
            ToolTip="Next Page"
            Runat="server" />
        </td></tr>
        </table>
        </PagerTemplate>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Using a template for the paging interface.

Figure 11.8. Using a template for the paging interface.

The GridView in Listing 11.12 includes a PagerTemplate that contains a Menu control. When the GridView is bound to its data source, the grdMovies_DataBound() method executes and creates menu items that correspond to each page in the GridView. When you click a menu item, the page index of the GridView is updated.

To customize the PagerTemplate, you can add button controls to the template such as the Button, ImageButton, or LinkButton controls. Set the CommandName property of the button control to the value Page and the CommandArgument property to one of the following values:

  • NextCauses the GridView to display the next page of data items.

  • PrevCauses the GridView to display the previous page of data items.

  • FirstCauses the GridView to display the first page of data items.

  • LastCauses the GridView to display the last page of data items.

  • Integer Value—Causes the GridView to display a particular page of data items.

Editing Data

The GridView control also enables you to edit database data. The amazing thing is that you can use the GridView to edit the contents of a database table row without writing a single line of code.

The page in Listing 11.13 illustrates how you can update and delete records in the Movies database table by using the GridView control (see Figure 11.9).

Editing records with the GridView.

Figure 11.9. Editing records with the GridView.

Example 11.13. EditGrid.aspx

<%@ Page Language="VB" MaintainScrollPositionOnPostback="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Edit GridView</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateEditButton="true"
        AutoGenerateDeleteButton="true"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        UpdateCommand="UPDATE Movies SET Title=@Title, Director=@Director
            WHERE Id=@Id"
        DeleteCommand="DELETE Movies WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>

In Listing 11.13, notice that the GridView control has both its AutoGenerateEditButton and AutoGenerateDeleteButton properties enabled. When these properties are enabled, Edit and Delete links are automatically rendered next to each row in the GridView.

Note

You can take advantage of the <%@ Page %> directive’s MaintainScrollPositionOnPostback attribute to scroll a page back automatically to the same position whenever the page is posted back to the server. For example, if you add this attribute and click an Edit link rendered by a GridView, the page automatically scrolls to the record being edited. This attribute works with Internet Explorer 6+, Firefox 1+, and Opera 8+.

When you click an Edit link, you can edit a particular database row. The GridView automatically renders a check box for any Boolean columns and a text field for any other type of column.

Note

The GridView control does not support inserting new records into a database table. If you need to insert new records, use either the DetailsView or FormView control.

Furthermore, notice that the GridView control includes a DataKeyNames property. When editing and deleting rows with the GridView, you need to assign the name of the primary key field from the database table being modified to this property. In Listing 11.13, the Movies ID column is assigned to the DataKeyNames property.

Finally, notice that the SqlDataSource control associated with the GridView control includes a SelectCommand, UpdateCommand, and DeleteCommand property. These properties contain the SQL statements that are executed when you display, insert, and delete records with the GridView control.

The SQL statements contained in both the UpdateCommand and DeleteCommand include parameters. For example, the UpdateCommand looks like this:

UPDATE Movies SET Title=@Title, Director=@Director
WHERE Id=@Id

The @Title and @Director parameters represent the new values for these columns that a user enters when updating a record with the GridView control. The @Id parameter represents the primary key column from the database table.

Handling Concurrency Issues

The GridView control can track both the original and modified value of each database column. The GridView control tracks the original and updated values of a column so that you can handle concurrency conflicts. Imagine that you are building a massive order entry system. Your company has hundreds of employees modifying orders with a page that contains a GridView control. If two employees open the same customer record at the same time, then one employee might overwrite changes made by the other employee.

You can prevent this type of concurrency conflict by using the page in Listing 11.14.

Example 11.14. Concurrency.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Concurrency</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateEditButton="true"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="original_{0}"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        UpdateCommand="UPDATE Movies SET Title=@Title, Director=@Director
            WHERE Id=@original_Id AND Title=@original_Title
            AND Director=@original_Director"
        Runat="server" />

    </div>
    </form>
</body>
</html>

In Listing 11.14, the SqlDataSource control includes both a ConflictDetection and OldValuesParameterFormatString property. These two properties cause the SqlDataSource control to track both the original and modified versions of each column.

The ConflictDetection property can have one of the following two values:

  • CompareAllValues

  • OverwriteChanges

By default, the ConflictDetection property has the value OverwriteChanges, which causes the SqlDataSource control to overwrite the previous value of a column with its new value. When ConflictDetection is set to the value CompareAllValues, the SqlDataSource tracks both the original and modified version of each column.

The OldValuesParameterFormatString property is used to provide a distinguishing name for the original value of a column. For example, the value of the SqlDataSource control’s UpdateCommand looks like this:

UPDATE Movies SET Title=@Title, Director=@Director
WHERE Id=@original_Id AND Title=@original_Title
AND Director=@original_Director

The @original_Id, @original_Title, and @original_Director parameters represent the original values of these columns. If the value of the Title or Director columns has changed in the underlying database, then the record is not updated. In other words, if someone else beats you to the record change, then your modifications are ignored.

Displaying Empty Data

The GridView includes two properties that enable you to display content when no results are returned from the GridView control’s data source. You can use either the EmptyDataText property or the EmptyDataTemplate property to handle empty data.

For example, the page in Listing 11.15 contains a movie search form. If you enter a search string that does not match the start of any movie title, then the contents of the EmptyDataText property are displayed (see Figure 11.10).

Displaying a message when no records match.

Figure 11.10. Displaying a message when no records match.

Example 11.15. ShowEmptyDataText.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Sub btnSubmit_Click(sender As object, e As EventArgs)
        grdMovies.Visible = True
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show Empty Data Text</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:TextBox
        id="txtTitle"
        Runat="server" />
    <asp:Button
        id="btnSubmit"
        Text="Search"
        OnClick="btnSubmit_Click"
        Runat="server" />
    <hr />

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        EmptyDataText="<img src='sad.gif'/> No Matching Movies!"
        Visible="false"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Title,Director FROM Movies
            WHERE Title LIKE @Title+'%'"
        Runat="server">
        <SelectParameters>
        <asp:ControlParameter
            Name="Title"
            ControlID="txtTitle"
            PropertyName="Text" />
        </SelectParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>

If you use the search form in Listing 11.15 to search for a movie that doesn’t exist, then an icon of a frowning face and the text No Matching Movies! is displayed.

Notice that the initial value of the GridView control’s Visible property is set to False. The GridView is displayed only after you click the button. If you did not add this additional logic, then the EmptyDataText message would be displayed when the page is first opened.

As an alternative to using the EmptyDataText property, you can use an EmptyDataTemplate to display content when a data source does not return any results. For example, the page in Listing 11.16 prompts you to enter a new movie when no matching movies are found (see Figure 11.11).

Displaying a template when no records match.

Figure 11.11. Displaying a template when no records match.

Example 11.16. ShowEmptyDataTemplate.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs)
        grdMovies.Visible = True
    End Sub

    Protected Sub dtlMovie_ItemInserted(ByVal sender As Object, ByVal e As DetailsViewInsertedEventArgs)
        txtTitle.Text = CType(e.Values("Title"), String)
        grdMovies.DataBind()
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show Empty Data Template</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:TextBox
        id="txtTitle"
        Runat="server" />
    <asp:Button
        id="btnSubmit"
        Text="Search"
        OnClick="btnSubmit_Click"
        Runat="server" />
    <hr />

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        Visible="false"
        Runat="server">
        <EmptyDataTemplate>
        <p>
        No matching movies were found. If you would like
        to add a new movie, enter it in the form below.
        </p>
        <asp:DetailsView
            id="dtlMovie"
            DataSourceID="srcMovies"
            DefaultMode="Insert"
            AutoGenerateInsertButton="true"
            AutoGenerateRows="false"
            Runat="server" OnItemInserted="dtlMovie_ItemInserted">
            <Fields>
            <asp:BoundField
                HeaderText="Title:"
                DataField="Title" />
            <asp:BoundField
                HeaderText="Director:"
                DataField="Director" />
            </Fields>
        </asp:DetailsView>

        </EmptyDataTemplate>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Title,Director FROM Movies
            WHERE Title LIKE @Title+'%'"
        InsertCommand="INSERT Movies (Title, Director)
            VALUES (@Title, @Director)"
        Runat="server">
        <SelectParameters>
        <asp:ControlParameter
            Name="Title"
            ControlID="txtTitle"
            PropertyName="Text" />
        </SelectParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>

The EmptyDataTemplate in Listing 11.16 contains some text and a DetailsView control that you can use to insert a new movie into the Movies database table. You can add any HTML content or ASP.NET controls to an EmptyDataTemplate that you need.

Formatting the GridView Control

The GridView control includes a rich set of formatting properties that you can use to modify its appearance. I recommend that you don’t use most of these properties because using these properties results in bloated pages. Instead, I recommend that you use Cascading Style Sheets to format the GridView control.

The GridView control includes a CssClass property. The control also exposes several Style objects that include the CssClass property:

  • AlternatingRowStyleEnables you to format every other row.

  • FooterStyleEnables you to format the footer row.

  • HeaderStyleEnables you to format the header row.

  • PagerStyleEnables you to format the pager row.

  • RowStyleEnables you to format each row.

  • SelectedRowStyleEnables you to format the selected row.

For example, the page in Listing 11.17 contains a GridView that is formatted with Cascading Style Sheet rules (see Figure 11.12).

A GridView control formatted with CSS.

Figure 11.12. A GridView control formatted with CSS.

Example 11.17. FormatGrid.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        .grid
        {
            font:16px Arial, Sans-Serif;
        }
        .grid td, .grid th
        {
            padding:10px;
        }
        .header
        {
            text-align:left;
            color:white;
            background-color:blue;
        }
        .row td
        {
            border-bottom:solid 1px blue;
        }
        .alternating
        {
            background-color:#eeeeee;
        }
        .alternating td
        {
            border-bottom:solid 1px blue;
        }
    </style>
    <title>Format Grid</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        GridLines="None"
        CssClass="grid"
        HeaderStyle-CssClass="header"
        RowStyle-CssClass="row"
        AlternatingRowStyle-CssClass="alternating"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

In Listing 11.17, the column header text is left aligned. Also notice that banding is added to the table rendered by the GridView. Alternating rows are rendered with a gray background.

The GridView control has a few formatting properties that you might need to use even when formatting a GridView with Cascading Style Sheets. For example, in Listing 11.17, the GridLines property was assigned the value None to suppress the default rendering of borders around each table cell. Here is a list of these properties.

  • GridLinesRenders borders around table cells. Possible values are Both, Vertical, Horizontal, and None.

  • ShowFooterWhen True, renders a footer row at the bottom of the GridView.

  • ShowHeaderWhen True, renders a header row at the top of the GridView.

Using View State with the GridView Control

By default, the GridView control stores the values of all the columns contained in all the rows that it renders in ViewState. In other words, all the rows that the GridView retrieves from its data source are stuffed in a hidden form field.

The advantage of using ViewState is that the GridView does not need to query the database for the same set of records every time a page containing the GridView is displayed. The records are retrieved from the database only when the page first loads.

The disadvantage of using ViewState is that it means that a lot of information might need to be pushed over the wire to a user’s browser. All ViewState information is stored in a hidden form field. When a large number of rows are displayed, this hidden form field can become enormous. When ViewState becomes too large, it can significantly impact a page’s performance.

You can disable ViewState by assigning the value False to the GridView control’s EnableViewState property. Even if you disable ViewState, you can still display, sort, page, and edit database records with the GridView control (The GridView uses ControlState to track vital state information.) When displaying a large number of records, you should turn ViewState off.

You can view the amount of ViewState that a GridView is using by enabling tracing for the page that contains the GridView. Add the Trace="True" attribute to the Page directive like this:

<%@ Page Trace="true" %>

When tracing is enabled, a Control Tree section is appended to the end of a page when the page is rendered in a browser. The Control Tree section displays the ViewState size used by each control contained in the page.

Using Fields with the GridView Control

In all the sample code in the previous section, the GridView control was used to automatically render an HTML table that contains a list of data items. However, there is a problem with allowing the GridView to render its columns automatically. The result does not look very professional.

For example, the column headers are simply the names of the underlying database columns. Displaying the column name EntryDate as a column header seems, well, a little cheesy. We really need to be able to specify custom column headers.

Another problem with enabling the GridView to render its columns automatically is that you give up any control over column formatting. For example, the BoxOfficeTotals column is displayed as a decimal amount without any currency formatting. The EntryDate column always displays in short-date and long-time format.

Furthermore, it would be nice to be able to display the values of certain columns as images, drop-down lists, or hyperlinks. If you use the automatically generated columns, then you are stuck with the user interface you are given.

The solution to all these problems is to specify explicitly the fields that a GridView displays. The GridView control supports the following types of fields:

  • BoundFieldEnables you to display the value of a data item as text.

  • CheckBoxFieldEnables you to display the value of a data item as a check box.

  • CommandFieldEnables you to display links for editing, deleting, and selecting rows.

  • ButtonFieldEnables you to display the value of a data item as a button (image button, link button, or push button).

  • HyperLinkFieldEnables you to display the value of a data item as a link.

  • ImageFieldEnables you to display the value of a data item as an image.

  • TemplateFieldEnables you to customize the appearance of a data item.

The following sections examine how you can take advantage of each of these different types of fields.

Note

You can create custom fields that work with the GridView control. This option is explored in the final section of this chapter.

Using BoundFields

A BoundField always displays the value of a data item as text when a row is in normal display mode. When a row is selected for editing, a BoundField displays the value of a data item in a single line text field.

The most important three properties of the BoundField class are the DataField, DataFormatString, and HeaderText properties. The page in Listing 11.18 illustrates how to use these properties when displaying a list of movies (see Figure 11.13).

Using BoundFields with the GridView control.

Figure 11.13. Using BoundFields with the GridView control.

Example 11.18. ShowBoundField.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show BoundField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <asp:BoundField
            DataField="Title"
            HeaderText="Movie Title" />
        <asp:BoundField
            DataField="Director"
            HeaderText="Movie Director" />
        <asp:BoundField
            DataField="BoxOfficeTotals"
            DataFormatString="{0:c}"
            HtmlEncode="false"
            HeaderText="Box Office Totals" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT * FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Notice that the GridView control includes an AutoGenerateColumns property that is assigned the value False. If you don’t disable automatically generated columns, then both columns represented by the BoundFields and all the columns from the data source are displayed redundantly.

In Listing 11.18, BoundFields are used to display the Title, Director, and BoxOfficeTotals columns. The DataField property is used to represent the column that a BoundField displays. The HeaderText property determines the column header.

The BoundField used to display the BoxOfficeTotals column includes a DataFormatString property. This format string formats the values of the BoxOfficeTotals column as a currency amount. Notice that the BoundField also includes an HtmlEncode property that is set to the value False. When formatting a field, you need to prevent the GridView from encoding the field.

Warning

When using the DataFormatString property, always set the HtmlEncode property to False. Otherwise, the formatting is not applied. This is a change from the previous version of ASP.NET.

Note

For more information about string formatting, see the Formatting Types topic in the Microsoft .NET Framework SDK 2.0 documentation.

A BoundField supports several other useful properties:

  • AccessibleHeaderTextEnables you to add an HTML abbr attribute to the column header.

  • ApplyFormatInEditModeEnables you to apply the DataFormatString to the field when the row is in edit display mode.

  • ConvertEmptyStringToNullEnables you to convert an empty string "" into the value Nothing (null) when editing a column.

  • DataFieldEnables you to specify the name of the field that the BoundField displays.

  • DataFormatStringEnables you to use a format string to format a data item.

  • FooterStyleEnables you to format the column footer.

  • FooterTextEnables you to display text in the column footer.

  • HeaderImageUrlEnables you to display an image in the column header.

  • HeaderStyleEnables you to format the column header.

  • HeaderTextEnables you to display text in the column header.

  • HtmlEncodeEnables you to HTML-encode the value of a data item, which enables you to avoid script injection attacks.

  • InsertVisibleEnables you to not display a column when inserting a new record (does not apply to the GridView control).

  • ItemStyleEnables you to format a data item.

  • NullDisplayTextEnables you to specify text that is displayed when a data item has the value Nothing (null).

  • ReadOnlyEnables you to prevent the data item from being edited in edit mode.

  • ShowHeaderEnables you to display the column header.

  • SortExpressionEnables you to associate a sort expression with the column.

  • VisibleEnables you to hide a column.

Using CheckBoxFields

A CheckBoxField, as you can probably guess, displays a check box. When a row is not in edit mode, the check box is displayed but it is disabled.

The page in Listing 11.19 illustrates how you can use a CheckBoxField (see Figure 11.14).

Using the CHECKBOXFIELD with the GRIDVIEW control.

Figure 11.14. Using the CHECKBOXFIELD with the GRIDVIEW control.

Example 11.19. ShowCheckBoxField.aspx.

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show CheckBoxField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateColumns="false"
        AutoGenerateEditButton="true"
        Runat="server">
        <Columns>
        <asp:BoundField
            DataField="Title"
            HeaderText="Movie Title" />
        <asp:CheckBoxField
            DataField="InTheaters"
            HeaderText="In Theaters" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,InTheaters FROM Movies"
        UpdateCommand="UPDATE Movies SET
            Title=@Title, InTheaters=@InTheaters
            WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>

The CheckBoxField inherits from the BoundField class, so it includes all the properties of the BoundField class. It also supports the following property:

  • TextDisplays text next to each check box.

Using CommandFields

You can use a CommandField to customize the appearance of the Edit, Delete, Update, Cancel, and Select buttons displayed by the GridView control. For example, the page in Listing 11.20 uses icons for the standard edit buttons (see Figure 11.15).

Example 11.20. ShowCommandField.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show CommandField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <asp:CommandField
            ButtonType="Image"
            ShowEditButton="true"
            EditText="Edit Movie"
            EditImageUrl="Edit.gif"
            UpdateText="Update Movie"
            UpdateImageUrl="Update.gif"
            ShowCancelButton="true"
            CancelText="Cancel Edit"
            CancelImageUrl="Cancel.gif"
            ShowDeleteButton="true"
            DeleteText="Delete Movie"
            DeleteImageUrl="Delete.gif" />
        <asp:BoundField
            DataField="Title"
            HeaderText="Movie Title" />
        <asp:BoundField
            DataField="Director"
            HeaderText="Movie Director" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Title,Director FROM Movies"
        UpdateCommand="UPDATE Movies SET
            Title=@Title, Director=@Director
            WHERE Id=@Id"
        DeleteCommand="DELETE Movies
            WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>
Using a CommandField with the GridView control.

Figure 11.15. Using a CommandField with the GridView control.

Notice that you do not enable the AutoGenerateEditButton or AutoGenerateDeleteButton properties when using a CommandField. Instead, you use the CommandField to set up the standard editing buttons explicitly.

The CommandField supports the following properties:

  • ButtonTypeEnables you to specify the type of button displayed by the CommandField. Possible values are Button, Image, and Link.

  • CancelImageUrlEnables you to specify an image to display for the Cancel button.

  • CancelTextEnables you to specify the text to display for the Cancel button.

  • CausesValidationEnables you to disable validation when an edit button is clicked.

  • DeleteImageUrlEnables you to specify an image to display for the Delete button.

  • DeleteTextEnables you to specify the text to display for the Delete button.

  • EditImageUrlEnables you to specify an image to display for the Edit button.

  • EditTextEnables you to specify the text to display for the Edit button.

  • InsertImageUrlEnables you to specify an image to display for the Insert button.

  • InsertTextEnables you to specify the text to display for the Insert button.

  • NewImageUrlEnables you to specify an image to display for the New button (does not apply to GridView).

  • NewTextEnables you to specify the text to display for the New button.

  • SelectImageUrlEnables you to specify the image to display for the Select button.

  • SelectTextEnables you to specify the text to display for the Select button.

  • ShowCancelButtonEnables you to display the Cancel button.

  • ShowDeleteButtonEnables you to display the Delete button.

  • ShowEditButtonEnables you to display the Edit button.

  • ShowInsertButtonEnables you to display the Insert button (does not apply to GridView).

  • ShowSelectButtonEnables you to display the Select button.

  • UpdateImageUrlEnables you to specify the image to display for the Update button.

  • UpdateTextEnables you to specify the text to display for the Update button.

  • ValidationGroupEnables you to associate the edit buttons with a validation group.

Using Button Fields

You use a ButtonField to display a button in a GridView. You can use a ButtonField to represent a custom command or one of the standard edit commands.

For example, the GridView in Listing 11.21 contains two ButtonFields that a user can click to change the display order of the movie category records (see Figure 11.16).

Example 11.21. ShowButtonField.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Sub grdMovieCategories_RowCommand(ByVal sender As Object, ByVal e As GridViewCommandEventArgs)
        Dim index As Integer = Int32.Parse(CType(e.CommandArgument, String))
        Dim id As Integer = CType(grdMovieCategories.DataKeys(index).Values("Id"), Integer)
        Dim position As Integer = CType(grdMovieCategories.DataKeys(index).Values("Position"), Integer)
        Select Case e.CommandName
            Case "Up"
                position = position − 1
            Case "Down"
                position = position + 1
        End Select
        srcMovieCategories.UpdateParameters("Id").DefaultValue = id.ToString()
        srcMovieCategories.UpdateParameters("Position").DefaultValue = position.ToString()
        srcMovieCategories.Update()
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show ButtonField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovieCategories"
        DataSourceID="srcMovieCategories"
        DataKeyNames="Id,Position"
        AutoGenerateColumns="false"
        OnRowCommand="grdMovieCategories_RowCommand"
        Runat="server">
        <Columns>
        <asp:ButtonField
            Text="Move Up"
            CommandName="Up" />
        <asp:ButtonField
            Text="Move Down"
            CommandName="Down" />
        <asp:BoundField
            DataField="Position"
            HeaderText="Position" />
        <asp:BoundField
            DataField="Name"
            HeaderText="Category Name" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovieCategories"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id, Name, Position FROM MovieCategories
            ORDER BY Position"
        UpdateCommand="UPDATE MovieCategories SET
            Position=@Position WHERE Id=@Id"
        Runat="server">
        <UpdateParameters>
        <asp:Parameter
            Name="Id" />
        <asp:Parameter
            Name="Position" />
        </UpdateParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>
Using ButtonFields with the GridView control.

Figure 11.16. Using ButtonFields with the GridView control.

When you click either the Move Up or Move Down buttons in the page in Listing 11.21, the GridView control’s RowCommand event is raised. This event is handled by the grdMovieCategories_RowCommand() method.

The grdMovieCategories_RowCommand() retrieves the index of the row containing the button that was clicked. The row index is grabbed from the GridViewCommandEventArgs’s CommandArgument property passed as the second parameter to the event handler.

The grdMovieCategories_RowCommand() method updates the position of a record by setting the SqlDataSource control’s Update parameters and calling the SqlDataSource control’s Update() method.

A ButtonField supports the following properties:

  • ButtonTypeEnables you to specify the type of button displayed by the CommandField. Possible values are Button, Image, and Link.

  • CausesValidationEnables you to disable validation when the button is clicked.

  • CommandNameEnables you to associate a standard edit command with the ButtonField. Possible values include Delete, Edit, Update, and Cancel.

  • DataTextFieldEnables you to use a data column to specify the button text.

  • DataTextFormatStringEnables you to format the button text.

  • TextEnables you to specify the button text.

  • ValidationGroupEnables you to associate the button with a validation group.

Notice that you can use CommandName to associate a ButtonField with one of the standard edit commands. For example, you can create a Delete button by assigning the value Delete to the CommandName property.

Using HyperLinkField

You use a HyperLinkField to create a link to another page. A HyperLinkField is particularly useful when you need to build two page Master/Detail forms.

For example, the page in Listing 11.22 displays a list of movie categories, and the page in Listing 11.23 displays a list of movies that match the selected category.

Example 11.22. Master.aspx

<%@ Page Language="VB" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Master</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovieCategories"
        DataSourceID="srcMovieCategories"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <asp:HyperLinkField
            HeaderText="Movie Categories"
            DataTextField="Name"
            DataNavigateUrlFields="Id"
            DataNavigateUrlFormatString="Details.aspx?id={0}" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovieCategories"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id, Name FROM MovieCategories"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Example 11.23. Details.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Details</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Title,Director FROM Movies
            WHERE CategoryId=@CategoryId"
        Runat="server">
        <SelectParameters>
        <asp:QueryStringParameter
            Name="CategoryId"
            QueryStringField="id" />
        </SelectParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>

The page in Listing 11.22 includes a GridView control that contains a HyperLinkField. The HyperLinkField creates a link to the Details.aspx page and passes the movie category ID as a query string parameter.

The HyperLinkField looks like this:

<asp:HyperLinkField
    HeaderText="Movie Categories"
    DataTextField="Name"
    DataNavigateUrlFields="Id"
    DataNavigateUrlFormatString="Details.aspx?id={0}" />

The DataNavigateUrlFields property represents the fields used with the DataNavigateFormatString. The DataNavigateFormatString plugs the value of the ID column from the DataNavigateUrlFields into the {0} placeholder.

Note

The DataNavigateUrlFields property accepts a comma-separated list of column names. You can use multiple placeholders in the DataNavigateUrlFormatString.

When you link to the page in Listing 11.23, the list of matching movies is displayed. Notice that the SqlDataSource control includes a QueryStringParameter that represents the movie category ID query string parameter.

You also can use HyperLinkFields when working with frames. For example, the page in Listing 11.24 employs a GridView to display a list of movies. The page also includes an iframe (inline frame), which displays details for a particular movie. The iframe displays the page contained in Listing 11.25 (see Figure 11.17).

Displaying a single-page Master/Detail form.

Figure 11.17. Displaying a single-page Master/Detail form.

Example 11.24. FrameMaster.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        html
        {
            background-color:silver;
        }
        .content
        {
            width:500px;
            margin:auto;
            background-color:white;
        }
        .column
        {
            padding:10px;
            float:left;
        }
        #FrameDetails
        {
            width:100%;
            height:400px;
        }
    </style>
    <title>Frame Master</title>
</head>
<body>
    <form id="form1" runat="server">
    <div class="content">

    <div class="column">

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <asp:HyperLinkField
            HeaderText="Movies"
            DataTextField="Title"
            DataNavigateUrlFields="Id"
            DataNavigateUrlFormatString="FrameDetails.aspx?id={0}"
            Target="FrameDetails" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT * FROM Movies"
        Runat="server" />

    </div>
    <div class="column">

    <iframe name="FrameDetails" id="FrameDetails"></iframe>

    </div>

    <br style="clear:both" />
    </div>
    </form>
</body>
</html>

Example 11.25. FrameDetails.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Frame Details</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:DetailsView
        id="dtlMovie"
        DataSourceID="srcMovieDetails"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovieDetails"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Title, Director, InTheaters
            FROM Movies WHERE Id=@MovieId"
        Runat="server">
        <SelectParameters>
        <asp:QueryStringParameter
            Name="MovieId"
            QueryStringField="id" />
        </SelectParameters>
    </asp:SqlDataSource>

    </div>
    </form>
</body>
</html>

Notice that the HyperLinkField contained in Listing 11.24 includes a Target property. The Target property contains the name of the iframe. When you click a movie link, the FrameDetails.aspx page opens in the named iframe.

The HyperLinkField supports the following properties:

  • DataNavigateUrlFieldsRepresents the field or fields from the data source to use with the DataNavigateUrlFormatString.

  • DataNavigateUrlFormatStringRepresents a format string that can be used to create the hyperlink.

  • DataTextFieldRepresents a field from the data source to use for the hyperlink label.

  • DataTextFormatStringRepresents a format string that can be used to format the hyperlink label.

  • NavigateUrlRepresents a fixed link to another page.

  • TargetRepresents the target of a link. Possible values include _blank, _parent, _self, and _top. You can also supply the name of a frame or iframe.

  • TextRepresents fixed text to display as the label for the hyperlink.

Using ImageField

You use an ImageField to display an image stored on the server’s hard drive. You can’t use an ImageField to display images stored in a database table.

The page in Listing 11.26 illustrates how you can use the ImageField when creating a simple photo gallery (see Figure 11.18).

Example 11.26. ShowImageField.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub frmPhoto_ItemInserting(ByVal sender As Object, ByVal e As FormViewInsertEventArgs)
        ' Get the FileUpload control
        Dim upPhoto As FileUpload = CType(frmPhoto.FindControl("upPhoto"), FileUpload)
        srcImages.InsertParameters("FileName").DefaultValue = upPhoto.FileName

        ' Save contents to file system
        Dim savePath As String = MapPath("~/Photos/" + upPhoto.FileName)
        upPhoto.SaveAs(savePath)
    End Sub

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show ImageField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdImages"
        DataSourceID="srcImages"
        AutoGenerateColumns="false"
        ShowHeader="false"
        Runat="server">
        <Columns>
        <asp:ImageField
            DataImageUrlField="FileName"
            DataImageUrlFormatString="~/Photos/{0}"
            DataAlternateTextField="AltText"
            ControlStyle-Width="200px" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcImages"
        ConnectionString="<%$ ConnectionStrings:Photos %>"
        SelectCommand="SELECT FileName, AltText FROM Photos"
        InsertCommand="INSERT Photos (FileName, AltText)
            VALUES (@FileName, @AltText)"
        Runat="server">
        <InsertParameters>
            <asp:Parameter Name="FileName" />
        </InsertParameters>
    </asp:SqlDataSource>

    <hr />
    <asp:FormView
        id="frmPhoto"
        DefaultMode="Insert"
        DataSourceID="srcImages"
        OnItemInserting="frmPhoto_ItemInserting"
        Runat="server">
        <InsertItemTemplate>
        <h1>Add Photo</h1>
        <asp:Label
            id="lblPhoto"
            Text="Photo:"
            AssociatedControlID="upPhoto"
            Runat="server" />
        <br />
        <asp:FileUpload
            id="upPhoto"
            Runat="server" />
        <br />
        <asp:Label
            id="lblAltText"
            Text="Alternate Text:"
            AssociatedControlID="txtAltText"
            Runat="server" />
        <br />
        <asp:TextBox
            id="txtAltText"
            Text='<%# Bind("AltText") %>'
            Columns="50"
            Runat="server" />
        <br />
        <asp:Button
            id="btnInsert"
            Text="Add New Photo"
            CommandName="Insert"
            Runat="server" />
        </InsertItemTemplate>
    </asp:FormView>

    </div>
    </form>
</body>
</html>
Using an ImageField with the GridView control.

Figure 11.18. Using an ImageField with the GridView control.

The GridView in Listing 11.26 contains an ImageField that looks like this:

<asp:ImageField
    DataImageUrlField="FileName"
    DataImageUrlFormatString="~/Photos/{0}"
    DataAlternateTextField="AltText"
    ControlStyle-Width="200px" />

The DataImageUrlField property contains the name of a field from the data source that represents the path to an image on the server hard drive. The DataImageUrlFormatString enables you to format this path. Finally, the DataAlternateTextField enables you to specify the value of the alt attribute used by the <img> tag.

Web Standards Note

Always supply an alt attribute for your <img> tags so that blind users of your web application can interpret an image’s meaning. In the case of purely decorative images, create an empty alt attribute (alt="").

An ImageField supports the following properties:

  • AlternateTextEnables you to specify fixed alternate text.

  • DataAlternateTextFieldEnables you to specify a field that represents the alternate text.

  • DataAlternateTextFormatStringEnables you to format the alternate text.

  • DataImageUrlFieldEnables you to specify a field that represents the image path.

  • DataImageUrlFormatStringEnables you to format the image path.

  • NullImageUrlEnables you to specify an alternate image when the DataImageUrlField is Nothing (null).

Using TemplateField

A TemplateField enables you to add any content to a GridView column that you need. A TemplateField can contain HTML, DataBinding expressions, or ASP.NET controls.

TemplateFields are particularly useful when you are using a GridView to edit database records. You can use a TemplateField to customize the user interface and add validation to the fields being edited.

For example, the page in Listing 11.27 contains a GridView that enables you to edit the records contained in the Movies database table. TemplateFields are used to render the user interface for editing the movie title and category columns (see Figure 11.19).

Using TemplateFields with the GridView control.

Figure 11.19. Using TemplateFields with the GridView control.

Example 11.27. ShowTemplateField.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show TemplateField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateColumns="false"
        AutoGenerateEditButton="true"
        Runat="server">
        <Columns>
        <asp:TemplateField HeaderText="Title">
            <ItemTemplate>
            <%# Eval("Title") %>
            </ItemTemplate>
            <EditItemTemplate>
            <asp:TextBox
                id="txtTitle"
                Text='<%# Bind("Title") %>'
                Runat="server" />
            <asp:RequiredFieldValidator
                id="valTitle"
                ControlToValidate="txtTitle"
                Text="(required)"
                Runat="server" />
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Category">
            <ItemTemplate>
            <%# Eval("Name") %>
            </ItemTemplate>
            <EditItemTemplate>
            <asp:DropDownList
                id="ddlCategory"
                DataSourceID="srcMovieCategories"
                DataTextField="Name"
                DataValueField="Id"
                SelectedValue='<%# Bind("CategoryId") %>'
                Runat="server" />
            </EditItemTemplate>
        </asp:TemplateField>
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString='<%$ ConnectionStrings:Movies %>'
        SelectCommand="SELECT Movies.Id, Title, CategoryId, Name
            FROM Movies JOIN MovieCategories
            ON MovieCategories.Id = Movies.CategoryId"
        UpdateCommand="UPDATE Movies SET Title=@Title, CategoryId=@CategoryId
            WHERE Id=@Id"
        Runat="server" />

    <asp:SqlDataSource
        id="srcMovieCategories"
        ConnectionString='<%$ ConnectionStrings:Movies %>'
        SelectCommand="SELECT Id, Name FROM MovieCategories"
        Runat="server" />

    </div>
    </form>
</body>
</html>

The GridView in Listing 11.27 contains two TemplateFields. The first TemplateField enables you to display and edit the value of the Title column. The contents of the ItemTemplate are displayed when a row is not selected for editing. The contents of the EditItemTemplate are displayed when the row is selected for editing.

The EditItemTemplate for the Title column includes a RequiredFieldValidator control. This RequiredFieldValidator control prevents a user from updating a record without entering a value for the Title column.

The second TemplateField displays the value of the movie category column. The EditItemTemplate contains a DropDownList control, which enables you to change the movie category associated with the record being edited.

A TemplateField supports the following six types of templates:

  • AlternatingItemTemplateThe contents of this template are displayed for every other row rendered by the GridView.

  • EditItemTemplateThe contents of this template are displayed when a row is selected for editing.

  • FooterTemplateThe contents of this template are displayed in the column footer.

  • HeaderTemplateThe contents of this template are displayed in the column header.

  • InsertItemTemplateThe contents of this template are displayed when a new data item is inserted (does not apply to the GridView control).

  • ItemTemplateThe contents of this template are displayed for every row rendered by the GridView.

Working with GridView Control Events

The GridView control includes a rich set of events that you can handle to customize the control’s behavior and appearance. These events can be divided into three groups.

First, the GridView control supports the following set of events that are raised when the control displays its rows:

  • DataBindingRaised immediately before the GridView is bound to its data source.

  • DataBoundRaised immediately after a GridView is bound to its data source.

  • RowCreatedRaised when each row in the GridView is created.

  • RowDataBoundRaised when each row in the GridView is bound to data.

Second, the GridView control includes the following set of events that are raised when you are editing records:

  • RowCommandRaised when an event is raised by a control contained in the GridView.

  • RowUpdatingRaised immediately before a GridView updates a record.

  • RowUpdatedRaised immediately after a GridView updates a record.

  • RowDeletingRaised immediately before a GridView deletes a record.

  • RowDeletedRaised immediately after a GridView deletes a record.

  • RowCancelingEditRaised when you cancel updating a record.

Finally, the GridView control supports the following events related to sorting, selecting, and paging:

  • PageIndexChangingRaised immediately before the current page is changed.

  • PageIndexChangedRaised immediately after the current page is changed.

  • SortingRaised immediately before sorting.

  • SortedRaised immediately after sorting.

  • SelectedIndexChangingRaised immediately before a row is selected.

  • SelectedIndexChangedRaised immediately after a row is selected.

In this section, you learn how to handle the RowDataBound event (my favorite event included with the GridView control) to create GridView special effects. You learn how to handle the RowDataBound event to highlight particular rows, show column summaries, and create nested Master/Detail forms.

Highlighting GridView Rows

Imagine that you want to highlight particular rows in a GridView. For example, when displaying a table of sales totals, you might want to highlight the rows in which the sales are greater than a certain amount.

You can modify the appearance of individual rows in a GridView control by handling the RowDataBound event. For example, the page in Listing 11.28 displays every movie that has a box office total greater than $300,000.00 with a yellow background color (see Figure 11.20).

Highlighting rows in the GridView control.

Figure 11.20. Highlighting rows in the GridView control.

Example 11.28. HighlightRows.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub grdMovies_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim boxOfficeTotals As Decimal = CType(DataBinder.Eval(e.Row.DataItem, "BoxOfficeTotals"), Decimal)
            If boxOfficeTotals > 300000000 Then
                e.Row.BackColor = System.Drawing.Color.Yellow
            End If
        End If
    End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Highlight Rows</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        OnRowDataBound="grdMovies_RowDataBound"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <asp:BoundField
            DataField="Title"
            HeaderText="Title" />
        <asp:BoundField
            DataField="BoxOfficeTotals"
            DataFormatString="{0:c}"
            HtmlEncode="false"
            HeaderText="Box Office Totals" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT * FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

In Listing 11.28, the grdMovies_RowDataBound() method is executed when the GridView renders each of its rows (including its header and footer). The second parameter passed to this event handler is an instance of the GridViewRowEventArgs class. This class exposes a GridViewRow object that represents the row being bound.

The GridViewRow object supports several useful properties (this is not a complete list):

  • CellsRepresents the collection of table row cells associated with the row being bound.

  • DataItemRepresents the data item associated with the row being bound.

  • DataItemIndexRepresents the index of the data item in its DataSet associated with the row being bound.

  • RowIndexRepresents the index of the row being bound.

  • RowStateRepresents the state of the row being bound. Possible values are Alternate, Normal, Selected, Edit. Because these values can be combined (for example, the RowState can be Alternate Edit), use a bitwise comparison with RowState.

  • RowTypeRepresents the type of row being bound. Possible values are DataRow, Footer, Header, NullRow, Pager, Separator.

In Listing 11.28, the RowType property is used to verify that the row is a DataRow (not a header row or some other type of row). The DataItem property is used to retrieve the database record associated with the row. Notice that the DataBinder.Eval() method is used to retrieve the value of the BoxOfficeColumn.

Displaying Column Summaries

Imagine that you want to display a column total at the bottom of a column. In that case, you can handle the GridView RowDataBound event to sum the values in a column and display the summary in the column footer.

For example, the page in Listing 11.29 contains a GridView control that displays a summary column representing the total box office sales of all movies (see Figure 11.21).

Displaying a column summary.

Figure 11.21. Displaying a column summary.

Example 11.29. SummaryColumn.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Private _boxOfficeTotalsTotal As Decimal = 0

    Protected Sub grdMovies_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim boxOfficeTotals As Decimal = CType(DataBinder.Eval(e.Row.DataItem, "BoxOfficeTotals"), Decimal)
            _boxOfficeTotalsTotal += boxOfficeTotals
        End If
        If e.Row.RowType = DataControlRowType.Footer Then
            Dim lblSummary As Label = CType(e.Row.FindControl("lblSummary"), Label)
            lblSummary.Text = String.Format("Total: {0:c}", _boxOfficeTotalsTotal)
        End If
    End Sub

</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Summary Column</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovies"
        OnRowDataBound="grdMovies_RowDataBound"
        AutoGenerateColumns="false"
        ShowFooter="true"
        Runat="server">
        <Columns>
        <asp:BoundField
            DataField="Title"
            HeaderText="Title" />
        <asp:TemplateField HeaderText="Box Office Totals">
        <ItemTemplate>
            <%# Eval("BoxOfficeTotals", "{0:c}") %>
        </ItemTemplate>
        <FooterTemplate>
            <asp:Label
                id="lblSummary"
                Runat="server" />
        </FooterTemplate>
        </asp:TemplateField>
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT * FROM Movies"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Notice that the GridView control uses a TemplateField to represent the BoxOfficeTotals column. The TemplateField includes a <FooterTemplate> that contains a Label control. The grdMovies_RowDataBound() method displays the total of the box office totals in this Label control.

Displaying Nested Master/Details Forms

You also can handle the RowDataBound event to create nested Master/Details forms. The page in Listing 11.30 displays a list of movie categories and displays a list of matching movies under each category (see Figure 11.22).

Displaying a nested Master/Detail form.

Figure 11.22. Displaying a nested Master/Detail form.

Example 11.30. NestedMasterDetail.aspx

<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<script runat="server">

    Protected Sub grdMovieCategories_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim categoryId As Integer = CType(DataBinder.Eval(e.Row.DataItem,"Id"), Integer)
            Dim srcMovies As SqlDataSource = CType(e.Row.FindControl("srcMovies"), SqlDataSource)
            srcMovies.SelectParameters("CategoryId").DefaultValue = categoryId.ToString()
        End If
    End Sub

</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        .categories h1
        {
            font:bold 16px Arial, Sans-Serif;
        }
        .movies
        {
            margin-left:20px;
            margin-bottom:10px;
            width:100%;
        }
    </style>
    <title>Nested Master/Detail</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataSourceID="srcMovieCategories"
        OnRowDataBound="grdMovieCategories_RowDataBound"
        AutoGenerateColumns="false"
        CssClass="categories"
        ShowHeader="false"
        GridLines="none"
        Runat="server">
        <Columns>
        <asp:TemplateField>
        <ItemTemplate>
            <h1><%# Eval("Name") %></h1>
            <asp:GridView
                id="grdMovies"
                DataSourceId="srcMovies"
                CssClass="movies"
                GridLines="none"
                Runat="server" />

            <asp:SqlDataSource
                id="srcMovies"
                ConnectionString="<%$ ConnectionStrings:Movies %>"
                SelectCommand="SELECT Title,Director FROM Movies
                    WHERE CategoryId=@CategoryId"
                Runat="server">
                <SelectParameters>
                    <asp:Parameter Name="CategoryId" />
                </SelectParameters>
            </asp:SqlDataSource>
        </ItemTemplate>
        </asp:TemplateField>
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovieCategories"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id,Name FROM MovieCategories"
        Runat="server" />

    </div>
    </form>
</body>
</html>

The grdMovieCategories_RowDataBound() method handles the RowDataBound event. This event handler grabs the movie category ID from the current row’s DataItem property. Next, it retrieves the SqlDataSource control contained in the grdMovieCategories TemplateField. Finally, it assigns the movie category ID to a parameter contained in the SqlDataSource control’s SelectParameters collection.

Note

Notice that you must use the FindControl() method to get the SqlDataSource control from the TemplateField. The templates in a TemplateField each create their own naming containers to prevent naming collisions. The FindControl() method enables you to search a naming container for a control with a matching ID.

Extending the GridView Control

Like any other control in the ASP.NET framework, if you don’t like any aspect of the GridView control, you always have the option of extending the control. In this section, you learn how to extend the GridView control with custom fields.

To create a custom field, you can inherit a new class from any of the existing fields or any of the following base classes:

  • DataControlFieldThe base class for all fields.

  • ButtonFieldBaseThe base class for all button fields, such as the ButtonField and CommandField.

In this section, you learn how to create a long text field, a delete button field, and a validated field.

Creating a Long Text Field

None of the existing GridView fields do a good job of handling large amounts of text. You can fix this problem by creating a custom field, named the LongTextField, which you can use to display the value of text columns regardless of the length of the text.

In normal display mode, the LongTextField displays the text in a scrolling <div> tag. In edit display mode, the text appears in a multi-line TextBox control (see Figure 11.23).

Displaying a long text field.

Figure 11.23. Displaying a long text field.

To create a custom field, we’ll inherit a new class from the base BoundField control. The custom LongTextField is contained in Listing 11.31.

Example 11.31. LongTextField.vb

Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.HtmlControls

Namespace myControls
    ''' <summary>
    ''' Enables you to display a long text field
    ''' </summary>
    Public Class LongTextField
        Inherits BoundField

        Private _width As Unit = New Unit("250px")
        Private _height As Unit = New Unit("60px")

        ''' <summary>
        ''' The width of the field
        ''' </summary>
        Public Property Width() As Unit
            Get
                Return _width
            End Get
            Set(ByVal Value As Unit)
                _width = Value
            End Set
        End Property

        ''' <summary>
        ''' The height of the field
        ''' </summary>
        Public Property Height() As Unit
            Get
                Return _height
            End Get
            Set(ByVal Value As Unit)
                _height = Value
            End Set
        End Property

        ''' <summary>
        ''' Builds the contents of the field
        ''' </summary>
        Protected Overrides Sub InitializeDataCell(ByVal cell As DataControlFieldCell, ByVal rowState As DataControlRowState)
            ' If not editing, show in scrolling div
            If (rowState And DataControlRowState.Edit) = 0 Then
                Dim div As HtmlGenericControl = New HtmlGenericControl("div")
                div.Attributes("class") = "longTextField"
                div.Style(HtmlTextWriterStyle.Width) = _width.ToString()
                div.Style(HtmlTextWriterStyle.Height) = _height.ToString()
                div.Style(HtmlTextWriterStyle.Overflow) = "auto"

                AddHandler div.DataBinding, AddressOf div_DataBinding

                cell.Controls.Add(div)
            Else
                Dim txtEdit As TextBox = New TextBox()
                txtEdit.TextMode = TextBoxMode.MultiLine
                txtEdit.Width = _width
                txtEdit.Height = _height

                AddHandler txtEdit.DataBinding, AddressOf txtEdit_DataBinding

                cell.Controls.Add(txtEdit)
            End If
        End Sub

        ''' <summary>
        ''' Called when databound in display mode
        ''' </summary>
        Private Sub div_DataBinding(ByVal s As Object, ByVal e As EventArgs)
            Dim div As HtmlGenericControl = CType(s, HtmlGenericControl)

            ' Get the field value
            Dim value As Object = Me.GetValue(div.NamingContainer)

            ' Assign the formatted value
            div.InnerText = Me.FormatDataValue(value, Me.HtmlEncode)
        End Sub

        ''' <summary>
        ''' Called when databound in edit mode
        ''' </summary>
        Private Sub txtEdit_DataBinding(ByVal s As Object, ByVal e As EventArgs)
            Dim txtEdit As TextBox = CType(s, TextBox)

            ' Get the field value
            Dim value As Object = Me.GetValue(txtEdit.NamingContainer)

            ' Assign the formatted value
            txtEdit.Text = Me.FormatDataValue(value, Me.HtmlEncode)
        End Sub

    End Class
 End Namespace

In Listing 11.31, the InitializeDataCell() method is overridden. This method is responsible for creating all the controls that the custom field contains.

First, a check is made to determine whether the field is being rendered when the row is selected for editing. Notice that a bitwise comparison must be performed with the rowState parameter because the rowState parameter can contain combinations of the values Alternate, Normal, Selected, and Edit (for example, the RowState can be both Alternate and Edit).

When the row is not in edit mode, a <div> tag is created to contain the text. An HtmlGenericControl represents the <div> tag. When the GridView is bound to its data source, the <div> tags get the value of its innerText property from the div_DataBinding() method.

When the row is selected for editing, a multi-line TextBox control is created. When the GridView is bound to its data source, the TextBox control’s Text property gets its value from the txtEdit_DataBinding() method.

You can experiment with the LongTextField with the page in Listing 11.32. This page uses the LongTextField to display the value of the Movie Description column.

Example 11.32. ShowLongTextField.aspx

<%@ Page Language="VB" %>
<%@ Register TagPrefix="custom" Namespace="myControls" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        .grid td, .grid th
        {
            padding:5px;
        }
    </style>
    <title>Show LongTextField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        CssClass="grid"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateColumns="false"
        AutoGenerateEditButton="true"
        Runat="server">
        <Columns>
        <asp:BoundField
            DataField="Title"
            HeaderText="Movie Title" />
        <asp:BoundField
            DataField="Director"
            HeaderText="Movie Director" />
        <custom:LongTextField
            DataField="Description"
            Width="300px"
            Height="60px"
            HeaderText="Movie Description" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id, Title, Director, Description
            FROM Movies"
        UpdateCommand="UPDATE Movies SET
            Title=@Title,Director=@Director,Description=@Description
            WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Creating a Delete Button Field

I don’t like the Delete button rendered by the GridView control’s CommandField. The problem is that it does not provide you with any warning before you delete a record. In this section, we fix this problem by creating a Delete button that displays a client-side confirmation dialog box (see Figure 11.24).

Displaying a confirmation dialog box.

Figure 11.24. Displaying a confirmation dialog box.

The DeleteButtonField inherits from the ButtonField class. The code for the custom field is contained in Listing 11.33.

Example 11.33. DeleteButtonField.vb

Imports System
Imports System.Web.UI.WebControls

Namespace myControls
    ''' <summary>
    ''' Displays a confirmation before deleting a record
    ''' </summary>
    Public Class DeleteButtonField
        Inherits ButtonField

        Private _confirmText As String = "Delete this record?"

        Public Property ConfirmText() As String
            Get
                Return _confirmText
            End Get
            Set(ByVal Value As String)
                _confirmText = value
            End Set
        End Property

        Public Sub New()
            Me.CommandName = "Delete"
            Me.Text = "Delete"
        End Sub

        Public Overrides Sub InitializeCell(ByVal cell As DataControlFieldCell, ByVal cellType As DataControlCellType, ByVal rowState As DataControlRowState, ByVal rowIndex As Integer)
            MyBase.InitializeCell(cell, cellType, rowState, rowIndex)
            If cellType = DataControlCellType.DataCell Then
                Dim button As WebControl = CType(cell.Controls(0), WebControl)
                button.Attributes("onclick") = String.Format("return confirm('{0}'),", _confirmText)
            End If
        End Sub

    End Class
End Namespace

Most of the work in Listing 11.33 is handled by the base ButtonField class. The InitializeCell() method is overridden so that the button can be grabbed. The button is added to the cell by the base ButtonField’s InitializeCell() method.

To create the confirmation dialog box, an onclick attribute is added to the button. If the JavaScript confirm statement returns false, then the button click is canceled.

You can test the DeleteButtonField with the page in Listing 11.34. This page enables you to delete records from the Movies database table.

Example 11.34. ShowDeleteButtonField.vb

<%@ Page Language="VB" %>
<%@ Register TagPrefix="custom" Namespace="myControls" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <style type="text/css">
        .grid td, .grid th
        {
            padding:5px;
        }
    </style>
    <title>Show DeleteButtonField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        CssClass="grid"
        DataSourceID="srcMovies"
        DataKeyNames="Id"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <custom:DeleteButtonField
            ConfirmText="Are you sure that you want to delete this record?" />
        <asp:BoundField
            DataField="Title"
            HeaderText="Movie Title" />
        <asp:BoundField
            DataField="Director"
            HeaderText="Movie Director" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT Id, Title, Director FROM Movies"
        DeleteCommand="DELETE Movies WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Creating a Validated Field

In this final section, we create a ValidatedField custom field. This field automatically validates the data that a user enters into a GridView when editing a record. The ValidatedField uses a RequiredFieldValidator to check whether a user has entered a value, and a CompareValidator to check whether the value is the correct data type (see Figure 11.25).

Using the ValidatedField to edit a record.

Figure 11.25. Using the ValidatedField to edit a record.

The ValidatedField is a composite field. The field contains three child controls—a TextBox, RequiredFieldValidator, CompareValidator—wrapped up in a container control.

The code for the ValidatedField is contained in Listing 11.35.

Example 11.35. ValidatedField.vb

Imports System
Imports System.Collections.Specialized
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace myControls
    ''' <summary>
    ''' Adds RequiredFieldValidator and CompareValidator
    ''' to BoundField
    ''' </summary>
    Public Class ValidatedField
        Inherits BoundField

        Private _validationDataType As ValidationDataType =
ValidationDataType.String

        Public Property ValidationDataType() As ValidationDataType
            Get
                Return _validationDataType
            End Get
            Set(ByVal Value As ValidationDataType)
                _validationDataType = Value
            End Set
        End Property

        ''' <summary>
        ''' Get value from TextBox
        ''' </summary>
        Public Overrides Sub ExtractValuesFromCell(ByVal dictionary As IOrderedDictionary, ByVal cell As DataControlFieldCell, ByVal rowState As DataControlRowState, ByVal includeReadOnly As Boolean)
            Dim edit As EditContainer = CType(cell.Controls(0), EditContainer)
            If dictionary.Contains(DataField) Then
                dictionary(DataField) = edit.Text
            Else
                dictionary.Add(DataField, edit.Text)
            End If
        End Sub

        ''' <summary>
        ''' Called when field is bound to data source
        ''' </summary>
        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
            Dim source As Control = CType(sender, Control)

            ' Get the field value
            Dim value As Object = Me.GetValue(source.NamingContainer)

            ' If the control is a table cell, display the text
            If TypeOf source Is DataControlFieldCell Then
                Dim formattedValue As String = Me.FormatDataValue(value, Me.HtmlEncode)
                If formattedValue = String.Empty Then
                     formattedValue = "&nbsp;"
                End If
                CType(source, DataControlFieldCell).Text = formattedValue
            End If

            ' If the control is an editor, display the editor
            If TypeOf source Is EditContainer Then
                Dim formattedValue As String = String.Empty
                Select Case _validationDataType
                    Case ValidationDataType.Date
                        Dim vdate As DateTime = CType(value, DateTime)
                        formattedValue = vdate.ToShortDateString()
                    Case ValidationDataType.Currency
                        Dim dec As Decimal = CType(value, Decimal)
                        formattedValue = dec.ToString("F")
                    Case Else
                        formattedValue = value.ToString()
                End Select
                CType(source, EditContainer).Text = formattedValue
            End If
        End Sub

        ''' <summary>
        ''' Build the field
        ''' </summary>
        Protected Overrides Sub InitializeDataCell(ByVal cell As DataControlFieldCell, ByVal rowState As DataControlRowState)
            If (rowState And DataControlRowState.Edit) = 0 Then
                AddHandler cell.DataBinding, AddressOf Me.OnDataBindField
            Else
                Dim editor As EditContainer = New EditContainer(DataField, _validationDataType)
                AddHandler editor.DataBinding, AddressOf Me.OnDataBindField
                cell.Controls.Add(editor)
            End If
        End Sub
    End Class

    ''' <summary>
    ''' This control is added to the field
    ''' </summary>
    Public Class EditContainer
        Inherits Control
        Implements INamingContainer

        Private _dataField As String
        Private _validationDataType As ValidationDataType

        Private _txtEdit As TextBox
        Private _valReq As RequiredFieldValidator
        Private _valCompare As CompareValidator

        Public Sub New(ByVal dataField As String, ByVal validationDataType As ValidationDataType)
            _dataField = dataField
            _validationDataType = validationDataType
        End Sub

        ''' <summary>
        ''' Expose the TextBox control's Text property
        ''' </summary>
        Public Property Text() As String
            Get
                EnsureChildControls()
                Return _txtEdit.Text
            End Get
            Set(ByVal Value As String)
                EnsureChildControls()
                _txtEdit.Text = Value
            End Set
        End Property

        ''' <summary>
        ''' Add TextBox, RequiredFieldValidator, and
        ''' CompareValidator
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            ' Add the textbox
            _txtEdit = New TextBox()
            _txtEdit.ID = "txtEdit"
            Controls.Add(_txtEdit)

            ' Add a RequiredFieldValidator control
            _valReq = New RequiredFieldValidator()
            _valReq.Display = ValidatorDisplay.Dynamic
            _valReq.Text = "(required)"
            _valReq.ControlToValidate = _txtEdit.ID
            Controls.Add(_valReq)

            ' Add a CompareValidator control
            _valCompare = New CompareValidator()
            _valCompare.Display = ValidatorDisplay.Dynamic
            _valCompare.Operator = ValidationCompareOperator.DataTypeCheck
            _valCompare.Type = _validationDataType
            _valCompare.Text = "(invalid)"
            _valCompare.ControlToValidate = _txtEdit.ID
            Controls.Add(_valCompare)
        End Sub
    End Class
 End Namespace

The file in Listing 11.35 contains two classes. It contains the ValidatedField class and the EditContainer class.

The ValidatedField class derives from the BoundField class and overrides the InitializeDataCell() method. When a row is not selected for editing, the field simply displays the value of the data item associated with it. When a row is selected for editing, the field creates a new EditContainer control.

The EditContainer control contains a TextBox, RequiredFieldValidator, and CompareValidator. Notice that the EditContainer implements the INamingContainer interface. Implementing this interface prevents naming collisions when more than one instance of the ValidatedField is used in a GridView row.

The ValidatedField is used in the page in Listing 11.36. This page contains a GridView control that you can use to edit the Movies database table. The GridView control includes three ValidatedFields: one for the Title, DateReleased, and BoxOfficeTotals columns.

If you edit a column, and attempt to submit the column without entering a value, then a validation error is displayed. Furthermore, if you attempt to enter a value that is not a date for the DateReleased column or a value that is not a currency amount for the BoxOfficeTotals column, then a validation error is displayed.

Example 11.36. ShowValidatedField.aspx

<%@ Page Language="VB" %>
<%@ Register TagPrefix="custom" Namespace="myControls" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Show ValidatedField</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    <asp:GridView
        id="grdMovies"
        DataKeyNames="Id"
        DataSourceID="srcMovies"
        AutoGenerateEditButton="true"
        AutoGenerateColumns="false"
        Runat="server">
        <Columns>
        <custom:ValidatedField
            DataField="Title"
            HeaderText="Movie Title" />
        <custom:ValidatedField
            DataField="DateReleased"
            DataFormatString="{0:D}"
            HtmlEncode="false"
            ValidationDataType="Date"
            HeaderText="Date Released" />
        <custom:ValidatedField
            DataField="BoxOfficeTotals"
            DataFormatString="{0:c}"
            HtmlEncode="false"
            ValidationDataType="Currency"
            HeaderText="Box Office Totals" />
        </Columns>
    </asp:GridView>

    <asp:SqlDataSource
        id="srcMovies"
        ConnectionString="<%$ ConnectionStrings:Movies %>"
        SelectCommand="SELECT * FROM Movies"
        UpdateCommand="UPDATE Movies SET Title=@Title,
            DateReleased=@DateReleased, BoxOfficeTotals=@BoxOfficeTotals
            WHERE Id=@Id"
        Runat="server" />

    </div>
    </form>
</body>
</html>

Summary

In this chapter, you learned how to use the GridView control to display, select, sort, page, and edit database records. You also learn how to customize the appearance of the columns rendered by a column by using different types of fields. In particular, you learned how to use BoundFields, CheckboxFields, CommandFields, ImageFields, TemplateFields, ButtonFields, and HyperLinkFields.

Next, you learned how to handle the RowDataBound event to create GridView special effects. For example, you learned how to add column summaries to a GridView.

Finally, you learned how to extend the GridView control with custom fields. We created custom fields, which enable you to display large text fields, display a confirmation dialog box before a record is deleted, and display validation error messages when editing a record.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset