Multi-item Selection

Enhancing the DataGrid Control

CoreCoder

LANGUAGES: VB, JavaScript

ASP.NET VERSIONS: 1.0 | 1.1| 2.0

 

Multi-item Selection

Enhancing the DataGrid Control

 

By Dino Esposito

 

The DataGrid control has some built-in support for item selection, but lacks an important capability - letting users select more than one item at a time. The DataGrid's extreme versatility, however, allows us as developers to make up for this deficiency pretty easily.

 

The single selection is perfect when you want to drill down into the information associated with a given item, but it's inadequate when you need to select a bunch of items and process them in a batch on the server. Want an example? Consider the user interface of the Hotmail.com inbox page (see Figure 1).

 


Figure 1: Hotmail allows users to select multiple messages from the inbox.

 

The Hotmail.com inbox allows users to select multiple messages and apply a given action (e.g. move to junk folder, delete, etc.) to all of them. But how can we implement this in ASP.NET?

 

Design a Multi-selection DataGrid

Start by creating a standard DataGrid control, then add a templated column to provide each row with a checkbox. Next, write all the necessary event handlers to retrieve the checked items when the page posts back.

 

Is that all? It depends on how much of a perfectionist you are. A templated column is sufficient to post references to a handful of items to the server, but some client-side script code would make it more attractive and professional-looking. For example, you can inject some JavaScript code to let users select and deselect all items with a single click, just like Hotmail does. Let's start with the checkbox template column. Figure 2 shows the DataGrid at design time.

 


Figure 2: Add a template column to the DataGrid and make it show a checkbox alongside the data cells.

 

The DataGrid is configured to show two columns of data (Customer and Country) plus an additional column to let users select records. The template column is defined as follows:

 

  

     BackColor="PowderBlue">

  

    

  

  

    

  

 

The element contains two templates in addition to the definition of the header's alignment and background color. The block defines the content of the header cell for the templated column. The section determines the content that individual cells in the column will show. You can use either the or control to render the checkbox on the final page served to the user. I've used the HTML server control (the tag) in the body of the grid for a reason that I'll explain later, and that's related to the client-side script code. So if you aren't willing to add any JavaScript functions, Web and HTML checkbox controls work just as well.

 

Remember that the ID assigned to the tag is not going to be replicated for each item in the data source. The actual ID of each checkbox is a unique string obtained by combining the ID with the ID of all the naming containers of the checkbox. In this case, they are the DataGridItem object (the element) and the DataGrid itself (the

element).

 

Now bind some data from the Northwind Customers table to the DataGrid and run the page. The result should resemble Figure 3.

 


Figure 3: Click on the checkbox and select the entire row.

 

Detect Checked Items

Unless you toggle on the AutoPostBack property on the CheckBox control, clicking on the checkbox has no repercussions on the server. The page won't post back, and checking and unchecking items remain purely client-side operations. For the page to post back, you must click on the Process data button. Once back in its native server environment, all controls in the page are rebuilt, including the DataGrid. Thanks to the information stored in the viewstate, the DataGrid retains all of its checked items. As a result, you can get a reference to all selected items simply by looping on all DataGrid items, extracting the corresponding CheckBox control, and verifying the value of the Checked property (see Figure 4).

 

For Each item As DataGridItem In theGrid.Items

  Dim itemType As ListItemType

 

  itemType = item.ItemType

  If itemType = ListItemType.Item Or _

     itemType = ListItemType.AlternatingItem Then

    Dim cb As HtmlInputCheckBox

    Dim ctl As Control = item.FindControl("YourItem")

 

    cb = CType(ctl, HtmlInputCheckBox)

    If Not (cb Is Nothing) Then

      If cb.Checked Then

        ' TODO:: process item

      End If

    End If

  End If

Next

Figure 4: Get a reference to all selected items simply by looping on all DataGrid items, extracting the corresponding CheckBox control, and verifying the value of the Checked property.

 

The DataGrid's Items collection contains a reference to all items created to render the grid, including header, footer, and pager, as well as items and alternating items. The ItemType property on the DataGridItem object being processed returns the type of the item. You must consider only Items and AlternatingItems. To locate the checkbox, use the FindControl method and pass in the relative ID of the control. By design, FindControl operates in the realm of the current container. This ensures that the code in Figure 4 makes its search only in the context of the current DataGrid item. However, FindControl isn't as fast as a direct, position-based search. Although slightly faster, this code is less flexible, and, for example, doesn't work if you give the template column a fixed width:

 

Dim ctl As Control = item.Cells(0).Controls(0)

 

In this case, in fact, the DataGrid renders the templated cells as follows:

 

  

 

