Mantaining checkboxes State with Gridview Paging

Hi Experts,
        I need some help with a problem, I have a template checkbox column in m gridview. I have a custom sorting and paging procedure. The checkboxes do no persist their state going through different pages in the Gridview, I have found many incomplete solutions to this online. can someone help me out with providing a complete way (in VB.NET) to achieve this functionality. I am working with VB.NET and ASP.NET 2.0. I have my Gridview's  viewstate enabled true, and have tried the following links but they did not work. Also there is a select all unselect All checkbox header checkbox, I could not get the javascript examples to work so ended up using a serverside solution for it, the header checkbox loses its state as well going between the gridview pages.

http://aspalliance.com/774
* This solution only works if the number of results is less then the page size, I cannot use this since some of my gridviews have 30, 40+ pages.

http://www.dotnetjunkies.com/HowTo/9D01155A-A413-454A-98DE-977D540CF35D.dcik
*Applicable to .NET 1.1, I am using 2.0  

 Thanks in advance, I have seen a lot of other people come across the same problem online, hopefully someone here can provide a solution to this
groovbox303Asked:
Who is Participating?
 
RamuncikasConnect With a Mentor Commented:
I hope this is what you expected

R
Default.aspx.txt
Default.aspx.vb.txt
0
 
RamuncikasCommented:
Hi.

Let me explain you the way gridview works.
Suppose you have a grid with page size set to 20 rows. You are pulling 600 records from database and binding them to grid. Grid takes only records needed and show them. The rest of records are only used to calculate the number of pages to show numbers in grid's pager. But these records are not persisted in viewstate.
When user changes page number (by clicking on "next" or "previous" arrows or by clicking page number) your page gets event notification that grid is changing page. What you do here is pull same 600 records from database and bind the to grid again. And again - grid takes only records to show and dumps the rest of them.
So even if you enable viewstate for your checkboxes grid is rebound on each page load and their state is lost.

What I would suggest is to track the state of your checkboxes manually. On the initial load of page, or to be more precise - on the initial load of data - you:
1. create a object of type Dictionary<string, bool>
2. cycle trough the rows of datatable and fill dictionary object with IDs and checkbox states of each row.
3. bind datatable to grid
4. store dictionary object to viewstate (for later use)

Next if user chooses to view another page of a grid you do the following:
1. get dictionary object from viewstate
2. cycle through the rows of a grid and save values of checkboxes to a dictionary object
3. fill datatable from database
4. bind datatable to grid
5. cycle through rows of grid and set values of checkboxes from a dictionary

This way dictionary object maintains the state of ALL chekboxes that your grid intends to show. What you do is only "manually mimic" the state of checkboxes.

I'd be hapy to show you some code for this, but unfortunatly I can do this in C# only.

Hope this helps
Feel free to ask further questions if any.

Ramuncikas
0
 
groovbox303Author Commented:

 Hi Ramuncikas,
               ok I have implemented something similar to what you are mentioning and it is working to some extent but definately in not all cases. I will post an example here in the code. Its just an array of country names I bind to the grid. I use an arraylist and store it in session and retrieve it in another function the only way I could see the user selected options was to put these functions in the Databinding event. Here is the kicker, since I am using custom paging and sorting, I cannot just bind the grid once. I have to bind it if first the user wants to see it by clicking on the checkbox and then on every page. This is the only way I have seen custom paging work with examples online. Please check out my functions for remembering the checked boxes and redoing them after binding the gridview. If you test this example you will see it works if you make a few choices on one page and few on another. but where this does not work is when a user can go back and unselect all their options on a page. Also their is a hidden html input called hdoption, if the user selects the select all option the value for this is changed to all and all checkboxes are selected for pages. But I cant seem to mantain the state of the html header checkbox and work out the logic for storing different combinations for all scenarios.  If you can provide a working example of this scenario Id really appreciate it. Here is the code. thanks for your help again.

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="HtmlControlTestVb._Default" %>
 
<!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 runat="server">
    <title>Untitled Page</title>
     <script type="text/javascript">
    function executeCheck(elem){
    if(elem.checked) SetAllCheckBoxes("form1","myBox",true);
    else SetAllCheckBoxes("gridview1",false);
}
 
function SetAllCheckBoxes(gridview, CheckValue){
    var objCheckBoxes = document.getElementById(gridview).getElementsByTagName("input");
	var option = document.getElementById("hdoption");
	var cb = document.getElementById("Headercb");
	
	if(cb.checked)
	{
	  option.value = "All";
	}
	else
	{
	  option.value = "None";
	}
	if(!objCheckBoxes)
		return;
	var countCheckBoxes = objCheckBoxes.length;
	if(!countCheckBoxes)
		objCheckBoxes.checked = CheckValue;
	else
		// set the check value for all check boxes
		for(var i = 0; i < countCheckBoxes; i++)
			objCheckBoxes[i].checked = CheckValue;
}
 
 </script>
