1.16. Allowing Selection Anywhere Within a DataGrid Row

Problem

You are implementing a DataGrid that requires selection of a row, but you do not want to have a Select button in every row of your DataGrid. What you really want is to allow the user to click anywhere within a row, like in a classic Windows application.

Solution

To every row in the DataGrid add a hidden Select button along with an onclick event that performs the same action as if the hidden Select button were clicked.

  1. Add a hidden ButtonColumn to the DataGrid.

  2. Set the ButtonType attribute to "LinkButton" so that a hidden hyperlinked Select button is rendered in every row.

  3. In the ItemDataBound event, add an onclick event to the DataGrid row that performs the same action as clicking the hidden Select button.

The approach produces output like that shown in Figure 1-20. Example 1-45 through Example 1-47 show the .aspx and code-behind files for the application that produces this result.

Output of DataGrid allowing selection anywhere

Figure 1-20. Output of DataGrid allowing selection anywhere

Discussion

To allow selection of a row of data simply by clicking on it, you create a DataGrid in the usual fashion but add a hidden ButtonColumn. The ButtonType attribute is set to "LinkButton“, and the CommandName attribute is set to "Select“. This causes the DataGrid to be rendered with a hidden hyperlinked Select button in every row.

<Columns>
  <asp:ButtonColumn ButtonType="LinkButton" 
                                   Visible="False" 
                                   CommandName="Select" />
    ...
</Columns>

In the code-behind, the DataGrid control’s ItemDataBound event handler (dgProblems_ItemDataBound) is used to expand the functionality of the hidden Select button to encompass the entire row. This method is called for every row of the DataGrid, including the header and footer, so the item type must be checked to see if this event applies to a given data row.

When the event applies to a data row, the first thing you must do is get a reference to the hidden Select button in the row. The LINK_BUTTON_COLUMN and LINK_BUTTON_CONTROL constants are used to avoid so-called “magic numbers” (hardcoded numbers that seem to appear out of nowhere in the code) and to make the code more maintainable.

Next, some client-side JavaScript is added to a row’s hidden hyperlinked Select button. Its sole purpose is to handle the onclick event for the row in the DataGrid that has just been data bound and to perform a call to _ _doPostBack. The JavaScript is added to the DataGrid row’s Attributes collection using the Add method, whose parameters are the name of the event we want to add to the control and the name of the function (along with its parameters) that is to be executed when the event occurs.

The Page’s GetPostBackClientHyperlink method is used to get the name of the client-side function created for the hidden Select button in the row being processed. It returns the name of the event method along with the required parameters. For the first row in our DataGrid, for example, the GetPostBackClientHyperlink method returns javascript:_ _doPostBack('dgProblems:_ctl2:_ctl0','').

Effectively, this adds an onclick event to all the table rows, which causes the method _ _doPostBack to be called anytime the user clicks on a data row in the grid. Because this onclick event is identical to the event created for the hidden Select button in the row, the postback is processed as a select event, thereby setting the SelectedIndex of the DataGrid to the clicked row.

Warning

Be aware that the selection of a row using this method requires a round trip to the server to perform the selection.

This example shows the use of Add, Edit, and Delete buttons below the DataGrid, which is typical of a scenario where a row is selected and then a desired action is performed on it. The methods for the Add, Edit, and Delete events were included in this recipe but were left empty to keep the code down to a reasonable size.

Example 1-45. DataGrid allowing selection anywhere (.aspx)

