Checking All CheckBoxes in a GridView Using Client-Side Script and a Check All CheckBox
By Scott Mitchell
An Updated Version Is Available! |
---|
In December 2010 I updated the code and concepts presented in this article to use unobtrusive JavaScript and the jQuery library. To learn more, read Checking All Checkboxes in a GridView Using jQuery. |
Introduction

In the code download that accompanied Checking All CheckBoxes in a GridView I included an example that improved the user interface with the client-side script approach to include a "Check/Uncheck All" checkbox in the header above the column of CheckBoxes in the GridView (see the screen shot to the right). Checking the header checkbox would check all checkboxes in the GridView, while unchecking the header checkbox would have the opposite effect. Adding such functionality was a bit more tricky than I had at first anticipated due to a couple of subtleties. In this article we'll explore these issues and the workarounds I employed to overcome them. Read on to learn more!
If you've not yet read the original article, Checking All CheckBoxes in a GridView, please take a moment to read that first before continuing on with this article...
Adding a "Check/Uncheck All" CheckBox to the Header
The first step in implementing this user experience is to add the "Check/Uncheck All" checkbox to the header of the column of CheckBoxes. Since we implemented the column of CheckBoxes in the GridView as a TemplateField, adding a header CheckBox control is as simple as adding a
HeaderTemplate
to the TemplateField, like so (some formatting-related GridView properties
have been removed for brevity):
|
This addition puts a CheckBox in the header of the column of CheckBoxes; however, clicking the header CheckBox doesn't do anything. Upon having the header CheckBox checked we want to check all of the CheckBoxes in the grid; similarly, when unchecking the header CheckBox we want all of the CheckBoxes in the grid to be unchecked. Moreover, to provide a snappy user experience, all of this needs to be done on the client-side using JavaScript.
In Checking All CheckBoxes in a GridView we looked at a JavaScript function named ChangeAllCheckBoxStates(checkState)
that checked (or unchecked) all of the CheckBoxes in the grid depending on the value of the checkState
input parameter.
In the client-side demo from that previous article, this function was called by the "Check All" and "Uncheck All" buttons passing in
values of true
and false
for the checkState
input parameter, respectively.
My initial idea was to simply reuse this JavaScript function with the header CheckBox. Namely, the client-side onclick
event for the header CheckBox would call ChangeAllCheckBoxStates
passing in this.checked
as the
value of checkState
.
To accomplish this I wired up the header CheckBox's client-side onclick
event
using the following server-side code in the GridView's DataBound
event handler:
Protected Sub FileList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles FileList.DataBound
|
The For Each
loop builds up the CheckBoxIDs
client-side array, which is iterated through in
the ChangeAllCheckBoxStates
function to check or uncheck all of the grid's CheckBoxes. Note that I grab a
reference to the header CheckBox by searching the GridView's HeaderRow
's control collection for the control
with ID
HeaderLevelCheckBox
. (This was the value of the ID
property of the header
CheckBox in the declarative syntax examined earlier in this article.)
Once I get a reference to the header CheckBox I indicate that the client-side onclick
event handler should invoke the
ChangeAllCheckBoxStates
passing in this.checked
as the value of checkState
by
assigning the JavaScript to invoke to the CheckBox's Attributes
collection's onclick
setting.
The Attributes
collection provides a way to specify additional attribute values for the control's rendered
HTML; see Working
with Client-Side Script for more information on working with client-side HTML attributes and events using server-side ASP.NET
code.
With this addition, checking the header CheckBox calls ChangeAllCheckBoxStates(true)
, checking all of the CheckBoxes in the
grid; conversely, unchecking the header CheckBox calls ChangeAllCheckBoxStates(false)
, unchecking all of the CheckBoxes.
Problem #1: The "Check All" and "Uncheck All" Buttons Don't Check/Uncheck the Header CheckBox
After making this change, the first issue I found was that checking the "Check All" and "Uncheck All" buttons on the page checked (or unchecked) all of the CheckBoxes in the grid except for the header CheckBox. This is because these two buttons call
ChangeAllCheckBoxStates
and ChangeAllCheckBoxStates
simply iterates through the
CheckBoxIDs
array, checking or unchecking all of the CheckBoxes registered in that array. Since the header CheckBox
is not included in that array, it's not affected by the ChangeAllCheckBoxStates
function. (Of course this isn't an issue
when directly checking or unchecking the header CheckBox since the user-based action of checking or unchecking the header CheckBox
handles this.)
To remedy this I simply added the header CheckBox to the list of ID
s in the CheckBoxIDs
array.
This was accomplished by adding the following line of code to the GridView's DataBound
event handler
immediately after setting the header CheckBox's Attributes("onclick")
property:
'Add the CheckBox's ID to the client-side CheckBoxIDs array
|
By adding the header CheckBox's ID
to the array, clicking the "Check All" or "Uncheck All" buttons correctly
check or uncheck the header CheckBox along with the row-level CheckBoxes in the grid.
Problem #2: Checking All CheckBoxes Manually or Unchecking a CheckBox Does Not Update the "Check/Uncheck All" Header CheckBox
Since the "Check/Uncheck All" header CheckBox checks all of the CheckBoxes in the grid I initially thought of the header CheckBox as a user interface item that solely performs some action - specifically, if you check the header CheckBox, all row-level CheckBoxes in the grid are checked. However, the header CheckBox really represents information about state of the CheckBoxes in the grid. That is, if the header CheckBox is checked, it indicates that all of the other CheckBoxes in the grid are also checked and if the header CheckBox is not checked then one or more of the other CheckBoxes in the grid must be unchecked.
Thinking of the header CheckBox in these terms, it became clear to me that the header CheckBox need to automatically be checked if all of the other CheckBoxes are manually checked; conversely, if all of the other CheckBoxes are checked and then the user manually unchecks one of them, the header CheckBox needs to be automatically unchecked. To accomplish this I decided to do the following:
- Add a JavaScript function named
ChangeHeaderAsNeeded()
that checks the header CheckBox if all of the other CheckBoxes in the grid are checked and unchecks the header CheckBox if there exists an unchecked CheckBox - Wire up each row-level CheckBox's client-side
onclick
event to call theChangeHeaderAsNeeded()
function
ChangeHeaderAsNeeded()
function. This function assumes that the first CheckBoxIDs
array element is the ID
of the header CheckBox, and that the remaining elements are the ID
s of the
row-level CheckBoxes. The function loops through the row-level CheckBoxes and, for each CheckBox, determines if it's checked. If
it is not, it unchecks the header CheckBox and exits the function. If the loop finishes without finding an unchecked
row-level CheckBox, the header CheckBox is checked.
|
Next, we need to configure each row-level CheckBox so that when its client-side onclick
event is fired the
ChangeHeaderAsNeeded()
function is invoked. This can be accomplished by adding the following client-side code
to the For Each
loop in the GridView's DataBound
event handler:
... Code omitted for brevity ...
|
That's all there is to it! With that change, checking all of the CheckBoxes manually will cause the header CheckBox to become checked; similarly, when all row-level CheckBoxes are checked, unchecking a single one will uncheck the header CheckBox.
Problem #3: When Doing a Postback that Doesn't Rebind the Data to the GridView, the "Check/Uncheck All" Header CheckBox No Longer Works
The code in the
DataBound
event handler assigns the client-side attributes to the checkboxes in the GridView
and also specifies the elements in the client-side CheckBoxIDs
array. The Web controls' Attributes
collection is stored in view state, but the script emitted using the ClientScriptManager
class is not.
This distinction is important because the DataBound
event handler only executes when the data is explicitly
bound to the GridView. If the GridView's view state is enabled (the default), on a non-grid related postback the GridView
is reconstructed from view state and does not explicitly rebind to its data source. Consequently, the DataBound
event handler does not run.
The net result of this behavior is that if the GridView's view state is enabled and a non-grid related postback occurs,
the client-side CheckBoxIDs
array is lost and the Check All / Uncheck All logic no longer works. To remedy this,
we need to forgo using the ClientScriptManager
and instead craft the necessary JavaScript for the array and
store it in a Literal Web control. Since the Literal control's Text
property is stored in view state, the client-side
array will remain regardless of whether the DataBound
event handler executes.
The following code achieves this by using a List of type String named ArrayValues
to hold the array elements. Every place
ClientScript.RegisterArrayDeclaration("CheckBoxIDs", value)
used to be is replaced by
ArrayValues.Add(value)
. Then, after all of the values have been added to the List, the Literal control's
Text
property is assigned the appropriate JavaScript syntax for creating this array. The bold sections
highlight the changes from the previous code snippets.
Protected Sub FileList_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles FileList.DataBound
|
Conclusion
In this article we saw how to add a "Check/Uncheck All" header CheckBox to the GridView that, using client-side techniques, provides a means to quickly check or uncheck all row-level CheckBoxes. The header CheckBox, along with the "Check All" and "Uncheck All" buttons, provide the user with multiple ways for checking or unchecking all of the grid's CheckBoxes. Since client-side script is used to handle checking and unchecking the CheckBoxes, the interface provides a snappy user experience.
Happy Programming!
Attachments
Further Reading