I've seen many same or similar questions in forums: How do I place checkboxes, textboxes, etc. in each row of my DataGrid? How to update their values? The answer is pretty simple, and in this article I'll show you how to do it.
We all know that DataGrid is a very powerful tool. In my experience, more than 90% of the time, the DataGrid is used to display data and possibly edit it one row at a time. Sometimes, you may need to edit multiple rows or even all data at once. A practical example would be in an application that sells items online, where customers might want to change one or more items in their basket at a time, clicking on checkboxes to remove the items they don't want.
Concept
In this example, I wrote a simple WebForm to manage a contact list stored in XML. The requirement is very simple: have the ability to add new contacts, edit/delete existing contacts. Users can modify or delete multiple contacts at once, and I also allow users to sort the data grid by columns of their choosing.
My examples are written in C#. If you prefer the VB version of these codes, the code is available in both formats in the download file.
The XML data file in the
Contacts.xml
example is very simple and intuitive.Since it's very simple, I didn't create a plan.
<?xml version="1.0" standalone="yes"?>
<Contacts>
<Contact>
<Email>[email protected]</Email>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
</Contact>
<Contact>
<Email>[email protected]</Email>
<FirstName>Jane</FirstName>
<LastName>Doe</LastName>
</Contact>
</Contacts>
ContactList.aspx
Setting up a WebForm is very simple. I placed a new DataGrid into my form and set it up to have 4 columns, with the first column containing checkboxes for deleting contacts. You'll notice that the main thing I did here is create each column as a template column (TemplateColumn). This allows me to place textbox and checkbox objects into the data template (ItemTemplate). This is a trick to display something other than text in each row of the grid. Additionally, you'll notice that I use a FooterTemplate to make creating new contacts easy and intuitive.
I also included a LinkButton to save modifications and deletions made by the user. But it is not used to add new contacts. The operation of adding new contacts is completed by the link button (LinkButton) in the footer template 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
After I chose to use an XML file to access the data, I decided to use a DataSet to access it. Because the DataSet object has ReadXml and WriteXml methods, this is a very reasonable choice. The first step is to read the data in XML. As you can see from the code, I created a member to handle sorting the data.
private DataSet _dsContacts;
private string _sSort;
private void Page_Load(object sender, System.EventArgs e)
{
//Load 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 )
{
// If it is loaded for the first time, bind data.
BindContacts();
_sSort = "FirstName";
}
else
{
// Otherwise, read the sorting state from the view state.
_sSort = (string)ViewState["Sort"];
}
}
In the second step, I created a method for binding data to the grid, which included data sorting logic and methods for reading data from disk.
private void BindContacts()
{
//Save sorting state to 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 used to handle adding new contacts to the list. Note: I checked whether the CommandName parameter is Add. It is to handle the return value of the link button (LinkButton) in the footer template (FooterTemplate) of the last column of the grid in the ASPX page.
private void dgContacts_ItemCommand(object source , System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
//Add new data to the dataset. Here I use an array to improve processing 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 skipped the SortCommand code because there are many other documents that discuss sorting in great detail. If you download the source code for this example, it's included.
Finally, I moved the click event (onClick) of the link button (LinkButton) on the form here. Here I loop through the data items in the DataGrid to perform any necessary delete and update operations.
private void btnUpdate_Click(object sender, System.EventArgs e)
{
// Loop through each data item.
foreach (DataGridItem in dgContacts.Items)
{
// Make sure it's a data item and not the top or bottom of the page.
if (di.ItemType == ListItemType.Item || di.ItemType == ListItemType.AlternatingItem)
{
// Get the current row after the update or delete operation is performed.
DataRow dr = _dsContacts.Tables["Contact"].Rows.Find(dgContacts.DataKeys[di.ItemIndex]);
// Check whether a row needs to be deleted.
if (((CheckBox)di.FindControl("chkDelete")).Checked)
{
_dsContacts.Tables["Contact"].Rows.Remove(dr);//Delete the specified row
}
else
{
//Update data row.
dr["Email"] = ((TextBox)di.FindControl("Email")).Text;
dr["FirstName"] = ((TextBox)di.FindControl("First")).Text;
dr["LastName"] = ((TextBox)di.FindControl("Last")).Text;
}
}
}
//Save it if there are changes.
if (_dsContacts.HasChanges())
{
SaveContacts();
}
BindContacts();//Binding
}
Conclusion
I can easily find the cells (Cells) of each DataGridItem in the control through the position of the control. There are multiple ways to accomplish this, and I'm sure you can find a better way to accomplish this task. As you can see, editing the entire data grid at once is very simple. The same method can also be used for paginated grids with slight modifications