Checking All CheckBoxes in a GridView Using Client-Side Script and a Check All CheckBoxBy Scott Mitchell
|An Updated Version Is Available!|
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
HeaderTemplateto the TemplateField, like so (some formatting-related GridView properties have been removed for brevity):
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
false for the
checkState input parameter, respectively.
event for the header CheckBox would call
ChangeAllCheckBoxStates passing in
this.checked as the
To accomplish this I wired up the header CheckBox's client-side
using the following server-side code in the GridView's
DataBound event handler:
For Each loop builds up the
CheckBoxIDs client-side array, which is iterated through in
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
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
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
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
ChangeAllCheckBoxStatessimply iterates through the
CheckBoxIDsarray, 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
ChangeAllCheckBoxStatesfunction. (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
IDs in the
This was accomplished by adding the following line of code to the GridView's
DataBound event handler
immediately after setting the header CheckBox's
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:
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
onclickevent to call the
ChangeHeaderAsNeeded()function. This function assumes that the first
CheckBoxIDsarray element is the
IDof the header CheckBox, and that the remaining elements are the
IDs 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
For Each loop in the GridView's
DataBound event handler:
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
DataBoundevent handler assigns the client-side attributes to the checkboxes in the GridView and also specifies the elements in the client-side
CheckBoxIDsarray. The Web controls'
Attributescollection is stored in view state, but the script emitted using the
ClientScriptManagerclass is not. This distinction is important because the
DataBoundevent 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
DataBoundevent 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,
CheckBoxIDs array is lost and the Check All / Uncheck All logic no longer works. To remedy this,
we need to forgo using the
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
highlight the changes from the previous code snippets.
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.