I have seen many questions in the forums that ask the same or a very similar
question. How do I put a check box, text box, etc.. in every row of my
DataGrid
and then update the values? The answer is rather simple and in this
article I will show you how this is accomplished.
We all know that the DataGrid can be a very powerful tool. Most of
the time, 90%+ in my experience, the
DataGrid
is used to display data and possibly edit one row of data at a time.
There are circumstances that arise that may call for the need to edit multiple,
even all, rows at one time. A good example of this is in a shopping cart
application where the customer can update the quantity of one or more items in
their basket at once and click check boxes to remove unwanted products.
The Scenario
In this example I have written a simple WebForm to manage a list of
contacts which I have stored in XML. The requirements are simply
the ability to add new contacts and to be able to modify or delete existing
contacts. The user should be able to modify or delete multiple contacts
at one time. I have also allowed the user to sort their grid by the
column of their choosing.
My example is written in C#. If you prefer the VB version of this code,
both version are available in the download.
Contacts.xml
The XML data file for this example is fairly simple and straight forward.
Due to the simplicity, I have not created a schema.
<?xml version="1.0" standalone="yes"?>
<Contacts>
<Contact>
<Email>myaddress@mycompany.com</Email>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
</Contact>
<Contact>
<Email>youraddress@yourcompany.com</Email>
<FirstName>Jane</FirstName>
<LastName>Doe</LastName>
</Contact>
</Contacts>
ContactList.aspx
Setting up the WebForm is simple as well. I placed a new
DataGrid
onto my form and gave it four columns, one for each field plus one to contain
the
CheckBox
for deleting a contact. You will notice that the main thing I did here is
created each column as a TemplateColumn. This allowed me to put the
TextBox
and
CheckBox
objects into the
ItemTemplate. This is the trick to
displaying something other than text in each row of the grid. In addition
you'll notice that I've used the
FooterTemplate
to make the addition of new contacts easy and (hopefully!) intuitive.
I've also included a single
LinkButton
which is used to save user modifications and deletions. It is not,
however, used to add new contacts. That is done by the link button in the
FooterTemplate
of the last column.
<asp:datagrid id="dgContacts" runat="server"
ShowFooter="True" AllowSorting="True" Forefont color="Black" GridLines="None"
CellPadding="2" Backfont color="LightGoldenrodYellow" BorderWidth="1px"
Borderfont color="Tan" Width="499px" AutoGenerateColumns="False"
DataKeyField="Email">
<SelectedItemStyle Forefont color="GhostWhite" Backfont
color="DarkSlateBlue"></SelectedItemStyle>
<AlternatingItemStyle Backfont
color="PaleGoldenrod"></AlternatingItemStyle>
<HeaderStyle Font-Bold="True" Backfont
color="Tan"></HeaderStyle>
<FooterStyle Backfont color="Tan"></FooterStyle>
<Columns>
<asp:TemplateColumn SortExpression="FirstName"
HeaderText="First Name">
<ItemTemplate>
<asp:TextBox id=First
runat="server" Width="109px" Text='<%# DataBinder.Eval(Container,
"DataItem.FirstName") %>'>
</asp:TextBox>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox id="NewFirst"
runat="server" Width="109px"></asp:TextBox>
</FooterTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn SortExpression="LastName"
HeaderText="Last Name">
<ItemTemplate>
<asp:TextBox id=Last
runat="server" Width="109px" Text='<%# DataBinder.Eval(Container,
"DataItem.LastName") %>'>
</asp:TextBox>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox id="NewLast"
runat="server" Width="109px"></asp:TextBox>
</FooterTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn SortExpression="Email"
HeaderText="Email">
<ItemTemplate>
<asp:TextBox id=Email
runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.Email")
%>'>
</asp:TextBox>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox id="NewEmail"
runat="server"></asp:TextBox>
</FooterTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Delete Contact">
<ItemStyle
HorizontalAlign="Center"></ItemStyle>
<ItemTemplate>
<asp:CheckBox Runat="server"
ID="chkDelete"></asp:CheckBox>
</ItemTemplate>
<FooterStyle
HorizontalAlign="Center"></FooterStyle>
<FooterTemplate>
<asp:LinkButton Runat="server"
Text="Add" CommandName="Add" ID="Linkbutton1"
NAME="Linkbutton1"></asp:LinkButton>
</FooterTemplate>
</asp:TemplateColumn>
</Columns>
</asp:datagrid>