<%@ Page Language="vb" AutoEventWireup="false" 
    Codebehind="CH01DatagridWithSelectionAnywhereVB.aspx.vb" 
    Inherits="ASPNetCookbook.VBExamples.CH01DatagridWithSelectionAnywhereVB" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>DataGrid With Selection Anywhere</title>
    <link rel="stylesheet" href="css/ASPNetCookbook.css">
  </head>
  <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0">
    <form id="frmDatagrid" method="post" runat="server">
      <table width="100%" cellpadding="0" cellspacing="0" border="0">
        <tr>
          <td align="center">
            <img src="images/ASPNETCookbookHeading_blue.gif">
          </td>
        </tr>
        <tr>
          <td class="dividerLine">
            <img src="images/spacer.gif" height="6" border="0"></td>
        </tr>
      </table>
      <table width="90%" align="center" border="0">
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center" class="PageHeading">
            DataGrid With Selection Anywhere (VB)
          </td>
        </tr>
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center">
                <!-- The first column defined in the Columns element is a 
                     hidden link button to provide the ability to make 
                     clicking anywhere in the row the same as clicking 
                     the link button.  See the dgProblems_ItemDataBound 
                     method in the code behind page -->
            <asp:DataGrid 
                                id="dgProblems" 
                                runat="server" 
                                BorderColor="000080" 
                                BorderWidth="2px"
                                AutoGenerateColumns="False"
                                width="100%">

                                <HeaderStyle 
                                  HorizontalAlign="Center" 
                                  ForeColor="#FFFFFF" 
                                  BackColor="#000080" 
                                  Font-Bold=true
                                  CssClass="TableHeader" /> 

                                <ItemStyle
                                  BackColor="#FFFFE0" 
                                  cssClass="TableCellNormal" />

                                <AlternatingItemStyle 
                                  BackColor="#FFFFFF" 
                                  cssClass="TableCellAlternating" />

                                <SelectedItemStyle 
                                  BackColor="#cccccc"
                                  cssClass="TableCellAlternating" />
                                

                                <Columns>
                                  <asp:ButtonColumn ButtonType="LinkButton" 
                                                    Visible="False" 
                                                    CommandName="Select" />
                                  <asp:BoundColumn HeaderText="Title" 
                                                   DataField="Title" 
                                                   ItemStyle-HorizontalAlign="Left" />
                                  <asp:BoundColumn HeaderText="Publish Date" 
                                                   DataField="PublishDate" 
                                                   ItemStyle-HorizontalAlign="Center" 
                                                   DataFormatString="{0:MMM dd, yyyy}"/>
                                  <asp:BoundColumn HeaderText="List Price" 
                                                   DataField="ListPrice" 
                                                   ItemStyle-HorizontalAlign="Center" 
                                                   DataFormatString="{0:C2}"/>
                                </Columns>
                                

                              </asp:DataGrid>
          </td>
        </tr>
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center">
            <table width="100%">
              <tr>
                <td width="18%">&nbsp;</td>
                <td width="15%" align="center">
                  <asp:ImageButton id="btnAdd" runat="server" 
                       ImageUrl="images/buttons/button_add.gif" /></td>
                <td width="10%">&nbsp;</td>
                <td width="15%" align="center">
                  <asp:ImageButton id="btnEdit" runat="server" 
                       ImageUrl="images/buttons/button_edit.gif" /></td>
                <td width="10%">&nbsp;</td>
                <td width="15%" align="center">
                  <asp:ImageButton id="btnDelete" runat="server" 
                       ImageUrl="images/buttons/button_delete.gif" /></td>
                <td width="17%">&nbsp;</td>
              </tr>
            </table>
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

Example 1-46. DataGrid allowing selection anywhere code-behind (.vb)

Option Explicit On 
Option Strict On
'-----------------------------------------------------------------------------
'
'   Module Name: CH01DatagridWithSelectionAnywhereVB.aspx.vb
'
'   Description: This class provides the code behind for
'                CH01DatagridWithSelectionAnywhereVB
'
'*****************************************************************************
Imports Microsoft.VisualBasic
Imports System.Configuration
Imports System.Data
Imports System.Data.OleDb
Imports System.Web.UI.WebControls

