[SOLVED] asp:GridView allow Edit button, but remove Update and Cancel buttons

Issue

This one is pretty straightforward. The asp:GridView automatically adds an Edit hyperlink to the left of every column like this:

enter image description here

which is fine because I need that functionality anyways. However, I’ve made it so clicking the Edit hyperlink brings up a new panel that allows the user to edit more properties than are shown in the GridView, so I don’t want the Update and Cancel buttons to appear after clicking Edit.

enter image description here

Ive added the Edit functionality by implementing an event handler that Handles GridView.RowCommand when e.CommandName = "Edit". I would like to just keep the Edit hyperlink there, or maybe programatically click Cancel, or really anything that will keep the Update and Cancel hyperlinks from showing when the user finishes working with the Edit panel. I’ve tried calling the event handler and sending in args with e.CommandName = "Cancel" but that didn’t work.

Another option I’m willing to explore is removing the Edit button entirely and making my event handler handle a different event, but I haven’t found any good resources for that either. Mostly it’s people trying to disable an Edit button that they’ve manually created on certain rows. I didn’t create this Edit hyperlink, though, and I can’t get it to go away.

Solution

Well, since you popping up a panel? Then just dump the whole template and auto editing system. Really, for quick and dirty – those built in GV events are great to get you up and running.

But, it turns out once you REALLY get the hang of how all this works? You can just pure code your way though this, and you even wind up with LESS code and LESS markup, and less hassle!!!

In fact, you can even dump the whole GV "row" handler – as it does not really again do you much of any favors here.

in fact, even better? Just drop in a plane jane edit button – even a asp.net one. You don’t really need anything much more! – and you wind up often with even LESS confusing code. And REALLY great? Well, each button you add to the GV has its own click event – is it own nice button click with its own button click – quite much like any other control or button you just drop into the GV.

In fact, if the queston was how to tab around and edit the gv like Excel, I can post a really short, easy solution, and NONE of it uses the built in events of the GV.

but, lets outline how this can work, and work with a "min" of code.