ContactList.cs
Since I elected to store my data in an XML file, I have decided to use a
DataSet
for accessing it. Since the
DataSet
object has the
ReadXml
and
WriteXml
methods it was a fairly logical choice. My first step was to read in
the XML. As you can see from the code, I also added a the foundation to
handle sorting.
private DataSet _dsContacts;
private string _sSort;
private void Page_Load(object
sender, System.EventArgs e)
{
// Load the XML file.
_dsContacts = new DataSet();
_dsContacts.ReadXml(Server.MapPath("Contacts.xml"));
DataColumn[] dcPk = {_dsContacts.Tables["Contact"].Columns["Email"]};
_dsContacts.Tables["Contact"].PrimaryKey =
dcPk;
if (!Page.IsPostBack )
{
// Only bind at this point if this is the
first page request.
BindContacts();
_sSort = "FirstName";
}
else
{
// Read the sort order from the view
state.
_sSort = (string)ViewState["Sort"];
}
}
Next I created the method used to bind the data to the grid, which includes the
logic for sorting the data and a method to persist the XML back to the disk.
private void BindContacts()
{
// Save the sort order to the view state.
ViewState["Sort"] = _sSort;
// Bind the grid to the sorted data view.
DataView dv =
new
DataView(_dsContacts.Tables["Contact"]);
dv.Sort = _sSort;
dgContacts.DataSource = dv;
dgContacts.DataBind();
}
private void SaveContacts()
{
_dsContacts.WriteXml(Server.MapPath("Contacts.xml"));
}
The
ItemCommand
event is where new contacts are added to the list. Note that I checked
the
CommandName
argument for a value of Add. This is the value that was
set back in the ASPX page for the LinkButton
in the
FooterTemplate
of the last column in the grid.
private void
dgContacts_ItemCommand(object source ,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
// Add the new item to the dataset. I use an
array here for efficiency.
if (e.CommandName ==
"Add")
{
string[] sContact =
{"", "", ""};
sContact[0] =
((TextBox)e.Item.FindControl("NewEmail")).Text;
sContact[1] =
((TextBox)e.Item.FindControl("NewFirst")).Text;
sContact[2] = ((TextBox)e.Item.FindControl("NewLast")).Text;
_dsContacts.Tables["Contact"].Rows.Add(sContact);
SaveContacts();
}
BindContacts();
}
I will skip the
SortCommand
code since there are a lot of other articles out there that deal with sorting
in much more detail. If you download the code for this example it is
included.
Finally I moved on to the
OnClick
event of the
LinkButton
on the form. This is where I looped through the items in the
DataGrid
to perform any necessary deletes and updates.
private void btnUpdate_Click(object sender,
System.EventArgs e)
{
// Loop through the items in the
datagrid.
foreach (DataGridItem di in
dgContacts.Items)
{
// Make sure this is an item and not the header or
footer.
if (di.ItemType ==
ListItemType.Item || di.ItemType ==
ListItemType.AlternatingItem)
{
// Get the current row for update or delete operations
later.
DataRow dr = _dsContacts.Tables["Contact"].Rows.Find(dgContacts.DataKeys[di.ItemIndex]);
// See if this one needs to be deleted.
if (((CheckBox)di.FindControl("chkDelete")).Checked)
{
_dsContacts.Tables["Contact"].Rows.Remove(dr);
}
else
{
// Update the row instead.
dr["Email"] = ((TextBox)di.FindControl("Email")).Text;
dr["FirstName"] = ((TextBox)di.FindControl("First")).Text;
dr["LastName"] = ((TextBox)di.FindControl("Last")).Text;
}
}
}
// Save the changes if there are any.
if
(_dsContacts.HasChanges())
{
SaveContacts();
}
BindContacts();
}
Conclusion
I could have just as easily referenced the controls by their positions within
the the Cells(x) of each DataGridItem. The point is, there's more than
one way to skin a cat and I'm sure that you can find some better ways to
accomplish this task. As you can see it's quite easy to edit entire
data grids at one time. This same approach will also work with paged
grids with a little modification.