Published: 11 Feb 2008
By: Mike Pope

How to persist selection correctly in a grid control (DataGrid, GridView, and ListView controls) when paging is enabled.

Introduction

I only recently ran across this, and it surprised me: when you use a grid control (DataGrid, GridView, and now even ListView) that supports both selection and paging, selection is persisted incorrectly as you navigate between grid pages.

Let's say that you select the second row. Use the pager to go to page two. You'll see that the second row on that page is selected, too. Third page, same thing. Turns out that row selection in a grid control is relative to the page - the default selection mechanism just selects "the second row" no matter what page you're on. I investigated a bit - surely this can't be what was intended? - but as far as I can determine, this behavior is by design.

I was surprised to discover this behavior at all, and I was surprised that I'd never ran across it at all, and surprised that a few seconds of Web search didn't turn up a solution. So I set about fixing it for my application. It initially turned out to be more complicated than I thought. I haven't run across an easier way yet, although I'm still suspicious that there might be.

For the fix, I handled two events: the selection event that's raised when you click the Select button in a grid row and the paging event that's raised in response to a paging gesture. In the early days (namely with the DataGrid control), you had to handle these events in order to implement the selection or paging behavior. For the GridView and ListView controls, selection and paging are handled automatically by the control, so you don't normally have to handle the events, except if - as here - you want to run some custom logic.

Handling the Selection and Paged Events

During the selection event, I grabbed the ID of the data record that was being selected. This means you have to be working with records whose primary-key IDs you have. I figure that if you're selecting records for some purpose, it's quite likely that you're already doing that. I store the ID in a persistent page property (i.e., stored in the page's view state). The code for that property is in Listing 1. The handler for the selection method is in Listing 2.

Listing 1: Persistent property to store the currently selected ID (DataGrid)

Listing 2: Handler for Selected event (DataGrid)

In the paged event handler, I first reset the selected index to -1 to indicate that there is no selection. I get the items (rows) for the current page and walk through them, comparing the ID of each item to the ID that I stored earlier for the selected record. If they match, I select that record. If there's no match, nothing on the page is selected, which is the behavior I actually want. The code for the paged event handler is in Listing 3.

Listing 3: Handler for Paged event (DataGrid)

Some Gotchas

There are a couple of gotchas. The issue that gave me the most problem was getting the list of items for the current page. I initially would just got the Items (Rows) collection, but as it turns out, at the beginning of the paging event, the Items collection is loaded with the previous page's records. The fix here was in the paged event handler to first call the grid control's DataBind method, which loads the Items collection with the correct list of records.

Another small gotcha for the DataGrid control was that if it happens that the selected record is on the current page, I have to call DataBind again in order to set the selected record. Calling DataBind more than once is suboptimal, but that's what seems to be required.

Adding De-selection

Another perhaps surprising "By Design" behavior is that clicking the Select button for a selected row doesn't unselect it. So while I was already customizing selection behavior, it seemed comparatively simple to add logic that would allow users to unselect a row by re-clicking the Select button.

In order to do that for the DataGrid control I stored the index of the selected item in another persistent property named SelectedIndex. This is also stored in view state; the code is virtually identical to the SelectedID property except for the name of the property and of the view state dictionary item. Then in the selection event handler, I compared the stored index and stored ID against the current index and current selection. If they matched, the user had clicked the Select button again, so I unselected the row. Listing 4 shows a version of the selection handler from Listing 2, but with the added code that enables de-selection.

Listing 4: Updated Selected handler with de-selection logic (DataGrid)

Differences Between DataGrid, GridView, and ListView Controls

The code to perform all these tasks is similar for the DataGrid, GridView, and ListView controls. In fact, it's slightly easier in the GridView and ListView controls, because their richer object model exposes more information that's useful for this task.

A trivial difference is that in the DataGrid control, rows are in the Items collection, and the current page index is in the CurrentPageIndex property. In GridView and ListView, these are, respectively, the Rows collection and PageIndex property.

