Tuesday, July 31, 2007

Checkboxes on a Datagrid with paging and sorting

I had a need to allow users to select records for processing in a datagrid that employed paging and sorting.  I've come up with a neat solution to the problem.
Here is a link to a working version:  http://test.koolsoft.com/dg_checkbox.aspx
My datagrid has some advanced features like reverse sorting and, of course, the runtime created checkboxes.  This sample uses the Northwind Orders table and we are only talking about he checkboxes in this article.
First, I create an empty template column in the datagrid to hold the checkbox.  Next, a checkbox has to be created for each row displayed in the datagrid.  This is done on the grid "ItemCreated" event...
Private Sub DataGrid1_ItemCreated(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles DataGrid1.ItemCreated

  If (e.Item.ItemType = ListItemType.Item) Or (e.Item.ItemType = ListItemType.AlternatingItem) Then 
    AddCheckbox(e) 
  End If
End Sub
Private Sub AddCheckbox(ByVal e As DataGridItemEventArgs) 

  Dim cb As New CheckBox
  Dim cell As TableCell = e.Item.Cells(0)
  cb.EnableViewState = True
  cb.AutoPostBack = True
  cb.ID = e.Item.ItemIndex
  cell.HorizontalAlign = HorizontalAlign.Right
  AddHandler cb.CheckedChanged, AddressOf OnCheckedChangedEvent
  cell.Controls.Add(cb)

End Sub
In the ItemCreated event, only add the checkbox on the actual Items (or AlternatingItem) and not the header or footer.  The AddCheckbox sub defines a new checkbox, gets the first column, sets some properties on the checkbox, adds the event handler to the checkbox and adds the checkbox to the cell.
Next, when the grid is binding data, I determine if the row has already been checked or not and set the checkbox appropriately.
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles DataGrid1.ItemDataBound

  Dim chkvalue As String
  Dim chkText As String
  Dim cb As CheckBox
  Dim litem As ListItem
  If (e.Item.ItemType = ListItemType.Item) Or (e.Item.ItemType = ListItemType.AlternatingItem) Then
    chkText = e.Item.Cells(2).Text.ToString
    chkvalue = e.Item.Cells(1).Text.ToString
    cb = CType(e.Item.Cells(0).Controls(0), CheckBox)
    litem = New ListItem(chkText, chkvalue)
    If ListBox1.Items.Contains(litem) Then
      cb.Checked = True
    Else
      cb.Checked = False
    End If
    litem = Nothing
  End If

End Sub
Again, I only affect changes when this is an item or alternatingitem.   I get the BuyerName (e.item.cells(2)), the Order# (e.item.cells(1)), and a handle to the checkbox created above.  Then create a list item with the row data and check the listbox if it exists.  If so, turn the checkbox "checked" property on.
Now to handle the checkbox "checkedchanged event...
Private Sub OnCheckedChangedEvent(ByVal sender As Object, ByVal e As System.EventArgs) 

  Dim griditem As DataGridItem
  Dim livalue As String
  Dim liText As String
  Dim cb As CheckBox
  Dim litem As ListItem
  For Each griditem In DataGrid1.Items 
    liText = DataGrid1.Items(griditem.ItemIndex).Cells(2).Text.ToString 
    livalue = DataGrid1.Items(griditem.ItemIndex).Cells(1).Text.ToString
    cb = CType(griditem.Cells(0).Controls(0), CheckBox)
    litem = New ListItem(liText, livalue)
    If cb.Checked Then
      If Not ListBox1.Items.Contains(litem) Then
        ListBox1.Items.Add(litem)
      End If
    Else
      If ListBox1.Items.Contains(litem) Then
        ListBox1.Items.Remove(litem)
      End If
    End
    If litem = Nothing
  Next

End Sub
When a checkbox is checked, this sub loops through all the displayed items in the grid and determines the status of the checkbox and makes the appropriate changes in the listbox on the page.  You can also use an array stored in the session object or the viewstate as well.  With a slight change to the OnCheckedChangedEvent, you can limit the number of items selected:
Private Sub OnCheckedChangedEvent(ByVal sender As Object, ByVal e As System.EventArgs) 

  Dim griditem As DataGridItem
  Dim livalue As String
  Dim liText As String
  Dim cb As CheckBox
  Dim litem As ListItem 
  Dim chkCountLimit as Int32 = 5 
  For Each griditem In DataGrid1.Items 
    liText = DataGrid1.Items(griditem.ItemIndex).Cells(2).Text.ToString 
    livalue = DataGrid1.Items(griditem.ItemIndex).Cells(1).Text.ToString
    cb = CType(griditem.Cells(0).Controls(0), CheckBox)
    litem = New ListItem(liText, livalue)
    If cb.Checked Then
      If Not ListBox1.Items.Contains(litem) Then
        If ListBox1.Items.Count < chkCountLimit Then
          ListBox1.Items.Add(litem)
        Else
          cb.Checked = False 
        End If
      End If
    Else
      If ListBox1.Items.Contains(litem) Then
        ListBox1.Items.Remove(litem)
      End If
    End
    If litem = Nothing
  Next

End Sub
If the limit of checkboxes is matched, the checkbox is turned back off.
That's it.  This will maintain the checkbox selection across the paging and sorting of the grid. Other things that can be done would be to hide all the unchecked ckeckboxes when the limit has been met and/or a checkbox to turn on all visible items in the grid.  If there is enough interest, I will write an article on that subject in the future.


Building a website is a piece of cake.
Yahoo! Small Business gives you all the tools to get online.

0 Comments: