Thursday 14 April 2011

Multirow Selection in GridView by retaining selections on Multiple Pages



In this post, I will be explaining the third implementation of the series (Multirow Selection in GridView when to retain multiple page selections)

I am using Northwind database for this example, so make sure to have Northwind database to use this sample. The comments on the code will explain more about the functionality of the code.

For more understanding below is the use case:

  • When page gets loaded, the GridView should bind the records from the database.
  • When the GridView shows, rows should be selected with the data already stored and fetched in code behind (say Id = 2, 3, 8 etc.). It means, the check box must be selected and the color of the row must be changed to selected color.
  • Once the page loaded, the used can select or unselect any row in the grid using check box provided on each row in a separate column.
  • When the user selects (check the checkbox) a particular row, the row background color must be changed to selected color.
  • When the user unselects (uncheck the checkbox) a particular row, the row background color must be changed to default color. (To know what is the existing back color of that row when unselecting row, I am storing the row back color in to a hidden control at each row)
  • When user press any button, the code behind should have the ability to get the selected row Id. (For this example, show the Id selected in the page itself)

This implementation will work for both single page and multi pages GridView selections. It means, if GridView shows have 10 records per pages and the user selects/unselects some records in one page and go to another page for some other record selection, the selection/un selection done in other pages will be retain. So if the user comes back to the same page which already selected/unselected, the same will be shown to the user and will be available in the code behind also.

To implement this, I am holding a hidden control which will be used to store all the selected row ids. Initially when the page gets loaded, the selected row ids will be populated from code behind to this control. So JavaScript will handle the selection behavior. As all this selection and un selection done thro’ Javascript, it will be faster than using code behind.

In this example, I am using CSS style to show the row selected normal colors. So there will be only two types of colors and no alternative row colors available in GridView. You can enhance the same example by having a hidden control in each row as implemented in the previous post.

The implementation as follows:

In aspx script.
<asp:GridView ID="grdViewProducts" runat="server"
    AllowPaging="True" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%"
    CellPadding="4" ForeColor="#333333" 
    onpageindexchanging="grdViewProducts_PageIndexChanging">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category Name" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" DataFormatString="{0:#0.00}" />
        <asp:TemplateField HeaderText="Select" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:CheckBox runat="server" ID="chkSelect" onclick="SelectRow(this.id)" />
                <asp:Label runat="server" ID="lblProductId" Text='<%# Eval("ProductID") %>' CssClass="InvisibleControl"></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Right" />
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
</asp:GridView>
<asp:HiddenField ID="hndSelectedRowId" runat="server" Value="" />
<asp:Button ID="btnProcess" runat="server" Text="Process" Width="100px" OnClick="btnProcess_Click" />
JavaScript
var Const_LastCol_Index = 6;

// I am calling a Javascript function to change the selected row color on page load
var browserName = navigator.appName;
if (browserName == "Microsoft Internet Explorer") {
    window.onload = SelectGridOnLoad;
}
else {
    if (browserName == "Netscape") //google chrome app.Name
    {
        setTimeout("SelectGridOnLoad()", 500);
    }
    else {
        window.onload = SelectGridOnLoad; // helps with Opera
    }
}

function SelectGridOnLoad() {
    
    // Looping all the row (Here - 1 is used as I have footer in the Grid View and starting from 1 Row (0 is Header)
    // So if you dont have Footer required to loop till end of the row (ie., no -1).
    for (var intRow = 1; intRow < document.getElementById('<%= grdViewProducts.ClientID %>').rows.length - 1; intRow++) {

        // Declaring variable for Holding Values
        var vProductId = 0;
        var vCheckById = '';

        // Getting the GridView Row
        var gridRow = document.getElementById('<%= grdViewProducts.ClientID %>').rows[intRow];

        for (var intCell = 0; intCell < gridRow.cells[Const_LastCol_Index].childNodes.length; intCell++) // Looping the last Row controls
        {
            if (gridRow.cells[Const_LastCol_Index].childNodes[intCell].id != null) {

                if (gridRow.cells[Const_LastCol_Index].childNodes[intCell].id.indexOf("chkSelect") != -1)
                    vCheckById = gridRow.cells[Const_LastCol_Index].childNodes[intCell].id; // Getting the CheckBox Id

                if (gridRow.cells[Const_LastCol_Index].childNodes[intCell].id.indexOf("lblProductId") != -1) {
                    if (browserName == "Microsoft Internet Explorer")  //Getting the ProductId
                        vProductId = gridRow.cells[Const_LastCol_Index].childNodes[intCell].innerText;
                    else
                        vProductId = gridRow.cells[Const_LastCol_Index].childNodes[intCell].textContent;
                }
            }
        }

        var vSelecteIds = "," + document.getElementById('<%= hndSelectedRowId.ClientID %>').value + ",";
        if (vSelecteIds.indexOf("," + vProductId + ",") != -1) // Means Selected
        {
            document.getElementById(vCheckById).checked = true;
            gridRow.className = "SelectedRowStyle";
        }
        else
            gridRow.className = "NormalRowStyle";
            
    }
}
function SelectRow(Id) {
    var vProductId = 0;

    var gridRow = document.getElementById(Id).parentNode.parentNode;
    
    // Getting the Product Id
    for (var intCell = 0; intCell < gridRow.cells[Const_LastCol_Index].childNodes.length; intCell++) // Looping the last Row controls
    {
        if (gridRow.cells[Const_LastCol_Index].childNodes[intCell].id != null) {

            if (gridRow.cells[Const_LastCol_Index].childNodes[intCell].id.indexOf("lblProductId") != -1) {
                if (browserName == "Microsoft Internet Explorer")  //Getting the ProductId
                    vProductId = gridRow.cells[Const_LastCol_Index].childNodes[intCell].innerText;
                else
                    vProductId = gridRow.cells[Const_LastCol_Index].childNodes[intCell].textContent;
            }
        }
    }

    if (document.getElementById(Id).checked == true) // If Selected
    {
        document.getElementById('<%= hndSelectedRowId.ClientID %>').value += "," + vProductId;
        gridRow.className = "SelectedRowStyle";
    }
    else // If unselected - Replace the style to normal row
    {
        var vSelectedId = "," + document.getElementById('<%= hndSelectedRowId.ClientID %>').value + ",";
        vSelectedId = vSelectedId.replace("," + vProductId + ",", ","); // Removing the Id

        vSelectedId = vSelectedId.replace(/,,/g, ","); // Replacing all ,, ,,, ,,,, etc., to ,
        document.getElementById('<%= hndSelectedRowId.ClientID %>').value = vSelectedId.substr(1, vSelectedId.length - 2); // Removing , both the end and assigning
        
        document.getElementById(Id).parentNode.parentNode.className = "NormalRowStyle";
    }
}
Style Sheet
.NormalRowStyle
{
 background-color:#F7F6F3;
 color:#333333;
}

.SelectedRowStyle
{
 background-color:#F7BE81;
 color:#333333;
}
.InvisibleControl
{
 display:none;
}
C# Codebehind
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        BindGrid();
        hndSelectedRowId.Value = "2,16,20";
    }
}
/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private void BindGrid()
{
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
    {

        SqlCommand command = new SqlCommand(
               "SELECT ProductID, ProductName, CompanyName, CategoryName, " +
               "QuantityPerUnit, UnitPrice FROM Products " +
               "JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
               "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection);

        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);

        IList<ProductView> productViewList = new List<ProductView>();
        while (dr.Read())
        {
            ProductView productView = new ProductView();
            productView.ProductID = Convert.ToInt32(dr["ProductID"].ToString());
            productView.ProductName = dr["ProductName"].ToString();
            productView.CompanyName = dr["CompanyName"].ToString();
            productView.CategoryName = dr["CategoryName"].ToString();
            productView.QuantityPerUnit = dr["QuantityPerUnit"].ToString();
            productView.UnitPrice = Convert.ToDouble(dr["UnitPrice"].ToString());
            productViewList.Add(productView);
        }
        grdViewProducts.DataSource = productViewList;
        grdViewProducts.DataBind();
    }
}

protected void btnProcess_Click(object sender, EventArgs e)
{
    if (grdViewProducts.Rows.Count > 0)
    {
        Response.Write("Selected Row Id : ");
        Response.Write(hndSelectedRowId.Value);
    }
}