Namespace ASPNetCookbook.VBExamples
  Public Class CH01DatagridWithSelectionAnywhereVB
    Inherits System.Web.UI.Page

    'controls on form
    Protected WithEvents btnAdd As System.Web.UI.WebControls.ImageButton
    Protected WithEvents btnEdit As System.Web.UI.WebControls.ImageButton
    Protected WithEvents btnDelete As System.Web.UI.WebControls.ImageButton
    Protected WithEvents dgProblems As System.Web.UI.WebControls.DataGrid

    '*************************************************************************
    '
    '   ROUTINE: Page_Load
    '
    '   DESCRIPTION: This routine provides the event handler for the page load
    '                event.  It is responsible for initializing the controls 
    '                on the page.
    '-------------------------------------------------------------------------
    Private Sub Page_Load(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
            Handles MyBase.Load

      If (Not Page.IsPostBack) Then
        bindData( )
      End If
    End Sub  'Page_Load

    '*************************************************************************
    '
    '   ROUTINE: dgProblems_ItemDataBound
    '
    '   DESCRIPTION: This routine is the event handler that is called for 
    '                each item in the datagrid after a data bind occurs.  It 
    '                is responsible for making each row a link button to 
    '                allow clicking anywhere to select a row.
    '-------------------------------------------------------------------------
    Private Sub dgProblems_ItemDataBound(ByVal sender As Object, _
                                  ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _
                              Handles dgProblems.ItemDataBound

                        'datagrid column containing link button defined on ASPX page
                        Const LINK_BUTTON_COLUMN As Integer = 0
                        'index of link button control in the link button column 
                        Const LINK_BUTTON_CONTROL As Integer = 0

                        Dim button As LinkButton

                        'check the type of item that was databound and only take action if it 
                        'was a row in the datagrid
                        If ((e.Item.ItemType = ListItemType.Pager) Or _
                            (e.Item.ItemType = ListItemType.Header) Or _
                            (e.Item.ItemType = ListItemType.Footer)) Then
                          'do nothing
                        Else
                          'the item that was bound is an "Item", "AlternatingItem", "EditItem", 
                          '"SelectedItem" or "Separator" (in other words a row) so get a 
                          'reference to the link button column defined in the Columns property 
                          'of the datagrid (in the aspx page) and add an event handler for the 
                          'the onclick event for this entire row.  This will make clicking 
                          'anywhere in the row select the row.
                          'NOTE: This is tightly coupled to the definition of the bound columns 
                          '      in the aspx page.
                          button = _
                            CType(e.Item.Cells(LINK_BUTTON_COLUMN).Controls(LINK_BUTTON_CONTROL), _
                                  LinkButton)
                          e.Item.Attributes.Add("onclick", _
                                                Page.GetPostBackClientHyperlink(button, ""))
                        End If
                      End Sub  'dgProblems_ItemDataBound

    '*************************************************************************
    '
    '   ROUTINE: btnAdd_Click
    '
    '   DESCRIPTION: This routine is the event handler that is called when 
    '                the Add button is clicked.
    '-------------------------------------------------------------------------
    Private Sub btnAdd_Click(ByVal sender As Object, _
                             ByVal e As System.Web.UI.ImageClickEventArgs) _
                Handles btnAdd.Click

      'place code here to perform Add operations
    End Sub  'btnAdd_Click

    '*************************************************************************
    '
    '   ROUTINE: btnEdit_Click
    '
    '   DESCRIPTION: This routine is the event handler that is called when 
    '                the Edit button is clicked.
    '-------------------------------------------------------------------------
    Private Sub btnEdit_Click(ByVal sender As Object, _
                              ByVal e As System.Web.UI.ImageClickEventArgs) _
                Handles btnEdit.Click

      'place code here to perform Edit operations
    End Sub  'btnEdit_Click

    '*************************************************************************
    '
    '   ROUTINE: btnDelete_Click
    '
    '   DESCRIPTION: This routine is the event handler that is called when 
    '                the Delete button is clicked.
    '-------------------------------------------------------------------------
    Private Sub btnDelete_Click(ByVal sender As Object, _
                                ByVal e As System.Web.UI.ImageClickEventArgs) _
                Handles btnDelete.Click

      'place code here to perform Delete operations
    End Sub  'btnDelete_Click

    '*************************************************************************
    '
    '   ROUTINE: bindData
    '
    '   DESCRIPTION: This routine queries the database for the data to 
    '                displayed and binds it to the datagrid
    '
    '-------------------------------------------------------------------------
    Private Sub bindData( )
      Dim dbConn As OleDbConnection
      Dim dCmd As OleDbCommand
      Dim dReader As OleDbDataReader
      Dim strConnection As String
      Dim strSQL As String

      Try
        'get the connection string from web.config and open a connection 
        'to the database
        strConnection = _
            ConfigurationSettings.AppSettings("dbConnectionString")
        dbConn = New OleDb.OleDbConnection(strConnection)
        dbConn.Open( )

        'build the query string and get the data from the database
        strSQL = "SELECT Title, PublishDate, ListPrice " & _
                 "FROM Book " & _
                 "ORDER BY Title"

        dCmd = New OleDbCommand(strSQL, dbConn)
        dReader = dCmd.ExecuteReader(CommandBehavior.Default)

        'set the source of the data for the datagrid control and bind it
        dgProblems.DataSource = dReader
        dgProblems.DataBind( )

        'select first item in the datagrid
        dgProblems.SelectedIndex = 0

      Finally
        'cleanup
        If (Not IsNothing(dReader)) Then
          dReader.Close( )
        End If

        If (Not IsNothing(dbConn)) Then
          dbConn.Close( )
        End If
      End Try
    End Sub      'bindData
  End Class  'CH01DatagridWithSelectionAnywhereVB
End Namespace

Example 1-47. DataGrid allowing selection anywhere code-behind (.cs)

//----------------------------------------------------------------------------
//
//   Module Name: CH01DatagridWithSelectionAnywhereCS.aspx.cs
//
//   Description: This class provides the code behind for
//                CH01DatagridWithSelectionAnywhereCS.aspx
//
//****************************************************************************
using System;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ASPNetCookbook.CSExamples
{
  public class CH01DatagridWithSelectionAnywhereCS : System.Web.UI.Page
  {
    // controls on form
    protected System.Web.UI.WebControls.ImageButton btnAdd;
    protected System.Web.UI.WebControls.ImageButton btnEdit;
    protected System.Web.UI.WebControls.ImageButton btnDelete;
    protected System.Web.UI.WebControls.DataGrid dgProblems;

    //************************************************************************
    //
    //   ROUTINE: Page_Load
    //
    //   DESCRIPTION: This routine provides the event handler for the page
    //                load event.  It is responsible for initializing the
    //                controls on the page.
    //------------------------------------------------------------------------
    private void Page_Load(object sender, System.EventArgs e)
    {
      // wire the item data bound and button events
      this.dgProblems.ItemDataBound +=
        new DataGridItemEventHandler(this.dgProblems_ItemDataBound);
      this.btnAdd.Click +=
        new ImageClickEventHandler(this.btnAdd_Click);
      this.btnEdit.Click +=
        new ImageClickEventHandler(this.btnEdit_Click);
      this.btnDelete.Click +=
        new ImageClickEventHandler(this.btnDelete_Click);

      if (!Page.IsPostBack)
      {
        bindData( );
      }
    }  // Page_Load

    //************************************************************************
    //
    //   ROUTINE: dgProblems_ItemDataBound
    //
    //   DESCRIPTION: This routine is the event handler that is called for
    //                each item in the datagrid after a data bind occurs.  It
    //                is responsible for making each row a link button to
    //                allow clicking anywhere to select a row.
    //------------------------------------------------------------------------
    private void dgProblems_ItemDataBound(Object sender,
                        System.Web.UI.WebControls.DataGridItemEventArgs e)
                      {
                        // datagrid column containing link button defined on ASPX page
                        const int LINK_BUTTON_COLUMN = 0;
                        // index of link button control in the link button column
                        const int LINK_BUTTON_CONTROL = 0;
                        LinkButton button = null;

                        // check the type of item that was databound and only take action if it
                        // was a row in the datagrid
                        if ((e.Item.ItemType == ListItemType.Pager) ||
                            (e.Item.ItemType == ListItemType.Header) ||
                            (e.Item.ItemType == ListItemType.Footer))
                        {
                          // do nothing
                        }
                        else
                        {
                          // the item that was bound is an "Item", "AlternatingItem", 
                          // "EditItem", "SelectedItem" or "Separator" (in other words a row)
                          // so get a reference to the link button column defined in the 
                          // Columns property of the datagrid (in the aspx page) and add an 
                          // event handler for the the onclick event for this entire row.  This 
                          // will make clicking anywhere in the row select the row.
                          // NOTE: This is tightly coupled to the definition of the bound 
                          //       columns in the aspx page.
                          button =
                   (LinkButton)(e.Item.Cells[LINK_BUTTON_COLUMN].Controls[LINK_BUTTON_CONTROL]);
                          e.Item.Attributes.Add("onclick",
                            Page.GetPostBackClientHyperlink(button, ""));
                        }
                      }  // dgProblems_ItemDataBound

    //************************************************************************
    //
    //   ROUTINE: btnAdd_Click
    //
    //   DESCRIPTION: This routine is the event handler that is called when
    //                the Add button is clicked.
    //------------------------------------------------------------------------
    private void btnAdd_Click(Object sender,
      System.Web.UI.ImageClickEventArgs e)
    {
      // place code here to perform Add operations
    }  // btnAdd_Click

    //************************************************************************
    //
    //   ROUTINE: btnEdit_Click
    //
    //   DESCRIPTION: This routine is the event handler that is called when
    //                the Edit button is clicked.
    //------------------------------------------------------------------------
    private void btnEdit_Click(Object sender,
      System.Web.UI.ImageClickEventArgs e)
    {
      // place code here to perform Edit operations
    }  // btnEdit_Click

    //************************************************************************
    //
    //   ROUTINE: btnDelete_Click
    //
    //   DESCRIPTION: This routine is the event handler that is called when
    //                the Delete button is clicked.
    //------------------------------------------------------------------------
    private void btnDelete_Click(Object sender,
      System.Web.UI.ImageClickEventArgs e)
    {
      // place code here to perform Delete operations
    }  // btnDelete_Click

    //************************************************************************
    //
    //   ROUTINE: bindData
    //
    //   DESCRIPTION: This routine queries the database for the data to
    //                displayed and binds it to the repeater
    //------------------------------------------------------------------------
    private void bindData( )
    {
      OleDbConnection dbConn = null;
      OleDbCommand dCmd = null;
      OleDbDataReader dReader = null;
      String strConnection = null;
      String strSQL =null;

      try
      {
        // get the connection string from web.config and open a connection 
        // to the database
        strConnection = 
          ConfigurationSettings.AppSettings["dbConnectionString"];
        dbConn = new OleDbConnection(strConnection);
        dbConn.Open( );

        // build the query string and get the data from the database
        strSQL = "SELECT Title, PublishDate, ListPrice " +
                 "FROM Book " +
                 "ORDER BY Title";

        dCmd = new OleDbCommand(strSQL, dbConn);
        dReader = dCmd.ExecuteReader(CommandBehavior.Default);

        // set the source of the data for the datagrid control and bind it
        dgProblems.DataSource = dReader;
        dgProblems.DataBind( );

        // select first item in the datagrid
        dgProblems.SelectedIndex = 0;
      }  // try

      finally
      {
        // clean up
        if (dReader != null)
        {
          dReader.Close( );
        }

        if (dbConn != null)
        {
          dbConn.Close( );
        }
      }  // finally
    }  // bindData
  }  // CH01DatagridWithSelectionAnywhereCS
}

Get ASP.NET Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.