Creating a Fully Editable DataGrid
By Joel Gray
Published: 4/18/2003
Reader Level: Beginner
Rated: 4.12 by 26 member(s).
Tell a Friend
Rate this Article
Printable Version
Discuss in the Forums

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.



Marketplace
(Sponsored Links)
What are the green links?
   



 
Copyright © 2007 CMP Tech LLC |
Privacy Policy (4/10/06) | Your California Privacy Rights (4/10/06) | Terms of Service | Advertising Info | About Us | Help