You don’t mention what kind of "dialog" you using here (and there are lot of them, ranging from jquery.UI (which I will use), or even the dialog from the ajaxtoolkit is also very nice.

So, I will say I do NOT like to suggest introduction of "just one more" JavaScript library. However, almost for sure, you have jQuery installed by default. So, adding jquery.UI is not all that big of deal.

So, our goals:

Dump the messy GV event model.
STILL use + write as much as possible easy server side code.
Write and use a absolute min of JavaScript code.

Now, I am going to use a few helper routines I wrote in vb.net. They are short, and easy to use. I became VERY fast tired of writing the same code over and over to:

Shuffle data from data table to some controls for edit
Shuffle data from controls back to table, and then save table to database.

However, even without the above "shuffle to/from" routines, you see that this code is MUCH less hassle by actually dumping the GV events.

Ok, So, lets assume a simple grid of hotels. And on each row, we want a edit button.

So, the first easy part, here is our grid:

    <div style="width:40%">
        <asp:GridView ID="GHotels" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="ID" CssClass="table">
            <Columns>
                <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
                <asp:BoundField DataField="LastName" HeaderText="LastName"    />
                <asp:BoundField DataField="HotelName" HeaderText="HotelName"  />
                <asp:BoundField DataField="Description" HeaderText="Description"  />
                <asp:TemplateField HeaderText="Edit">
                    <ItemTemplate>
                        <asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn" OnClick="cmdEdit_Click" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </div>

A few FYI: Yes, to save time, I did use the wizards to create above. I then blow out the datasource1, and data source setting of the gv. and I removed the "ID" from display for each row. NOTE VERY careful – the PK row "id" setting is now this:

DataKeyNames="ID"

This is great feature, since it will maintain, use, and allow you to easy get the PK row id, but NOT have to expose it in the markup – less code, better to never show the PK stuff to users.

And note how I set CssClass="table". Don’t worry about that, but doing so will bootstrap the GV – and it looks WAY WAY nicer. (and it also auto expands/grows to the div width size – do try this – since bootstrap is installed by default).

And now our code is this:
(don’t worry – I’ll include all code used here).

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not IsPostBack Then
        LoadGrid
    End If

End Sub

Sub LoadGrid()

    Dim cmdSQL As New SqlCommand("SELECT * FROM tblHotelsA ORDER BY HotelName")
    GHotels.DataSource = MyrstP(cmdSQL)
    GHotels.DataBind()

End Sub

and now we have this:

enter image description here

So far, really easy.

Now, it not clear what you used for editing (your popup), but we need "some kind" of popup system. So, I’ll pick jquery.UI. It work rather well – far better then trying to get a bootstrap dialog to work.

So, after the GV, we need to layout + make a "one row" editor. (and place it in a div). so, this looks quite nice for this purpose: (and during development, I do NOT set display:none, but once you have it looking ok, then set display:none for the div style – that hides it (jquery.ui then takes care of this).

Ok, not too much to our "edit detals div". I dropped htis right after the GV.

So, we have this:

    <div id="EditRecord" runat="server" style="float:left;display: none">
        <style>
            .iForm label {display:inline-block;width:90px}
            .iForm input {border-radius:8px;border-width:1px;margin-bottom:10px}                
            .iForm textarea {border-radius:8px;border-width:1px;margin-bottom:10px}     
            .iForm input[type=checkbox] {margin-right:8px}
        </style>

        <div style="float:left" class="iForm">
                <label>HotelName</label><asp:TextBox ID="txtHotel" runat="server" f="HOtelName" width="280"></asp:TextBox> <br />
                <label>First Name</label><asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox> <br />
                <label>Last Name</label><asp:TextBox ID="tLN" runat="server" f="LastName" Width="140"></asp:TextBox> <br />
                <label>City</label><asp:TextBox ID="tCity" runat="server" f="City" Width="140"></asp:TextBox> <br />
                <label>Province</label><asp:TextBox ID="tProvince" runat="server" f="Province" Width="75"></asp:TextBox> <br />
        </div>
        <div style="float:left;margin-left:20px" class="iForm">
            <label>Description</label> <br />
            <asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine" 
                Height="150px" f="Description" ></asp:TextBox> <br />
            <asp:CheckBox ID="chkActive" f="Active" Text=" Active" runat="server" TextAlign="Right" />
            <asp:CheckBox ID="chkBalcony" f="Balcony" Text=" Has Balcony" runat="server" TextAlign="Right" />
        </div>
        <div style="clear:both"></div>
        <button id="cmdSave" runat="server" class="btn" onserverclick="cmdSave_ServerClick" >
            <span aria-hidden="true" class="glyphicon glyphicon-floppy-saved"> Save</span> 
        </button>

        <button id="cmdCancel" runat="server" class="btn" style="margin-left:15px">
            <span aria-hidden="true" class="glyphicon glyphicon-arrow-left"> Back/Cancel</span>
        </button>

        <button id="cmdDelete" runat="server" class="btn" style="margin-left:15px">
            <span aria-hidden="true" class="glyphicon glyphicon-trash"> Delete</span>
        </button>
   </div>

And now our ONE bit of JavaScript – but this is ALL we have to write:

Drop this right after the above div:

    <script>
        function PopEdit() {
            var myDialog = $("#EditRecord");
            myDialog.dialog({
                title: "Edit Hotel Information",
                modal: true,
                width: 860,
                appendTo: "form"
            });
        }
    </script>

Ok, that is quite much it.

Now, we have that row button click. As noted, this is just a plane jane asp.net button, or whatever you like.

That button in the GV was this:

<asp:TemplateField HeaderText="Edit">
   <ItemTemplate>
     <asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn" OnClick="cmdEdit_Click" />
   </ItemTemplate>
</asp:TemplateField>

And the code for this button:

Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)

    Dim btn As Button = sender
    Dim gRow As GridViewRow = btn.NamingContainer
    Dim pkID = GHotels.DataKeys(gRow.RowIndex).Item("ID")

    Dim cmdSQL As New SqlCommand("SELECT * from tblHotelsA where ID = @ID")
    cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = pkID
    Dim rstData As DataTable = MyrstP(cmdSQL)

    Call fLoader(Me.EditRecord, rstData.Rows(0))
    ViewState("rstData") = rstData
    ClientScript.RegisterStartupScript(Page.GetType(), "PopEditKey", "PopEdit()", True)

End Sub

Not a lot of code. We simple load up our "div", and then pop it with that one lone jquery.UI bit of code.

And the result is Now this:

enter image description here

Ok, so now we need the save button code. That is this:

Protected Sub cmdSave_ServerClick(sender As Object, e As EventArgs)

    Dim rstData As DataTable = ViewState("rstData")
    Call fWriterW(EditRecord, rstData.Rows(0))
    Call SaveTable(rstData, "tblHotelsA")
    LoadGrid()

End Sub

And the cancel/back button? Well as we know ANY post-back will collopse the dialog, so it actually does not need much code. And if you wish, I can post the code for the delete button (and we probably should provide a nice looking "confirm" delete prompt – and we can use a jQuery.Ui dialog for that – but again, use 90% server side clean vb code for the delete button.

Ok, so now the helper routines I used for above.

I have 3

 MyRstP - this just accepts a sql command - retruns a table.

Public Function MyrstP(sqlCmd As SqlCommand) As DataTable

    Dim rstData As New DataTable
    Using sqlCmd
        Using conn = New SqlConnection(My.Settings.TEST4)
            conn.Open()
            sqlCmd.Connection = conn
            rstData.Load(sqlCmd.ExecuteReader)
        End Using
    End Using
    Return rstData

End Function

above just saves the hassle of "over and over" having to write query code.

Then we have a routine called "fLoader". Once again, it became OH so tiring to write code over and over that just takes a data row, and shoves it out to controls on the form. So, for each control in a "div", I just make up my own "tag". I use f="data base column name goes here"

So, inside of a given div, then you see this for hotel name:

<label>HotelName</label><asp:TextBox ID="txtHotel" runat="server" f="HotelName" width="280"></asp:TextBox> <br />
<label>First Name</label><asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox> <br />

So, for each control, I kind of cooked up my own binding – saves you have to write that code over and over.

So, floader looks like this (I just added a module1 to mycode). and thus all forms and routines can use this code. All it does is take the rstData (row(0), and move the database columns to the controls that have a tag f="column name". It is a bit of code, but after having written it, it sure makes shoving data out to controls rather easy.

Public Sub fLoader(F As HtmlGenericControl, rst As DataRow)

    For Each c As System.Web.UI.Control In F.Controls
        Select Case c.GetType
            Case GetType(TextBox)
                Dim ctlC As TextBox = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                    End If
                End If
            Case GetType(Label)
                Dim ctlC As Label = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                    End If
                End If
            Case GetType(DropDownList)
                Dim ctlC As DropDownList = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                    End If
                End If
            Case GetType(CheckBox)
                Dim ctlC As CheckBox = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        ctlC.Checked = rst(ctlC.Attributes("f"))
                    End If
                End If
            Case GetType(RadioButtonList)
                Dim ctlC As RadioButtonList = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        ctlC.SelectedValue = rst(ctlC.Attributes("f"))
                    End If
                End If
        End Select
    Next

End Sub

And then I have the reverse. Take from controls -> back into the rstData (data row, but from the datatable).

It really is the above code in "reverse" and takes values from controls and shoves then back into the rstData.

Public Sub fWriterW(f As HtmlGenericControl, rst As DataRow)

    For Each c As System.Web.UI.Control In f.Controls
        Select Case c.GetType
            Case GetType(TextBox)
                Dim ctlC As TextBox = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                    End If
                End If
            Case GetType(Label)
                Dim ctlC As Label = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                    End If
                End If
            Case GetType(DropDownList)
                Dim ctlC As DropDownList = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                    End If
                End If
            Case GetType(CheckBox)
                Dim ctlC As CheckBox = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        rst(ctlC.Attributes("f")) = ctlC.Checked
                    End If
                End If

            Case GetType(RadioButtonList)
                Dim ctlC As RadioButtonList = c
                If Not ctlC.Attributes("f") Is Nothing Then
                    If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                        rst(ctlC.Attributes("f")) = ctlC.SelectedValue
                    End If
                End If


        End Select
    Next

End Sub

and then one more routine – this routine just saves the database table BACK to the database. Hence you pass it the rstData, and table name.

Sub SaveTable(rstData As DataTable, strTable As String)

    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand("select * FROM " & strTable, conn)
            Dim da As New SqlDataAdapter(cmdSQL)
            Dim daU As New SqlCommandBuilder(da)
            conn.Open()
            da.Update(rstData)
        End Using
    End Using

End Sub

So, those little helper routines – I just place in a plane jane Module, and thus all my code can use it. It really nice since you really don’t’ mess with much sql, and NEVER have to mess with a boatload of parameters.

To be fair, I could have posted the ONE grid row button code. Note how it is a simple regular button – and does NOT use the GV events – but is a plane jane "click" event for server side code. And do look at the code close – note how it picks up the row clicked on, and also how it picked up the PK ID.

Answered By – Albert D. Kallal

Answer Checked By – Dawn Plyler (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *