Check or uncheck all CheckBoxes in an ASP.NET GridView with jQuery
A check box in the header that selects every row is standard in admin grids. In Web Forms the boxes are server controls inside a TemplateField; jQuery handles the clicking in the browser, and the server reads the result on postback as usual.
Markup
<asp:GridView ID="gvMail" runat="server" AutoGenerateColumns="false" DataKeyNames="MessageId" CssClass="grid">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
<asp:CheckBox ID="chkAll" runat="server" CssClass="check-all" ToolTip="Select all" />
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="chkRow" runat="server" CssClass="check-row" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Subject" HeaderText="Subject" />
<asp:BoundField DataField="Received" HeaderText="Received" DataFormatString="{0:g}" />
</Columns>
</asp:GridView>
<asp:Button ID="btnDelete" runat="server" Text="Delete selected" OnClick="btnDelete_Click" />
An asp:CheckBox renders the CssClass on a <span> that wraps the <input>, not on the input itself. The selectors below therefore look for the input inside the span: .check-row input.
jQuery
$(function () {
var $grid = $(".grid");
function rows() { return $grid.find(".check-row input[type=checkbox]"); }
function header() { return $grid.find(".check-all input[type=checkbox]"); }
// header -> rows
$grid.on("change", ".check-all input", function () {
rows().prop("checked", this.checked);
header().prop("indeterminate", false);
});
// rows -> header
$grid.on("change", ".check-row input", function () {
var total = rows().length, checked = rows().filter(":checked").length;
header().prop({ checked: total > 0 && checked === total,
indeterminate: checked > 0 && checked < total });
});
});
The indeterminate property shows a dash in the header box when only some rows are selected. It is a visual state only: it is never posted, and the server sees the header box as unchecked.
Reading the selection on the server
protected void btnDelete_Click(object sender, EventArgs e)
{
var ids = new List<int>();
foreach (GridViewRow row in gvMail.Rows)
{
if (row.RowType != DataControlRowType.DataRow) continue;
var chk = (CheckBox)row.FindControl("chkRow");
if (chk.Checked) ids.Add((int)gvMail.DataKeys[row.RowIndex].Value);
}
if (ids.Count == 0) return;
MailRepository.Delete(ids); // one parameterised statement, or a table-valued parameter
BindGrid();
}
Check boxes changed by script are posted like any others, so chk.Checked reflects what the user sees. Bind the grid only when !IsPostBack in Page_Load, otherwise the boxes are recreated unchecked before the click handler runs. Deleting a list of ids in one call is shown in Passing a DataTable as a table-valued parameter.
Variations
- Highlight the selected rows: Highlight a GridView row when its check box is ticked.
- Keep selections across pages: Keep checked rows with paging.
- Without jQuery:
document.querySelectorAll(".check-row input").forEach(cb => cb.checked = all.checked).
Common mistakes
- Selecting
.check-rowand settingcheckedon it: the class is on the wrapping span, not the input. - Binding the handlers once, then refreshing the grid in an
UpdatePanel: delegate from a container outside the panel, or rebind inpageLoad(). - Trusting the header box on the server: it is not a reliable "all rows" flag, especially with paging. Read each row.
Official documentation
- jQuery API: .prop(), :checked selector, .on()
- MDN: HTMLInputElement.indeterminate
- Microsoft Learn: CheckBox class, GridView.DataKeys