Null reference dropdown in gridview

Solution 1:

A a few things.

You could have the user make all changes, and the below the grid, one save button.

however, since you have a button on each row, to save the row back to database?

Then no need exists to loop all rows in the one save button row????

However, what happens if the user changes several rows - forgets to hit save, and then does hit save? In other words, the user going to be somewhat confused here?

So, it not clear that clicking on the save button should only save the current row?

If that is the goal, then we could have say this setup:

        <asp:GridView ID="GHotels" runat="server" AutoGenerateColumns="False"
            DataKeyNames="ID" CssClass="table" Width="50%">
            <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="Rate">
                    <ItemTemplate>
                    <asp:DropDownList ID="cboRank" runat="server"
                        DataValueField="ID"
                        DataTextField="Rating" >
                    </asp:DropDownList>
                    </ItemTemplate>
                </asp:TemplateField>

                <asp:TemplateField HeaderText="Save" ItemStyle-HorizontalAlign="Center">
                    <ItemTemplate>
                        <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" Width="60"
                          OnClick="cmdSave_Click"  />                          
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

And code to load would be:

Dim rstRank As New DataTable ' to load cbo box

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

    If Not IsPostBack Then

        LoadData()

    End If

End Sub

Sub LoadData()

    ' load combo box data
    rstRank = MyRst("SELECT ID, Rating from tblRating ORDER BY ID")

    Dim strSQL As String =
        "SELECT ID, FirstName, LastName, HotelName, Description, Ranking from tblHotels"

    GHotels.DataSource = MyRst(strSQL)
    GHotels.DataBind()

End Sub

Function MyRst(strSQL As String) As DataTable

    Dim rst As New DataTable

    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            conn.Open()
            rst.Load(cmdSQL.ExecuteReader)
        End Using
    End Using

    Return rst

End Function

Protected Sub GHotels_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GHotels.RowDataBound

    If e.Row.RowType = DataControlRowType.DataRow Then

        Dim cboRank As DropDownList = e.Row.FindControl("cboRank")
        cboRank.DataSource = rstRank
        cboRank.DataBind()
        ' add blank row
        cboRank.Items.Insert(0, New ListItem("Select", "0"))

        Dim v As Object = e.Row.DataItem
        Dim rData As DataRowView = e.Row.DataItem

        If Not IsDBNull(rData("Ranking")) Then
            cboRank.SelectedValue = rData("Ranking")
        End If

    End If

End Sub

and we now have this:

enter image description here

Ok, so the save button - to save the one row?

this works:

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

    Dim btn As Button = sender
    Dim gRow As GridViewRow = btn.NamingContainer

    Dim cboRank As DropDownList = gRow.FindControl("cboRank")
    Dim strSQL As String = "UPDATE tblHotels SET Ranking = @Rating WHERE ID = @ID"

    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = GHotels.DataKeys(gRow.RowIndex).Value
            cmdSQL.Parameters.Add("@Rating", SqlDbType.Int).Value = cboRank.SelectedItem.Value
            conn.Open()
            cmdSQL.ExecuteNonQuery()
        End Using

    End Using

End Sub

So, as you can see, no real need to loop and save ALL rows, is there?

I would think that if you want ONE save button?

Then move the save button OUT of the grid, and then save have this:

        <asp:GridView ID="GHotels" runat="server" AutoGenerateColumns="False"
            DataKeyNames="ID" CssClass="table" Width="50%">
            <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="Rate">
                    <ItemTemplate>
                    <asp:DropDownList ID="cboRank" runat="server"
                        DataValueField="ID"
                        DataTextField="Rating" >
                    </asp:DropDownList>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
        <br />
        <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" Width="60"
        OnClick="cmdSave_Click"  />                          

And now this:

enter image description here

And the one save button (which I think after saving should say navagate to some other page - since we are done). That code to now save all rows, would be:

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

    Using conn As New SqlConnection(My.Settings.TEST4)

        Dim strSQL As String = "UPDATE tblHotels SET Ranking = @Rating WHERE ID = @ID"

        conn.Open()

        For Each gRow As GridViewRow In GHotels.Rows

            Dim cboRank As DropDownList = gRow.FindControl("cboRank")

            Using cmdSQL As New SqlCommand(strSQL, conn)
                cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = GHotels.DataKeys(gRow.RowIndex).Value
                cmdSQL.Parameters.Add("@Rating", SqlDbType.Int).Value = cboRank.SelectedItem.Value
                cmdSQL.ExecuteNonQuery()
            End Using
        Next

    End Using

End Sub

So, again the code is rather simular for saving "all" rows, or just the one row.

However, we could also drop in a button beside "save" called "un-do" or "cancel"

So, the above shows how to get the row click - for ONE row save, and the 2nd code shows how do to this for all rows.