</head>
<body>
    <form id="form1" runat="server" >
    <div>
    <span>This checkbox at the top is optional only if this is selected is the gridview loaded</span>
    <asp:CheckBox ID="chkview" runat="server" Text="Select if you want to choose countries" AutoPostBack="True" />
    <br />
    <br />
    <table style="FONT-SIZE: 10pt; WIDTH: 872px; FONT-FAMILY: arial" >
    
    <tr id="rowGrid" runat="server" style="">
      <td>
      <input type="checkbox" onclick="executeCheck(this)" id="headercb"/>Select/UnSelect All
      <asp:GridView CssClass='Gridview' ID="gridview1" runat="server" AutoGenerateColumns="False" CellPadding="4"  GridLines="Vertical" AllowPaging="True" AllowSorting="True" PageSize="5" BackColor="White" BorderColor="#DEDFDE" BorderStyle="None" BorderWidth="1px" ForeColor="Black">
            <Columns>
                     <asp:BoundField DataField="Country" HeaderText="Country" />
                    <asp:TemplateField>
                        <ItemTemplate>
                            <input type="checkbox" name="myBox" value="1" runat="server" id="mycheckBox" />
                        </ItemTemplate>
                    </asp:TemplateField>
            </Columns>
          <FooterStyle BackColor="#CCCC99" />
          <RowStyle BackColor="#F7F7DE" />
          <SelectedRowStyle BackColor="#CE5D5A" Font-Bold="True" ForeColor="White" />
          <PagerStyle BackColor="#F7F7DE" ForeColor="Black" HorizontalAlign="Right" />
          <HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" />
          <AlternatingRowStyle BackColor="White" />
    </asp:GridView> 
    <input id="hdoption" type="hidden" runat="server" enableviewstate="true" />
    </td>
     </tr>  
    </table>       
    </div>
    </form>
</body>
</html>
 
CODE BEHIND:
Imports System.Data
Imports System.Text
 
 
 