As a result, CheckBox isn't at position 0 as one would expect. The first position in the Controls collection is reserved for a LiteralControl object rendering the white space. The bottom line is that using an index-based approach makes your code more sensitive to changes to the page layout.

 

As you can see in Figure 3, the selected items have a different background color and are drawn in boldface. These graphical changes are applied on the client using JavaScript code.

 

Simply clicking on a checkbox displayed in a browser doesn't necessarily cause the page to post back. This happens only if you implement the checkbox using the element and set the Boolean AutoPostBack property to True. Note that the AutoPostBack property isn't defined on the HtmlInputCheckBox control (the class behind the tag).

 

Identify the Checked Items

So you now know a way to check multiple rows of a DataGrid and send them to the server in a single post. However, a DataGrid item references a particular data object, which is - after all - what you're really interested in. So how can you establish a reliable link between a checkbox and an item in the bound data source? The DataList and the DataGrid controls have a slick property named DataKeyField. This property takes the name of a column in the data source, typically the primary key, or another column with unique values.

 

In ASP.NET 1.x, the DataKeyField property must be a string, and subsequently it can address only a single column. In ASP.NET 2.0, the DataKeyField property is extended to support an array of columns.

 

In the previous example, I used the Customers table in the SQL Server's Northwind database. So the most obvious setting for DataKeyField is:

 

   DataKeyField="customerid">

...

 

The final effect of this code is that the DataGrid stores the value of the specified column for all data items currently displayed in the DataKeys property (an array of objects). In other words, the item index can be used to retrieve the key for each checked item:

 

' cb is the CheckBox retrieved as in the snippet above.

' item is the current DataGridItem object.

If cb.Checked Then

  Dim index As Integer = item.ItemIndex

  Dim key As String = theGrid.DataKeys(index)

  ProcessItem(key)

End If

 

The ProcessItem routine is a placeholder used to indicate any code you may write to process the checked item. Thanks to the DataKeyField and DataKeys properties, ProcessItem can easily be passed a key value that uniquely identifies the underlying data row.

 

Passing in the key is particularly suited to situations where you need to drill down into the data with a further query. If you're using cached data, you can simply use the item index to identify the data object in the in-memory data source. Note, however, that the ItemIndex property is a page-specific index. In case of a multi-page DataGrid, you need an absolute index to retrieve the corresponding data object from memory.

 

Modify the Style of a Checked Item

If you're content with the core item selection feature, you're pretty much done. Your grid is up and running and you might consider wrapping it up in an all-encompassing control. However, adding some client-side bells and whistles isn't that difficult, and will make the whole solution more elegant.

 

Let's see how to change the background color and the font weight of a checked row. These changes will take place entirely on the client by exploiting the DHTML capabilities of the browser. As a result, style changes will be lost when the page posts back and might not work on older browsers.

 

To change the style of the HTML element, you must be able to detect when the user clicks to check or uncheck the input element. This is as easy as defining the onclick attribute for the tag:

 

 

The checkbox is rendered as part of a templated grid column. This means that each checkbox displays within a

tag. To select the entire row you must retrieve the parent tag, and set the background color and any other graphical attribute (see Figure 5).

 

Figure 5: Setting the background color and font weight of selected rows.

 

The first argument is a reference to the checkbox itself. The second argument indicates the background color for the checked item (see Figure 3). You must place two iterative calls to the parentElement property of the checkbox to reach the parent table row. The HTML layout of each checkbox is as follows:

 

 

How can you bind the above script code to each checkbox in the DataGrid? First, register the

Figure 7: This code selects or deselects all items according to the checkbox on the column header.

 

The code in Figure 7 could be simplified if you're targeting IE 5.0 and newer. It also works as-is with Netscape Navigator 4.x. To retrieve all checkboxes in the table, I'm afraid there's no better way than checking all the input elements and filtering out all those that are neither checkboxes nor children of the grid. This latter condition is verified looking at the name attribute. By design, in ASP.NET the ID of the contained control is prefixed with the name of the parent. Once you've found a checkbox element, you set the checked attribute to the value of the header's checkbox checked attribute, and then call the same Select function defined earlier to apply graphical changes. Figure 8 shows this feature.

 


Figure 8: Click on the header's checkbox and select all the items in the grid.

 

Conclusion

Although the DataGrid control lacks certain built-in capabilities, it can be tailored to fit your needs. In this article we saw how to add the capability to let users select more than one item at a time. This is just one way your applications can be made to order.

 

The sample code accompanying this article is available for download.

 

Dino Esposito is a trainer and consultant who specializes in ASP.NET, ADO.NET, and XML. Author of Programming Microsoft ASP.NET from Microsoft Press, Dino is also the cofounder of http://www.VB2TheMax.com. Write to him at mailto:[email protected] or join the blog at http://weblogs.asp.net/despos.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish
tag in a table row - a