Working with CheckBoxes within DataGrid Control
Add CommentIntroduction
Recently I have seen a lot of queries on the newsgroups where people want to create Hotmail like functionality with CheckBoxes within their DataGrid control's. This article should help you sort out these queries.
The problem is that there is a CheckBox in the Header of
the DataGrid, just above a column of Checkboxes. Now the functionality
required is that when the header CheckBox is selected / deselected
relevantly all the CheckBoxes in the column should be selected /
deselected. Actually, selection / deselection of CheckBoxes is a
client-side scripting activity hence we will have to generate our
server-side code in such a way that it can be easily manipulated by
client-side scripting.
Another functionality required is that when an external
button is clicked we should be able to retrieve all the checked rows
from the DataGrid on the server.
Part 1- CheckBox Header
Let's start with the first part of the problem i.e. to deal with working of CheckBox in the header of a CheckBox column. To straight jump to the required functionality, I have used the Pubs database, that's installed with the .NET SDK and MSDE.
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<script language="C#" runat="server">
SqlConnection myConnection;
protected void Page_Load(Object Src, EventArgs E)
{
myConnection = new SqlConnection("server=(local)\\NetSDK;
database=pubs;Trusted_Connection=yes");
if (!IsPostBack)
BindGrid();
}
public void BindGrid()
{
SqlDataAdapter myCommand = new SqlDataAdapter("select * from Authors",
myConnection);
DataSet ds = new DataSet();
myCommand.Fill(ds, "Authors");
MyDataGrid.DataSource=ds.Tables["Authors"].DefaultView;
MyDataGrid.DataBind();
}
</script>
<body style="font: 10pt verdana">
<form runat="server" id="form1" name="form1" >
<h3><font face="Verdana">Working with Checkboxes within a DataGrid</font></h3>
<ASP:DataGrid id="MyDataGrid" runat="server"
Width="800"
BackColor="#ccccff"
BorderColor="black"
ShowFooter="false"
CellPadding=3
CellSpacing="0"
Font-Name="Verdana"
Font-Size="8pt"
HeaderStyle-BackColor="#aaaadd"
AutoGenerateColumns="false"
>
<Columns>
<asp:BoundColumn HeaderText="au_id" DataField="au_id" />
<asp:BoundColumn HeaderText="au_lname" DataField="au_lname" />
<asp:TemplateColumn HeaderText="au_fname" >
<ItemTemplate>
<asp:Label id="au_fname"
Text='<%# DataBinder.Eval(Container.DataItem, "au_fname") %>'
runat="server" />
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn HeaderText="city" DataField="city" />
<asp:TemplateColumn HeaderText="contract" >
<HeaderTemplate>
<input type=checkbox id="checkAll" runat="server">
Contracts
</HeaderTemplate>
<ItemTemplate>
<input type=checkbox runat="server" id="contract"
checked='<%# DataBinder.Eval(Container.DataItem, "contract") %>'/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</ASP:DataGrid>
</form>
</body>
</html>
|
The above code snip should be very familiar to you all by now, I am
simply data binding the Authors table from the Pubs database to a
DataGrid.
The only advanced functionality here is the definition of custom
templates in the DataGrid. Since I want to display the Contract column
as a CheckBox column, I use a custom TemplateColumn definition and
bind the Boolean database field Contract with the checked property of
the HtmlInputCheckBox control defined in the ItemTemplate. Also within
the TemplateColumn I have defined a HeaderItem template where I have
placed a normal HtmlInputCheckBox control.
A point to note here is that if you want to perform any client-side scripting with your server controls, HtmlControls are the easiest to use, hence I have used the HtmlInputCheckBox control. Also I have data-bound the au_fname field within the ItemTemplate, since I want to retrieve the value this field upon postback in part-2 of this article, but generally you might prefer using the identity column of the table, so that it help's you perform further operations on the selected records.
Next, I change the definition of the HtmlInputCheckBox control in the Header so that it's onclick client-side event calls a JavaScript function CheckAll(). The updated definition of the HtmlInputCheckbox is shown below :
<input type=checkbox id="checkAll" onclick="CheckAll(this);" runat="server"> |
Now there are many ways you can handle this kind of interaction in JavaScript depending on your solution. Since there can be a lot of permutations of the JavaScript functionality wanted, I will be targeting a common functionality i.e. when the CheckBox in the header is selected / deselected all the CheckBoxes in the column below should check / uncheck respectively. Add the following JavaScript below the DataGrid definition in your code.
Note: I have checked this JavaScript
in IE 6.0 and Mozilla 1.0.
<script >
var frm = document.form1 ;
function CheckAll( checkAllBox )
{
var actVar = checkAllBox.checked ;
for(i=0;i< frm.length;i++)
{
e=frm.elements[i];
if ( e.type=='checkbox' && e.name.indexOf("contract") != -1 )
e.checked= actVar ;
}
}
</script>
|
In the above JavaScript, I first check the state of the CheckBox that fired the event i.e. the header CheckBox. Next, I loop through the form elements searching for all CheckBox controls which have the word "contract" in their name. Always remember that ASP.NET runtime allocates unique names for its controls on the client side, hence you cannot directly use the value of the name attribute you have used while coding your page. Although, the value you provide to the name attribute while defining the control will feature in the client side generated unique id for the control. Hence I am using the indexof function to match the client-side name of the CheckBoxes I want to select. Finally, I set the value of the CheckBoxes to match the value of the Header CheckBox.
This code works correctly, with one minor glitch
being that after the Header CheckBox has been selected, which in turn
selects all the CheckBoxes in the column and if any CheckBox in the
column is later unchecked, the Header CheckBox remains checked! (
Confusing? )
Ideally, the Header CheckBox should only remain checked only if all the CheckBoxes in the column are checked.
To counter this we need to wire-up the column CheckBoxes with another
JavaScript function as shown below:
<input type=checkbox runat="server" id="contract" onclick="UnCheck();" checked='<%# DataBinder.Eval(Container.DataItem, "contract") %>'/> |
Also add the new JavaScript function UnCheck as shown below:
function UnCheck()
{
for(i=0;i< frm.length;i++)
{
e=frm.elements[i];
if ( e.type=='checkbox' && e.name.indexOf("checkAll") != -1 )
{
e.checked= false ;
break;
}
}
}
|
In the above snip of code I just look through the form elements to find the CheckBox in the header and uncheck it.
This finishes the implementation of the first part of this tutorial, now you can save and test the page.
Part 2 - List Checked Rows
Coming to the second part of the article, I will discuss that after the relevant CheckBoxes have been selected there has to be a mechanism, so that on the server-side we know which rows were checked, hence we can perform the relevant operations, usually delete or select.
First we add a Button control to the page as shown below
<asp:button Text="List Selected" runat="server" OnClick="Show_Selected" /> |
And then define its server side event handler as shown below:
public void Show_Selected( object sender, EventArgs e)
{
foreach( DataGridItem di in MyDataGrid.Items )
{
HtmlInputCheckBox cb = (HtmlInputCheckBox)di.FindControl("contract") ;
if( cb !=null && cb.Checked )
{
Label lb = (Label)di.FindControl("au_fname");
Response.Write( lb.Text +"<br>" );
}
}
}
|
In the above method, I loop through each DataGridItem contained
within the DataGrid and search for the HtmlInputCheckBox control and
then check its value. Later the first names (au_fname) of the Checked
rows is printed out using Response.Write. You would be ideally
performing some other action based on this selected rows. Two things I
would like to instruct at this point,
1) I am printing the values of the au_fname field, since if you
remember in the first part I had defined a ItemTemplate for the field.
You could also use the DataGridItem's ControlCollection indexer to
find the relevant values.
2) The above logic will not work is you have paging enabled in your
DataGrid. In that case you will have to check the PageSize and
CurrentPage properties of the DataGrid first and only then retrieve values
from rows shown on that page.
For your convince I am listing the completely modified source code once again below
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<script language="C#" runat="server">
SqlConnection myConnection;
protected void Page_Load(Object Src, EventArgs E)
{
myConnection = new SqlConnection("server=(local)\\NetSDK;
database=pubs;Trusted_Connection=yes");
if (!IsPostBack)
BindGrid();
}
public void BindGrid()
{
SqlDataAdapter myCommand = new SqlDataAdapter("select * from Authors",
myConnection);
DataSet ds = new DataSet();
myCommand.Fill(ds, "Authors");
MyDataGrid.DataSource=ds.Tables["Authors"].DefaultView;
MyDataGrid.DataBind();
}
public void Show_Selected( object sender, EventArgs e)
{
foreach( DataGridItem di in MyDataGrid.Items )
{
HtmlInputCheckBox cb = (HtmlInputCheckBox)di.FindControl("contract") ;
if( cb !=null && cb.Checked )
{
Label lb = (Label)di.FindControl("au_fname");
Response.Write( lb.Text +"<br>" );
}
}
}
</script>
<body style="font: 10pt verdana">
<form runat="server" id="form1" name="form1" >
<h3><font face="Verdana">Working with Checkboxes within a DataGrid</font></h3>
<ASP:DataGrid id="MyDataGrid" runat="server"
Width="800"
BackColor="#ccccff"
BorderColor="black"
ShowFooter="false"
CellPadding=3
CellSpacing="0"
Font-Name="Verdana"
Font-Size="8pt"
HeaderStyle-BackColor="#aaaadd"
AutoGenerateColumns="false"
>
<Columns>
<asp:BoundColumn HeaderText="au_id" DataField="au_id" />
<asp:BoundColumn HeaderText="au_lname" DataField="au_lname" />
<asp:TemplateColumn HeaderText="au_fname" >
<ItemTemplate>
<asp:Label id="au_fname"
Text='<%# DataBinder.Eval(Container.DataItem, "au_fname") %>'
runat="server" />
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn HeaderText="city" DataField="city" />
<asp:TemplateColumn HeaderText="contract" >
<HeaderTemplate>
<input type=checkbox id="checkAll" onclick="CheckAll(this);" runat="server">
Contracts
</HeaderTemplate>
<ItemTemplate>
<input type=checkbox runat="server" id="contract" onclick="UnCheck();"
checked='<%# DataBinder.Eval(Container.DataItem, "contract") %>'/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</ASP:DataGrid>
<asp:button Text="List Selected" runat="server" OnClick="Show_Selected" />
<script >
var frm = document.form1 ;
function CheckAll( checkAllBox )
{
var actVar = checkAllBox.checked ;
for(i=0;i< frm.length;i++)
{
e=frm.elements[i];
if ( e.type=='checkbox' && e.name.indexOf("contract") != -1 )
e.checked= actVar ;
}
}
function UnCheck()
{
for(i=0;i< frm.length;i++)
{
e=frm.elements[i];
if ( e.type=='checkbox' && e.name.indexOf("checkAll") != -1 )
{
e.checked= false ;
break;
}
}
}
</script>
</form>
</body>
</html>
|
Conclusion
In this article we saw how to enable client-side JavaScript
interaction with CheckBox controls nested within a DataGrid. I hope
this article will help you open a few more ideas with the DataGrid
control.