protected void grdViewProducts_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    grdViewProducts.PageIndex = e.NewPageIndex;
    BindGrid();
}
VB Codebehind
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then
            BindGrid()
            hndSelectedRowId.Value = "2,16,20"
        End If
    End Sub
    ''' <summary>
    ''' Method which binds the data to the Grid
    ''' </summary>
    Private Sub BindGrid()
        Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("SQLConnection").ConnectionString)

            Dim command As New SqlCommand("SELECT ProductID, ProductName, CompanyName, CategoryName, " & "QuantityPerUnit, UnitPrice FROM Products " & "JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " & "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection)

            connection.Open()
            Dim dr As SqlDataReader = command.ExecuteReader(CommandBehavior.CloseConnection)

            Dim productViewList As IList(Of ProductView) = New List(Of ProductView)()
            While dr.Read()
                Dim productView As New ProductView()
                productView.ProductID = Convert.ToInt32(dr("ProductID").ToString())
                productView.ProductName = dr("ProductName").ToString()
                productView.CompanyName = dr("CompanyName").ToString()
                productView.CategoryName = dr("CategoryName").ToString()
                productView.QuantityPerUnit = dr("QuantityPerUnit").ToString()
                productView.UnitPrice = Convert.ToDouble(dr("UnitPrice").ToString())
                productViewList.Add(productView)
            End While
            grdViewProducts.DataSource = productViewList
            grdViewProducts.DataBind()
        End Using
    End Sub

    Protected Sub btnProcess_Click(ByVal sender As Object, ByVal e As EventArgs)
        If grdViewProducts.Rows.Count > 0 Then
            Response.Write("Selected Row Id : ")
            Response.Write(hndSelectedRowId.Value)
        End If
    End Sub

    Protected Sub grdViewProducts_PageIndexChanging(ByVal sender As Object, ByVal e As GridViewPageEventArgs)
        grdViewProducts.PageIndex = e.NewPageIndex
        BindGrid()
    End Sub
public class ProductView
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public string CompanyName { get; set; }
    public string CategoryName { get; set; }
    public string QuantityPerUnit { get; set; }
    public double UnitPrice { get; set; }
}
Public Class ProductView
    Public Property ProductID() As Integer
        Get
            Return m_ProductID
        End Get
        Set(ByVal value As Integer)
            m_ProductID = value
        End Set
    End Property
    Private m_ProductID As Integer
    Public Property ProductName() As String
        Get
            Return m_ProductName
        End Get
        Set(ByVal value As String)
            m_ProductName = value
        End Set
    End Property
    Private m_ProductName As String
    Public Property CompanyName() As String
        Get
            Return m_CompanyName
        End Get
        Set(ByVal value As String)
            m_CompanyName = value
        End Set
    End Property
    Private m_CompanyName As String
    Public Property CategoryName() As String
        Get
            Return m_CategoryName
        End Get
        Set(ByVal value As String)
            m_CategoryName = value
        End Set
    End Property
    Private m_CategoryName As String
    Public Property QuantityPerUnit() As String
        Get
            Return m_QuantityPerUnit
        End Get
        Set(ByVal value As String)
            m_QuantityPerUnit = value
        End Set
    End Property
    Private m_QuantityPerUnit As String
    Public Property UnitPrice() As Double
        Get
            Return m_UnitPrice
        End Get
        Set(ByVal value As Double)
            m_UnitPrice = value
        End Set
    End Property
    Private m_UnitPrice As Double
End Class

This code has been tested with IE 6.0/8.0, Chrome 10.0, Firefox 3.6, Opera 11.01

Here is the output of the example.
First Page without user selection (one record selected from code behind)

First page after selecting some records

Second page (some records are selected using code behind)

After selecting some records in second page and press Process button

Came back to First page and press Process button (the records which was selected retained)

download the source code in C# here and in VB here
The other links on How to select multiple row in GridView:

  1. Multirow Selection in GridView when CSS used for row style
  2. Multirow Selection in GridView when alternative row colors used
  3. Multirow Selection in GridView by retaining selections on Multiple Pages
  4. Select All in Single Page GridView
  5. Select All in Multiple Page GridView


0 Responses to “Multirow Selection in GridView by retaining selections on Multiple Pages”

Post a Comment