# VBForums CodeBank > CodeBank - ASP / ASP.NET / MVC / Web API >  GridView with integraded Delete confirmation and insert row

## timeshifter

I've longed for something like this for a long time... several pages I've written could have benefited greatly from this, but only one actually got it. It's wonderfully easy to use on the client side, which is every web developer's goal (usually  :Alien Frog: ), and while the code isn't exactly piss-easy, it's not that complex either. So, let's take this apart and see how it's done.

First, we need the GridView. I do my data binding in the codebehind, and I'd suggest the same for you if you're using this setup. The DataSource object is just too much hassle for the little benefit it provides. I'm just going to throw all the event handlers in the opening tag for sake of saving space here... but the key thing to remember is that we are adding extra stuff. Everything is in template columns because of it, except for the Edit button. That can actually be handled through the autogenerated Edit button, so we'll use that.


html Code:
<asp:GridView ID="gvPosts" runat="server" AutoGenerateColumns="False" OnRowCancelingEdit="gvPosts_RowCancelingEdit" OnRowEditing="gvPosts_RowEditing" OnRowUpdating="gvPosts_RowUpdating" AutoGenerateEditButton="true" OnRowDataBound="gvPosts_RowDataBound" ShowFooter="true" OnRowCommand="gvPosts_RowCommand">
As you can see here, we define the RowEditing and RowCancellingEdit commands, as they are handled by the AutoGenerated Edit button. Let's finish off the GridView itself before I get too absorbed in the codebehind.


html Code:
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="lbDelete" runat="server" CommandName="Delete1" Text="Delete"></asp:LinkButton>
<asp:Label ID="lblDeleteConfirmation" runat="server" Text="Are you sure?<br />" Visible="false" Width="100px"></asp:Label>
<asp:LinkButton ID="lbDeleteConfirm" runat="server" CommandName="DeleteConfirm" Text="-Confirm-  " Visible="false"></asp:LinkButton>
<asp:LinkButton ID="lbDeleteCancel" runat="server" CommandName="DeleteCancel" Text="-Cancel-" Visible="false"></asp:LinkButton>
</ItemTemplate>
<FooterTemplate>
<asp:LinkButton ID="lbAdd" runat="server" CommandName="Insert1" Text="Add"></asp:LinkButton>
</FooterTemplate>
</asp:TemplateField>
This is the first column. As you can see, for the ItemTemplate, we have the Delete button and confirmation defined, and in the FooterTemplate, we have an Add button. The Footer row will be the Insert row when it's all done. More on that later. Notice here we have the confirm/cancel buttons hidden, along with the text label. You can customize that to say whatever you want, but I've found that short and sweet tends to be the easiest.

The rest of the columns should be very similar in nature to each other, and should look something like the following:

html Code:
<asp:TemplateField HeaderText="Question">
<ItemTemplate>
<%#Eval("Question") %>
</ItemTemplate>
<EditItemTemplate>
<span style="display:none;"><asp:Label ID="lblID" runat="server" Text='<%#Eval("PostID").ToString() %>'></asp:Label></span>
<asp:TextBox ID="txtQuestion" runat="server" Text='<%#Eval("Question").ToString() %>'  TextMode="multiline" Rows="3"></asp:TextBox>
</EditItemTemplate>
<FooterTemplate>
<asp:TextBox ID="txtNewQuestion" runat="server"  TextMode="multiLine" Rows="3"></asp:TextBox>
</FooterTemplate>
</asp:TemplateField>
Yes, there's a hidden label in the EditItem template. That stores the PrimaryKey value from the database so the code knows what it's editing. Nothing unusual about the rest of it... ItemTemplate is read-only, EditItemTemplate is for editing, and FooterTemplate is for inserting. Any data columns should be laid out based on this. And that rather does it for the HTML side of it. Now for the code.

You may have noticed that the RowDataBound event is defined and handled. Why, you ask? Fun little issue I have yet to find a better method of. Remember our Delete controls? They need to have the index of the row they're in bound to them on the fly. That code is rather simple, though:


csharp Code:
protected void gvPosts_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if(e.Row.RowType== DataControlRowType.DataRow)
            {
                ((LinkButton)e.Row.FindControl("lbDelete")).CommandArgument=e.Row.RowIndex.ToString();
                ((LinkButton)e.Row.FindControl("lbDeleteConfirm")).CommandArgument=e.Row.RowIndex.ToString();
                ((LinkButton)e.Row.FindControl("lbDeleteCancel")).CommandArgument=e.Row.RowIndex.ToString();
            }
        }
All it does is find the controls and specify their CommandArguement to match the index of their row. No mess there.

Next are the RowEditing and RowCancellingEdit events. Since we aren't binding to a datasource object, the default commands won't get processed correctly. Two events, again, no mess.

csharp Code:
protected void gvPosts_RowEditing(object sender, GridViewEditEventArgs e)
        {
            gvPosts.EditIndex = e.NewEditIndex;
            UpdatePosts();
        }
         protected void gvPosts_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
        {
            gvPosts.EditIndex = -1;
            UpdatePosts();
        }
In this event, UpdatePosts() is the method that binds the gridview. I do a check in that method to see if the binding source has any rows... if not, I set AutoGenerateEditButton to false before binding the grid, which enables the row to completely hide and only display the insert row.

The RowUpdating event is nothing special... just pulls data out of the editing row and updates it. For me, that's through a stored procedure. Clean and simple. Just have to remember to set the gridview's EditIndex to -1, THEN rebind it.

Which leaves only RowCommand, which is also the meatiest one on the list. Inside the event is a series of conditionals, which will also explain why I'm setting the CommandArguement the way I do. My first conditional: checking to see if the user pressed Delete.


csharp Code:
if (e.CommandName == "Delete1")
            {
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDeleteConfirm")).Visible = true;
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDeleteCancel")).Visible = true;
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDelete")).Visible = false;
                ((Label)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lblDeleteConfirmation")).Visible = true;
            }
Once again, nothing messy. Just find the controls for the Delete system, change their Visible status, and let the PostBack take care of the rest. The DeleteCancel event is almost the same:

csharp Code:
else if (e.CommandName == "DeleteCancel")
            {
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDeleteConfirm")).Visible = false;
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDeleteCancel")).Visible = false;
                ((LinkButton)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lbDelete")).Visible = true;
                ((Label)gvPosts.Rows[int.Parse(e.CommandArgument.ToString())].FindControl("lblDeleteConfirmation")).Visible = false;
                UpdatePosts();
            }

The DeleteConfirm will basically do what DeleteCancel does, but it will also execute a database command to delete where the ID row equals the contents of that hidden ID label that's in the EditTemplate, then run the UpdatePosts() method again to rebind the data.

The Insert row shouldn't be hard to figure out here. Just pull the data out of the controls in the Footer row, run whatever command you need to, clear the text, and rebind the grid. And that's the end of the real contents of this post.

I certainly hope this helps someone else as much as it's helped me. There's quite a bit of customization that can be done with it, simply because so little is handled automatically. Template styles, custom visible statuses for whatever situation it's in, any field type you need. Pretty dynamic, but contained and intuitive enough for the average user to pick up on very quickly. Attached is a screenshot of this thing in action. Enjoy!  :wave:

----------

