I am trying to create rows with dynamic controls on button click in ASP.NET
GridView. The adding part is done, but stuck with the deletion of rows created. Done this for adding more rows:
ASP.NET:
<asp:GridView ID="Gridview1" runat="server" ShowFooter="true" AutoGenerateColumns="false" OnRowDataBound="OnRowDataBound" OnRowDeleting="OnRowDeleting">
<Columns>
<asp:BoundField DataField="RowNumber" HeaderText="Row Number" />
<asp:TemplateField HeaderText="Header 1">
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Header 2">
<ItemTemplate>
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Header 3">
<ItemTemplate>
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
</ItemTemplate>
<FooterStyle HorizontalAlign="Right" />
<FooterTemplate>
<asp:Button ID="ButtonAdd" runat="server" Text="Add New Row" OnClick="ButtonAdd_Click" />
</FooterTemplate>
</asp:TemplateField>
<asp:CommandField ShowDeleteButton="True" ButtonType="Button" />
</Columns>
</asp:GridView>
C#:
int count = 1;
private void SetInitialRow()
{
DataTable dt = new DataTable();
DataRow dr = null;
dt.Columns.Add(new DataColumn("RowNumber", typeof(string)));
dt.Columns.Add(new DataColumn("Column1", typeof(string)));
dt.Columns.Add(new DataColumn("Column2", typeof(string)));
dt.Columns.Add(new DataColumn("Column3", typeof(string)));
dr = dt.NewRow();
dr["RowNumber"] = 1;
dr["Column1"] = string.Empty;
dr["Column2"] = string.Empty;
dr["Column3"] = string.Empty;
dt.Rows.Add(dr);
ViewState["CurrentTable"] = dt;
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
private void AddNewRowToGrid()
{
int rowIndex = 0;
if (Session["count"] != null)
count = (int)Session["count"];
count ;
Session["count"] = count;
if (ViewState["CurrentTable"] != null)
{
DataTable dtCurrentTable = (DataTable)ViewState["CurrentTable"];
DataRow drCurrentRow = null;
if (dtCurrentTable.Rows.Count > 0)
{
for (int i = 1; i <= dtCurrentTable.Rows.Count; i )
{
Label label1 = (Label)Gridview1.Rows[rowIndex].Cells[0].FindControl("Label1");
TextBox box1 = (TextBox)Gridview1.Rows[rowIndex].Cells[1].FindControl("TextBox1");
TextBox box2 = (TextBox)Gridview1.Rows[rowIndex].Cells[2].FindControl("TextBox2");
TextBox box3 = (TextBox)Gridview1.Rows[rowIndex].Cells[3].FindControl("TextBox3");
drCurrentRow = dtCurrentTable.NewRow();
drCurrentRow["RowNumber"] = count ;
dtCurrentTable.Rows[i - 1]["Column1"] = box1.Text;
dtCurrentTable.Rows[i - 1]["Column2"] = box2.Text;
dtCurrentTable.Rows[i - 1]["Column3"] = box3.Text;
rowIndex ;
}
dtCurrentTable.Rows.Add(drCurrentRow);
ViewState["CurrentTable"] = dtCurrentTable;
Gridview1.DataSource = dtCurrentTable;
Gridview1.DataBind();
}
}
else
{
Response.Write("ViewState is null");
}
SetPreviousData();
}
private void SetPreviousData()
{
int rowIndex = 0;
if (ViewState["CurrentTable"] != null)
{
DataTable dt = (DataTable)ViewState["CurrentTable"];
if (dt.Rows.Count > 0)
{
for (int i = 0; i < dt.Rows.Count; i )
{
TextBox box1 = (TextBox)Gridview1.Rows[rowIndex].Cells[1].FindControl("TextBox1");
TextBox box2 = (TextBox)Gridview1.Rows[rowIndex].Cells[2].FindControl("TextBox2");
TextBox box3 = (TextBox)Gridview1.Rows[rowIndex].Cells[3].FindControl("TextBox3");
box1.Text = dt.Rows[i]["Column1"].ToString();
box2.Text = dt.Rows[i]["Column2"].ToString();
box3.Text = dt.Rows[i]["Column3"].ToString();
rowIndex ;
}
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
SetInitialRow();
}
}
protected void ButtonAdd_Click(object sender, EventArgs e)
{
AddNewRowToGrid();
}
For deleting rows, tried the below code but seems like it's having some kind of row index mismatch and doesn't delete associated row on button click:
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string item = e.Row.Cells[0].Text;
foreach (Button button in e.Row.Cells[2].Controls.OfType<Button>())
{
if (button.CommandName == "Delete")
{
button.Attributes["onclick"] = "if(!confirm('Do you want to delete " item "?')){ return false; };";
}
}
}
}
protected void OnRowDeleting(object sender, GridViewDeleteEventArgs e)
{
if (ViewState["CurrentTable"] != null)
{
DataTable dtCurrentTable = (DataTable)ViewState["CurrentTable"];
if (dtCurrentTable.Rows.Count > 0)
{
int rowIndex = Convert.ToInt32(e.RowIndex);
dtCurrentTable.Rows[rowIndex].Delete();
for (int i = 1; i <= dtCurrentTable.Rows.Count; i )
{
TextBox box1 = (TextBox)Gridview1.Rows[rowIndex].Cells[1].FindControl("TextBox1");
TextBox box2 = (TextBox)Gridview1.Rows[rowIndex].Cells[2].FindControl("TextBox2");
TextBox box3 = (TextBox)Gridview1.Rows[rowIndex].Cells[3].FindControl("TextBox3");
dtCurrentTable.Rows[i - 1]["Column1"] = box1.Text;
dtCurrentTable.Rows[i - 1]["Column2"] = box2.Text;
dtCurrentTable.Rows[i - 1]["Column3"] = box3.Text;
rowIndex--;
}
ViewState["CurrentTable"] = dtCurrentTable;
Gridview1.DataSource = dtCurrentTable;
Gridview1.DataBind();
}
SetPreviousData();
}
}
Is there anything that I am doing wrong with the delete part?
CodePudding user response:
Not at all clear why you have all that extra code in add row????
If you want to default values, why not take them from the persisted datatable, and why bother with the grid??? - I don't think you need all that code.
Now, to make this really work? You do want a extra routine - Grid to table.
but, lets leave that for now.
2nd, note that because data table is a object, once you point the object to viewstate, then changes ALSO point back to view state (you don't have to re-save back into viewstate. With a string, or int counter - yes you do, but not with objects (since then you only really working with a pointer to the given object).
Also, don't bother with the built in GV delete button - it really is more pain then it is worth. Just drop in a button into the GV, and wrire up a click event. That way you don't have to mess with the row data bound event to shove in the client side confirm you have.
Also, for most grids, you don't need to show/display/include/have the row ID. In fact for reasons of secuirty, in most cases I don't display the database PK row id (and you don't have to).
And one more FYI: When you start to add a LOT of custom controls to a grid view? Well, it always painful that each custom control needs that template tags. And with quite a few controls, that becomes really messy fast. So, I suggest a ListView, as each custom control dropped into the ListView does not requite those template tags. But, lets leave this tip for next around around.
Also, MANY do not realize that you can send a whole datatalbe BACK to the database if the datatable came from a database, and can execute this save back WITH ONE command that will automatic write back all updates, inserts and even deletes. Of course in your example, we just playing and building a 100% in-memory data table, but this whole setup works near the same if/when the datatable was from a database.
So, I would dump the built in delete button.
I would dump the row data bound code.
I also would consider moving the add button OUT of the GV. You can turn on "ShowHeaderWhenEmpty" = true. that will THEN display the header - even without data. But without data, the footer DOES NOT show, and thus if you delete the one row, you be unable to add that row.
So, our markup is this:
NOTE close, I did not want to write code to load up each row - we don't have to, so note how I added the Text = expression. Again, less code.
<asp:GridView ID="Gridview1" runat="server" ShowFooter="true"
AutoGenerateColumns="false" CssClass="table"
ShowHeaderWhenEmpty="true" Datakeys="RowNumber" >
<Columns>
<asp:BoundField DataField="RowNumber" HeaderText="Row Number" />
<asp:TemplateField HeaderText="Header 1">
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Eval("Column1") %>' ></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Header 2">
<ItemTemplate>
<asp:TextBox ID="TextBox2" runat="server" Text='<%# Eval("Column2") %>' ></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Header 3">
<ItemTemplate>
<asp:TextBox ID="TextBox3" runat="server" Text='<%# Eval("Column3") %>' ></asp:TextBox>
</ItemTemplate>
<FooterStyle HorizontalAlign="Right" />
<FooterTemplate>
<asp:Button ID="ButtonAdd" runat="server" Text="Add New Row"
OnClick="ButtonAdd_Click" css/>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Delete">
<ItemTemplate>
<asp:Button ID="cmdDelete" runat="server" Text="Delete" CssClass="btn"
OnClick="cmdDelete_Click"
OnClientClick = '<%# "return confirm(\"Really delete this row "
Container.DisplayIndex " ("
Eval("RowNumber") ")" "\");" %>'
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And HOW you start the page up? Beyond important!!!
So, we have this code:
DataTable dt = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
SetInitialRow();
else
dt = ViewState["CurrentTable"] as DataTable;
}
private void SetInitialRow()
{
DataRow dr = null;
dt.Columns.Add(new DataColumn("RowNumber", typeof(int)));
dt.Columns[0].AutoIncrement = true;
//Set the Starting or Seed value.
dt.Columns[0].AutoIncrementSeed = 1;
//Set the Increment value.
dt.Columns[0].AutoIncrementStep = 1;
dt.Columns.Add(new DataColumn("Column1", typeof(string)));
dt.Columns.Add(new DataColumn("Column2", typeof(string)));
dt.Columns.Add(new DataColumn("Column3", typeof(string)));
dr = dt.NewRow();
dr["RowNumber"] = 1;
dr["Column1"] = string.Empty;
dr["Column2"] = string.Empty;
dr["Column3"] = string.Empty;
dt.Rows.Add(dr);
ViewState["CurrentTable"] = dt;
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
Note how I moved the table to class level.
Now code to add is this:
protected void ButtonAdd_Click(object sender, EventArgs e)
{
GridToTable();
DataRow MyNewRow = dt.NewRow();
dt.Rows.Add(MyNewRow);
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
And code to delete is this:
protected void cmdDelete_Click(object sender, EventArgs e)
{
GridToTable();
Button cmdDel = sender as Button;
GridViewRow gRow = cmdDel.NamingContainer as GridViewRow;
// get row to delete
int RowDel = gRow.RowIndex;
dt.Rows[RowDel].Delete();
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
So, as noted, above quite much makes this work. As noted, if we are to allow editing of the data (assume yes), then we need one more routine that moves grid back to data.
So, when you hit a save button, we call GridToTable.
So, when you hit a add button, we call Grid to Table.
So, when you hit delete button, we call Grid to table.
In other words, we REALLY need to save all edits BACK to the table before we add, or delete or even want to save the table to a database.
So, we need this routine:
void GridToTable()
{
// send current grid back to table.
foreach (GridViewRow gRow in Gridview1.Rows)
{
DataRow OneRow = dt.Rows[gRow.RowIndex];
OneRow["Column1"] = (gRow.FindControl("TextBox1") as TextBox).Text;
OneRow["Column2"] = (gRow.FindControl("TextBox2") as TextBox).Text;
OneRow["Column3"] = (gRow.FindControl("TextBox3") as TextBox).Text;
}
}