asp.netvb.netgridview

Kindly help Index was out of range. Must be non-negative


<asp:GridView ID="gvUnitNomRoll" runat="server"  OnRowCommand ="gvUnitNomRoll_RowCommand1"  SkinID="gridviewSkin" Width="100%"
    DataKeyNames="id, name, desig, idr, service" EmptyDataText="" AutoGenerateColumns="false">
    <Columns>
        <asp:TemplateField HeaderText="Sl" HeaderStyle-HorizontalAlign="Center" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate> <%#Ctype (Container, GridViewRow).RowIndex+1 %></ItemTemplate>
        </asp:TemplateField>
          <asp:BoundField HeaderText="id" DataField="name" Visible ="false"/>
        <asp:BoundField HeaderText="Name" DataField="name"/>
        <asp:BoundField HeaderText="desig" DataField="desig" />
        <asp:BoundField HeaderText="Idr" DataField="idr" />
        <asp:BoundField HeaderText="Service" DataField="service" />
       <asp:ButtonField ButtonType="Button" CommandName="ed" ControlStyle-CssClass ="allBtn blue"  HeaderText="Update Details" 
           ItemStyle-HorizontalAlign="Center" ItemStyle-Width="80px" Text="Update" />                                        
        <asp:ButtonField ButtonType="Button" CommandName="tfrOut" ControlStyle-CssClass ="allBtn red"  
            HeaderText="Transfer" ItemStyle-HorizontalAlign="Center" ItemStyle-Width="80px" Text="Transfer Out" />                                        
        <asp:ButtonField ButtonType="Button" CommandName="Verify" ControlStyle-CssClass ="allBtn green" 
            HeaderText="Verify" ItemStyle-HorizontalAlign="Center"  ItemStyle-Width="80px" Text="Verify" />  


    </Columns>
</asp:GridView>

this is my vb.code

Dim CmdName As String = e.CommandName
Dim nInt As Integer = Convert.ToInt32(e.CommandArgument)
Dim currentPno As String = gvUnitNomRoll.DataKeys(nInt).Item("id").ToString() 'Here throws error ' 
If (e.CommandName = "ed") Then
    If currentPno <> "" Then

It throws error

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index

while debugging

Dim currentPno As String = gvUnitNomRoll.DataKeys(nInt).Item("id").ToString() 'Here throws error '

Solution

  • Let's back up here a bit.

    First up, there is VERY little reason to stuff up datakeys with all those values. You HAVE those values in the grid, so, no real need to put them again into datakeys.

    Datakeys as a general rule SHOULD ONLY hold the database primary key "ID".

    And daykeys are nice, since THEN you don't have to display/include/have/worry about showing that ID in the GV (users don't need to see or care). And in fact for security reasons, you don't need nor want to include the database PK id in that gv anyway (kind of the whole point of datakeys).

    Also, I don't see ANYWHERE where you are setting command argument, but you THEN attempt to pull command argument into a int value (so, that's where your code is failing).

    In general, if you want a simple button click in the GV?

    Then just drop in a plain Jane regular button. They work better, are easy to use, and BETTER you do DOUBT in the past worked with good old asp.net button.

    I don't have your data, but let's load up a gv with some rows of data (Hotels). Then add a button click to the gv, and then get the one data row the user clicked on.

    <h3>Hotels</h3>
    <asp:GridView ID="GridView1" runat="server" Width="40%"
        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="City" HeaderText="City" />
            <asp:BoundField DataField="Description" HeaderText="Description" />
            <asp:TemplateField>
                <ItemTemplate>
                    <asp:Button ID="cmdEdit" runat="server" Text="Edit"
                        CssClass="btn"
                        onclick="cmdEdit_Click" />
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
    

    So, use a "Template" field, and inside of that, you can put ANY standard plain Jane asp.net control - including that of a button.

    Now, in most cases, when we drag + drop a button into a webform, we can double click on that button to wire up a click event. However, since the button is "nested" inside of the gv, then we can't easy double click on the button, and jump to the code behind stub.

    In markup view, simple type in onclick=.

    When you hit "=", then IntelliSense will kick in, and "offer" you to create the simple button click.

    This works, and looks like this:

    enter image description here

    so, here is a our gv markup:

    <h3>Hotels</h3>
    <asp:GridView ID="GridView1" runat="server" Width="40%"
        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="City" HeaderText="City" />
            <asp:BoundField DataField="Description" HeaderText="Description" />
            <asp:TemplateField>
                <ItemTemplate>
                    <asp:Button ID="cmdEdit" runat="server" Text="Edit"
                        CssClass="btn"
                        OnClick="cmdEdit_Click"
                         />
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
    

    And our code behind to load the GV is this:

    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()
    
        Dim strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName"
        Dim cmdSQL As New SqlCommand(strSQL)
        GridView1.DataSource = MyrstP(cmdSQL)
        GridView1.DataBind()
    
    End Sub
    

    OK, so now we need the event code for that button click:

    Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
    
        Dim btn As Button = sender
        Dim gRow As GridViewRow = btn.NamingContainer
        Dim intPK As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
    
        Dim cmdSQL = New SqlCommand("SELECT * FROM tblHotelsA WHERE ID = @ID")
        cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = intPK
        Dim rstHotel As DataTable = MyrstP(cmdSQL)
    
        Debug.Print($"Row click = {gRow.RowIndex}")
        Debug.Print($"Data base PK id (data keys) = {intPK}")
        Debug.Print($"Hotel Name from gv = {gRow.Cells(2).Text}")
    
        If rstHotel.Rows.Count > 0 Then
            Call EditOne(rstHotel.Rows(0))
        End If
    
    
    End Sub
    

    So, output from above is this:

    enter image description here

    And clicking on a row button, we get this:

    Row click = 5
    Data base PK id (data keys) = 3
    Hotel Name from gv = Sandman Inn
    

    Of course I have a routine called "Editone". All that does is hide the GV, display a div with standard controls, and some code that fills out the controls.

    So, edit one looks like:

    Sub EditOne(MyRow As DataRow)
    
        ViewState("PKID") = MyRow("ID")
        fLoader(EditRecord, MyRow)
        GridView1.Visible = False
        EditRecord.Visible = True
    
    End Sub
    

    So, now we see/get this:

    enter image description here

    "fLoader" is a routine that loops all controls in that div, and shoves in values from the one data row.