More interestingly, whereas the DataGrid control has only -ed events (SelectedIndexChanged, PageIndexChanged), newer controls support -ing events (SelectedIndexChanging, PageIndexChanging). For paging, I used the PageIndexChanged event in all cases, because I have to check for the selection after paging has finished. But in the GridView and ListView controls, the SelectedIndexChanging event gets the GridViewSelectEventArgs and ListViewSelectEventArgs objects (respectively) as parameters; these objects support a NewSelectedIndex property. This property makes it unnecessary to store the SelectedIndex property that I used for the DataGrid control, so I handle the selected event for DataGrid, and the selecting event for GridView and ListView.

Another small difference is that in the DataGrid control I had to explicitly manage paging by setting the current page to the target page index (which is available via a parameter passed to the paged method). As noted earlier, the GridView and ListView controls handle this behavior automatically, so you can leave out the couple of lines required to perform this task.

Finally, for the GridView and ListView controls, I didn't need to call DataBind explicitly after setting the selected index or page index. This isn't an optimization - DataBind is handled internally by the control, so it's not as if it's not invoked - but it does mean one less thing to code.

For completeness, I've included the code for all of the grid controls. The SelectedID property is the same in all cases. Listing 5 shows the selection handler for the GridView control. Note that this is the SelectedIndexChanging event, not the SelectedIndexChanged event.

Listing 5: Handler for Selecting event (GridView)

Listing 6 shows the paged handler for the GridView control.

Listing 6: Handler for Paged event (GridView)

Listing 7 shows the selecting handler for the ListView control.

Listing 7: Handler for Selecting event (ListView)

Listing 8 shows the paged handler for the ListView control.

Listing 8: Handler for Paged event (ListView)

Summary

By default, the selection behavior for grid controls that support paging does not work the way you probably want it to. However, by adding a small amount of code in selection and paging event handlers, you tweak the controls to display (and to not display) selection when they should.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Mike Pope

Mike Pope has been a member of the ASP.NET User Education team since version 1.0 of the product.

View complete profile here.

Other articles in this category


Optimized Paging and Sorting in ASP.NET GridView
In this article I am going to demonstrate what are the performance drawbacks of using conventional w...
Custom GridView with Paging and Filtering
A custom control derived from the GridView that implements filtering, custom Top pager and custom Bo...
A first look at the Dynamic Data Engine—the DynamicGridView Control
Dino Esposito introduces the ASP.NET DynamicGridView Control.
Sorting a Gridview with multiple Columns
The Gridview is the most used control to display data in a tabular format. By default it supports ma...

You might also be interested in the following related blog posts


RadControls for WPF/Silverlight Q3 Beta 2 release is live! read more
Multiple child views with RadGridView for WinForms read more
Silverlight Release History : Q2 2009 (version 2009.2.701) read more
Web.UI ASP.NET Grid: Synchronize Checkbox States with Row Selection read more
WPF Release History : Q1 2009 SP2 (version 2009.1.526) read more
Silverlight Release History : Q1 2009 SP2 (version 2009.1.526) read more
WPF Release History : Q1 2009 SP1 (version 2009.1.413) read more
Convert Web.UI Grid into a data entry spreadsheet read more
WebUI Test Studio Release History : Q1 2009 (version 2009.1.311) read more
Bubble chart read more
Top
 
 
 

Discussion


Subject Author Date
placeholder I know this is old but... Dale Hermsen 1/29/2012 3:23 AM
RE: I know this is old but... Mike Pope 1/29/2012 3:08 PM
placeholder TextBoxes? Panos Palladinos 9/14/2012 9:54 AM
Use EnablePersistedSelection property Paul Noone 9/22/2013 8:24 PM
placeholder Maybe I'm missing something Clay Angelly 2/21/2008 4:45 PM
datasource controls Mike Pope 2/26/2008 1:36 AM
placeholder Thanks Clay Angelly 3/2/2008 9:42 AM

Please login to rate or to leave a comment.