Asp.net/JQuery GridView Multi-Selection Snippet
Posted: Tue Aug 11, 2015 11:18 am
Here we are concerned with the behaviours of an <asp:GridView> as it relates to selection. This widget by default supports a lightweight selection system...but many people choose not to use it because of the way it looks. What it does is add an extra column to your grid with a hyperlink that says "select" for each row. This is a pretty gritty way to handle things; most users wish to allow for a selection click in any column, not just over a hyperlink. I will detail two different solutions. One in asp.net code-behind, and one in JQuery. The Asp.Net solution is preferable if your GridView has a manageable amount of rows (50-200) or paging; the JQuery solution has better performance on the client side and can manage thousands of records without slowdown.
For the asp solution, the first thing you are going to want to do is enable the OnSelectedIndexChanged event in your grid view's markup. This assures we can assign our event listener properly.
Next, add the following method for your OnRowDataBound event:
This code causes the OnSelectedIndexChanged postback event to be fired on each row's "onClick" event. The fact that it uses postbacks instead of everything on client side is why performance becomes an issue with many records.
Next, we are going to use a HiddenField as comma delimiter-ed buffer holding each of our selected rows. Go ahead and add somewhere in your markup.
Finally, add the following method for your OnSelectedIndexChanged listener:
and you are good! A suggestion would be to change the FromHtml BackColor to a Css class representing your selection.
Finally, here is the JQuery solution for the same thing. With this solution, you no longer need the OnSelectedIndexChanged event listener so remove it from your markup. Same with the OnRowDataBound. Instead, you want to assure your GridView has the ViewStateMode="Enabled" attribute set.
Next, you are going to want to include in your markup the appropriate references to JQuery if you don't have it already. Something like this in your header content:
Next lets add a CSS class to represent our selected row. Something like this:
Finally, add the following JavaScript code:
Please note that you can remove the checkboxes in your application. They are redundant, but give the user an extra layer of control over their selection. If you want to have them, you need to make a column in your gridview have the checkboxes, something like this:
<Columns>
And that's it! Pretty simple, hope this is able to help save somebody some time. Thanks for reading
For the asp solution, the first thing you are going to want to do is enable the OnSelectedIndexChanged event in your grid view's markup. This assures we can assign our event listener properly.
Next, add the following method for your OnRowDataBound event:
Code: Select all
Protected Sub OnRowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Attributes("onclick") = Page.ClientScript.GetPostBackClientHyperlink(GridView1, "Select$" & e.Row.RowIndex)
e.Row.ToolTip = "Click to select this row."
End If
End Sub
Next, we are going to use a HiddenField as comma delimiter-ed buffer holding each of our selected rows. Go ahead and add
Code: Select all
<asp:HiddenField id="SelectionHolder" runat="server" />
Finally, add the following method for your OnSelectedIndexChanged listener:
Code: Select all
requires: gridview has OnSelectedIndexChanged event enabled
highlights selected row(s) from click/shift-click
Sub ProjectGridView_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim ctrlHeld As Boolean = If(Control.ModifierKeys = Keys.Control, True, False)
Dim shiftHeld As Boolean = If(Control.ModifierKeys = Keys.Shift, True, False)
Dim a As String() = Split(selectionHolder.Value.ToString, ",")
If Not selectionHolder.Value = "" And Not ctrlHeld And Not shiftHeld Then
Dim count As Integer = 0
For Each Str As String In a
If Not String.IsNullOrEmpty(Str) And Not count = 0 Then
'Str = Str.Remove(0, 1)
Dim num As Integer = Integer.Parse(Str)
If Not ctrlHeld Then
gvList.Rows(num).BackColor = If(num Mod 2 = 0, ColorTranslator.FromHtml("#FFFFFF"), ColorTranslator.FromHtml("#FFFFd1"))
gvList.Rows(num).ToolTip = "Click to select this row."
End If
End If
count += 1
Next
If Not ctrlHeld Then
selectionHolder.Value = ""
End If
End If
If (shiftHeld) And a.Count > 0 Then
Dim lastNum As Integer = Integer.Parse(a.Last)
If (lastNum > gvList.SelectedIndex) Then
For n As Integer = gvList.SelectedIndex To lastNum - 1
gvList.Rows(n).BackColor = ColorTranslator.FromHtml("#A1DCF2")
gvList.Rows(n).ToolTip = String.Empty
selectionHolder.Value += "," + n.ToString
Next
End If
If (lastNum < gvList.SelectedIndex) Then
For n As Integer = lastNum To gvList.SelectedIndex
gvList.Rows(n).BackColor = ColorTranslator.FromHtml("#A1DCF2")
gvList.Rows(n).ToolTip = String.Empty
selectionHolder.Value += "," + n.ToString
Next
End If
End If
selectionHolder.Value += "," + gvList.SelectedIndex.ToString
gvList.Rows(gvList.SelectedIndex).BackColor = ColorTranslator.FromHtml("#A1DCF2")
gvList.Rows(gvList.SelectedIndex).ToolTip = String.Empty
End Sub
Finally, here is the JQuery solution for the same thing. With this solution, you no longer need the OnSelectedIndexChanged event listener so remove it from your markup. Same with the OnRowDataBound. Instead, you want to assure your GridView has the ViewStateMode="Enabled" attribute set.
Next, you are going to want to include in your markup the appropriate references to JQuery if you don't have it already. Something like this in your header content:
Code: Select all
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
Code: Select all
<style type="text/css">
.row_selected{
background-color: #A1DCF2;
}
</style>
Code: Select all
<script type = "text/javascript">
//used for shiftselecting
var shiftPressed = false;
var previousRow = ""
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function (evt, args) {
$(document).ready(function () {
$(window).keydown(function (evt) {
//alert('Ctrl downX1');
if (evt.which == 16) { // ctrl
shiftPressed = true;
//alert('Ctrl downX');
}
}).keyup(function (evt) {
if (evt.which == 16) { // ctrl
shiftPressed = false;
//alert('Ctrl downX2');
}
});
});
});
//compound selection function for gridview rows
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function (evt, args) {
$(document).ready(function () {
$("[id*=gvList] td").click(function () {
//is event already handled?
var str = $(event.target)[0].id;
if (str.indexOf("cbImport") >= 0) {
return;
}
var index = $(this).closest('td').parent()[0].sectionRowIndex;
if (previousRow.toString().length == 0) {
//alert(index)
if (jQuery(this).closest('tr').find('[type=checkbox]').is(":checked")) {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', false);
//add the selected class to selected row
$($(this).closest("tr")).removeClass("row_selected");
previousRow = "";
} else {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', true);
//remove the selected class from selected row
$($(this).closest("tr")).addClass("row_selected");
previousRow = index.toString();
}
} else {
if (shiftPressed) {
var start = (parseInt(previousRow.toString()) < index) ? parseInt(previousRow.toString()) : index;
var end = (parseInt(previousRow.toString()) < index) ? index : parseInt(previousRow.toString());
var count = 0
$("[id*=gvList] tr:has(td)").each(function () {
if (count >= start && count <= end) {
if (jQuery(this).closest('tr').find('[type=checkbox]').is(":checked") && count != parseInt(previousRow.toString())) {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', false);
//add the selected class to selected row
$($(this).closest("tr")).removeClass("row_selected");
} else if (count != parseInt(previousRow.toString())) {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', true);
//remove the selected class from selected row
$($(this).closest("tr")).addClass("row_selected");
}
}
count++;
});
} else {
if (jQuery(this).closest('tr').find('[type=checkbox]').is(":checked")) {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', false);
//add the selected class to selected row
$($(this).closest("tr")).removeClass("row_selected");
previousRow = "";
} else {
jQuery(this).closest('tr').find('[type=checkbox]').prop('checked', true);
//remove the selected class from selected row
$($(this).closest("tr")).addClass("row_selected");
previousRow = index.toString();
}
}
}
});
});
});
//select all via header checkbox
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function (evt, args) {
$(document).ready(function () {
//Checkbox header select all settings
$('.chkSelectAll').click(function () {
if ($(this).is(":checked")) {
$('.chkSelect').prop('checked', true);
//add the selected class from each row
$('.chkSelect').closest("tr").addClass("row_selected");
} else {
$('.chkSelect').prop('checked', false);
//remove selected class from each row
$('.chkSelect').closest("tr").removeClass("row_selected");
}
previousRow = "";
});
});
});
//selection via checkboxes
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function (evt, args) {
$(document).ready(function () {
//Checkbox gridview row select settings
$('.chkSelect').click(function () {
//alert("Message");
//remove chkSelectAll checked when any of the child checkbox clicked
$('.chkSelectAll').removeAttr("checked");
if ($(this).is(":checked")) {
//add the selected class to selected row
$($(this).closest("tr")).addClass("row_selected");
previousRow = $(this).closest('td').parent()[0].sectionRowIndex.toString();
//alert(previousRow.toString());
} else {
//remove the selected class from selected row
$($(this).closest("tr")).removeClass("row_selected");
previousRow = "";
//alert(previousRow.toString());
}
});
});
});
</script>
<Columns>
Code: Select all
<asp:TemplateField>
<HeaderTemplate>
<input type="checkbox" id="chkSelectAll" class="chkSelectAll" style="width: 50px;" runat="server" />
</HeaderTemplate>
<ItemTemplate>
<input type="checkbox" class="chkSelect" id="cbImport" style="width: 50px;" runat="server"/>
</ItemTemplate>
</asp:TemplateField>
<%--YOUR OTHER COLUMN DATA HERE--%>
</Columns>