How to Make a Fully Editable GridView in ASP.NET 2
This article describes how I made a fully editable gridview in
C# using ASP.NET 2. The idea is that the GridView looks and works
like an Excel spreadsheet. You see all the cells in the table, and
you can edit any of the cells you like, and they are automatically
updated (i.e. saved to the database). This is very often how people
expect data tables to work in web pages, and I've often seen people
clicking in vain on un-editable gridview cells, somehow expecting
to edit them, and not understanding that they need to click an Edit
button at the end of the row.
How to make a fully editable gridview
Here is how to make it:
- Make a datasource and link it to your database table. I used
an AccessDataSource in this example, but a SqlDataSource will
work just as well.

- Make a GridView and assign its datasource to be the datasource
you just made.
- Edit each of the columns of the GridView and turn them all into
Template Fields.
- Now got to Edit Templates and edit each template field in turn.
Go to the ItemTemplate for each column. You will see that the
GridView has put a label into each ItemTemplate. This is the label
that displays the cell data for the grid. Delete the Label and
put a TextBox in there instead. Edit the DataBindings of the textbox
and set the Field Binding of the Text property to be the database
table field that this column corresponds to.

By this point you will have a fully editable gridview with just
one problem... None of the data that you edit will be saved to the
database. Our challenge is to make it so that the GridView looks
for changed data and saves it when you:
- Move from one field to another field
- Click a big friendly "Save" button at the bottom of
the table
- Go to the next page in the table if the data is paged
I tackled these factors like this:
- I ignore what happens when people move from one field to another.
I only save the data when they click Save or when they move to
another page.
- I Created a "Save" button below the GirdView that
does a postback when you click it. I made it so that doing a postback
saves all the data.
- I found that moving to another page causes a postback, and so
this will trigger my save routine too.
This is how it all works:
First, I edited my source code to get rid of unwanted TextBoxes
that are automatically put in there by the GUI. There is no need
for EditItem TextBoxes, as our GridView never goes into edit mode.
I also put in a HiddenField in the first column of my GridView
and bound this to the Primary Key of the table (in my case "ID").
I did this so that I can find the primary key of the row that needs
saving when I get to my save routine.
I also changed the border style of the TextBoxes so that they don't
have borders. I think this makes things less cluttered and look
more like an excel spreadsheet.
The final source code for the GridView ended up like this:
<asp:GridView ID="GridView1" runat="server"
AllowPaging="True" AutoGenerateColumns="False"
BackColor="White" BorderColor="#CCCCCC" BorderStyle="None"
BorderWidth="1px"
CellPadding="3" DataKeyNames="id" DataSourceID="AccessDataSource1">
<FooterStyle BackColor="White" ForeColor="#000066"
/>
<Columns>
<asp:BoundField DataField="id" HeaderText="id"
InsertVisible="False" ReadOnly="True"
SortExpression="id" />
<asp:TemplateField HeaderText="project_number" SortExpression="project_number">
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Bind("project_number") %>'
OnTextChanged="TextBox_TextChanged" BorderStyle="None"></asp:TextBox>
<asp:HiddenField ID="HiddenField1" runat="server"
Value='<%# Eval("id") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="description" SortExpression="description">
<ItemTemplate>
<asp:TextBox ID="TextBox2" runat="server"
Text='<%# Bind("description") %>'
OnTextChanged="TextBox_TextChanged" BorderStyle="None"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="manager" SortExpression="manager">
<ItemTemplate>
<asp:TextBox ID="TextBox3" runat="server"
Text='<%# Bind("manager") %>'
OnTextChanged="TextBox_TextChanged" BorderStyle="None"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<RowStyle ForeColor="#000066" />
<SelectedRowStyle BackColor="#669999" Font-Bold="True"
ForeColor="White" />
<PagerStyle BackColor="White" ForeColor="#000066"
HorizontalAlign="Left" />
<HeaderStyle BackColor="#006699" Font-Bold="True"
ForeColor="White" />
</asp:GridView>
As you can see, I've made a TextChanged function, and assigned
each TextBox to have its OnTextChanged event point to this function.
The idea here is that every time a TextBox value is changed, the
TextChanged event fires, and we know that a certain cell has been
edited. What we actually do is this:
- We have an array of booleans values called rowChanged.
- In Page_Load we define the size of this array to match the number
of rows in the GridView, and assign false to each element in the
array.
- Each time a TextBox_TextChanged event fires, we work out which
row the event fired from and mark the corresponding value in rowChanged
to be true.
- In Page_PreRender (one of the last events to fire before a page
is actually drawn) we look through the rowChanged array for all
true values. For each row that we now know has changed, we find
the new values in the TextBoxes for that row. We get the primary
key value from the HiddenField, and all the cell values from the
TextBox.Text values. Amazingly, during Page_PreRender, the textboxes
have the changed values that you put in them from before the PostBack,
probably thanks to the ViewState of the GridView. Once we have
the values, we have enough info to be able to construct an SQL
statement to update the row with the new data.
- Because clicking the Save button causes a postback, and also
moving to the next page of a GridView causes a postback, the data
automatically gets saved when you do either of these operations.
The bug is that data does not get saved if you click Back in your
browser.
Here is the source code:
public partial class fully_editable_gridview : System.Web.UI.Page
{
bool [] rowChanged;
protected void Page_Load(object sender, EventArgs e)
{
int totalRows = GridView1.Rows.Count;
rowChanged = new bool[totalRows];
if (Page.IsPostBack)
{
// Do Page load code...
}
}
protected void TextBox_TextChanged(object sender, EventArgs
e)
{
TextBox thisTextBox = (TextBox)sender;
GridViewRow thisGridViewRow = (GridViewRow)thisTextBox.Parent.Parent;
int row = thisGridViewRow.RowIndex;
rowChanged[row] = true;
}
protected void Page_PreRender(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
int totalRows = GridView1.Rows.Count;
for (int r = 0; r < totalRows; r++)
{
if (rowChanged[r])
{
GridViewRow thisGridViewRow = GridView1.Rows[r];
HiddenField hf1 = (HiddenField)thisGridViewRow.FindControl("HiddenField1");
string pk = hf1.Value;
TextBox tb1 = (TextBox)thisGridViewRow.FindControl("TextBox1");
string cell1 = tb1.Text;
TextBox tb2 = (TextBox)thisGridViewRow.FindControl("TextBox2");
string cell2 = tb2.Text;
TextBox tb3 = (TextBox)thisGridViewRow.FindControl("TextBox3");
string cell3 = tb3.Text;
// We know the Primary Key and data values for this gridview row,
so save this row to the database...
OleDbConnection con = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=P:\PROJECTMANAGEMENT\asp\oldprojects.mdb");
con.Open();
string sql = "update project set project_number='" + cell1
+ "', description='"+cell2+"', manager='"+cell3+"'
where id="+pk;
OleDbCommand sqlcommand = new OleDbCommand(sql, con);
sqlcommand.ExecuteNonQuery();
con.Close();
}
}
GridView1.DataBind();
}
}
}
All in all I am quite happy with this first attempt at a fully
editable GridView. My next modifications to it are to make it look
a little more like a spreadsheet, and to experiment with different
types of controls such as dropdown lists in the cells instead of
just textboxes. I'll keep you posted.
Last updated 8 November 2006
|