Partial Public Class _Default
    Inherits System.Web.UI.Page
 
    Dim countries As String() = Regex.Split("Japan,America,England,USA,Nigeria,China,Pakistan,Brazil,Mexico,Canada,France,Spain,Argentina,Bahamas,Puerto Rico,Russia", ",")
    Private cObjs As ArrayList
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 
        If Not Page.IsPostBack Then
            Session.Add("ShowGrid", "None")
        End If
        'If (chkview.Checked) Then
        '    If Session.Item("ShowGrid") = "None" Then
        '        BindGrid()
        '    End If
        'End If
        BindGrid()
 
        'If Page.IsPostBack Then
        '    RememberChecked()
        'End If
 
    End Sub
 
    Protected Sub BindGrid()
 
        Dim Row As DataRow
        Dim table As New DataTable("Test")
        table.Columns.Add("Country")
        Dim i As Int32
        Dim Count = countries.Length - 1
        For i = 0 To Count
            Row = table.NewRow
            Row("Country") = countries(i)
            table.Rows.Add(Row)
        Next
        Dim DS As New DataSet
        DS.Tables.Add(table)
 
        gridview1.DataSource = DS.Tables(0)
        gridview1.DataBind()
 
 
    End Sub
 
    Protected Sub chkview_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkview.CheckedChanged
            
       If chkview.Checked Then
            rowGrid.Style("display") = "block"
            Session.Item("ShowGrid") = "Block"
        Else
            rowGrid.Style("display") = "None"
            Session.Item("ShowGrid") = "None"
        End If
 
    End Sub
 
 
    Protected Sub gridview1_PageIndexChanging(ByVal sender As System.Object, ByVal e As System.Web.UI.WebControls.GridViewPageEventArgs) Handles gridview1.PageIndexChanging
 
        gridview1.DataSource = SortDataTable(gridview1.DataSource, True)
        gridview1.PageIndex = e.NewPageIndex
        gridview1.DataBind()
        RedoCheckBoxes()
    End Sub
 
    Protected Function SortDataTable(ByVal dataTable As DataTable, ByVal isPageIndexChanging As Boolean) As DataView
 
        If Not dataTable Is Nothing Then
            Dim dataView As New DataView(dataTable)
            If GridViewSortExpression <> String.Empty Then
                If isPageIndexChanging Then
                    dataView.Sort = String.Format("{0} {1}", GridViewSortExpression, GridViewSortDirection)
 
                Else
 
                    dataView.Sort = String.Format("{0} {1}", GridViewSortExpression, GetSortDirection())
                End If
            End If
            Return dataView
        Else
            Return New DataView()
        End If
    End Function
 
    Private Property GridViewSortExpression() As String
        Get
            Return IIf(ViewState("SortExpression") = Nothing, String.Empty, ViewState("SortExpression"))
        End Get
        Set(ByVal value As String)
            ViewState("SortExpression") = value
        End Set
    End Property
 
    Private Function GetSortDirection() As String
        Select Case GridViewSortDirection
            Case "ASC"
                GridViewSortDirection = "DESC"
 
            Case "DESC"
                GridViewSortDirection = "ASC"
        End Select
 
        Return GridViewSortDirection
    End Function
    Private Property GridViewSortDirection() As String
        Get
            Return IIf(ViewState("SortDirection") = Nothing, "ASC", ViewState("SortDirection"))
        End Get
        Set(ByVal value As String)
            ViewState("SortDirection") = value
        End Set
    End Property
 
    Protected Sub RememberChecked()
      
 
        Dim CheckedItems As New ArrayList()
        Dim PatID As String
        For Each row As GridViewRow In gridview1.Rows
            Dim cb As HtmlInputCheckBox = CType(row.FindControl("mycheckbox"), HtmlInputCheckBox)
            PatID = row.Cells(0).Text
 
            If Not IsNothing(Session("CheckedItems")) Then
                CheckedItems = Session("CheckedItems")
            End If
 
            'If Checkbox is checked, Add to the arraylist otherwise if unchecked check to see if it is in arraylist,
            'and if it is then remove it
 
            If cb.Checked Then
                If Not CheckedItems.Contains(PatID) Then
                    CheckedItems.Add(PatID)
                End If
            Else
                If CheckedItems.Contains(PatID) Then
                    CheckedItems.Remove(PatID)
                End If
            End If
        Next
        'Update Session with the list of checked Items
 
        Session("CheckedItems") = CheckedItems
 
 
 
    End Sub
    Protected Sub RedoCheckBoxes()
        If hdoption.Value = "All" Then
 
            For Each row As GridViewRow In gridview1.Rows
                Dim cb As HtmlInputCheckBox = CType(row.FindControl("mycheckbox"), HtmlInputCheckBox)
                cb.Checked = True
            Next
        Else
 
            Dim CheckedItems As New ArrayList()
            CheckedItems = Session("CheckedItems")
            If Not IsNothing(CheckedItems) Then
                'Loop through Gridview Items
                Dim PatId As String
                For Each row As GridViewRow In gridview1.Rows
 
                    Dim cb As HtmlInputCheckBox = CType(row.FindControl("mycheckbox"), HtmlInputCheckBox)
                    PatId = row.Cells(0).Text
                    If CheckedItems.Contains(PatId) Then
                        cb.Checked = True
                    End If
                Next
            End If
        End If
    End Sub
 
    Protected Sub gridview1_Sorting(ByVal sender As System.Object, ByVal e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles gridview1.Sorting
        GridViewSortExpression = e.SortExpression
        Dim pageIndex As Int16 = gridview1.PageIndex
        gridview1.DataSource = SortDataTable(gridview1.DataSource, False)
        gridview1.DataBind()
        gridview1.PageIndex = pageIndex
    End Sub
 
  
    Protected Sub gridview1_DataBinding(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles gridview1.DataBinding
        If Page.IsPostBack Then
            CountMofos()
            If IsNothing(Session("CheckedItems")) Then
 
                RememberChecked()
 
            ElseIf (gridview1.Rows.Count > 0) Then
 
                If (Session.Item("Count") <> "0") Then
                    Dim CheckedItems As New ArrayList()
                    CheckedItems = Session("CheckedItems")
                    RememberChecked()
 
                End If
 
           
            End If
        End If
    End Sub
 
    Protected Sub CountMofos()
        If Not IsNothing(Session("CheckedItems")) Then
            Dim Count As Int32 = 0
            For Each row As GridViewRow In gridview1.Rows
                Dim cb As HtmlInputCheckBox = CType(row.FindControl("mycheckbox"), HtmlInputCheckBox)
                If cb.Checked Then
                    Count = Count + 1
                End If
 
            Next
            Session.Add("Count", Count)
        End If
        
    End Sub
End Class

Open in new window

0
 
RamuncikasCommented:
BTW, I did not implement sorting. This should be as simple as copy/paste from your code.
0
 
nagdotnetCommented:
Hi,
Can the above solution be used when custom paging is done using ROW_NUMBER() ,that means getting only current page data from sql server
0
All Courses

From novice to tech pro — start learning today.