asp.netgridviewselectedindexchanged

How to get cell index by clicking on GridView column in asp.net - simple method


I know this question have been asked many times and with my limited knowledge of JS I have tried everything, I simply can not make any solutions work. So I've bound my data to GridView :

            Dim dt As DataTable = New DataTable()
            dt.Columns.Add(New DataColumn("ID"))
            dt.Columns.Add(New DataColumn("NAME"))
            dt.Columns.Add(New DataColumn("CITY"))
            dt.Columns.Add(New DataColumn("CURRENT JOB"))
            dt.Columns.Add(New DataColumn("@COMPANY"))
            dt.Columns.Add(New DataColumn("C")) ' communication
            dt.Columns.Add(New DataColumn("S")) 'select

            Dim ID As String
            Dim name As String
            Dim city As String
            Dim current_job As String
            Dim company As String
            Dim com As String

            For i = 0 To ds.Tables(0).Rows.Count - 1
                ID = ds.Tables(0).Rows(i)(1).ToString()
                name = ds.Tables(0).Rows(i)(2).ToString()
                city = ds.Tables(0).Rows(i)(3).ToString()
                current_job = ds.Tables(0).Rows(i)(9).ToString()
                company = ds.Tables(0).Rows(i)(10).ToString()

                If ds.Tables(0).Rows(i)(12).ToString() = "" Then
                    com = ""
                Else
                    com = "X"
                End If

                dt.Rows.Add(ID, name, city, current_job, company, com, "S")

            Next

            candidate_grid_view.DataSource = dt
            candidate_grid_view.DataBind()

I've registered cell click on candidate_grid_view.OnRowDataBound event :

Protected Sub Candidate_OnRowDataBound(sender As Object, e As System.Web.UI.WebControls.GridViewRowEventArgs)
    If e.Row.RowType = DataControlRowType.DataRow Then
        e.Row.Attributes("onclick") = Page.ClientScript.GetPostBackClientHyperlink(candidate_grid_view, "Select$" & e.Row.RowIndex)
        e.Row.Attributes("style") = "cursor:pointer"
    End If
End Sub

I can easy get row index by candidate_grid_view.SelectedRow.RowIndex in OnSelectedIndexChanged event, but I need click event for column (cell) that will register cell index (column index). There is no cellindex propery!?

Protected Sub Candidate_OnSelectedIndexChanged(sender As Object, e As EventArgs)
    Dim index As Integer = candidate_grid_view.SelectedRow.RowIndex
End Sub

Why something that easy as getting column index in VB.NET so complicated in ASP.NET?


Solution

  • Well, havng a row click - very easy.

    So, lets work our way though this.

    first, a gv - simple markup like this:

        <div style="padding:25px;width:50%">
            <asp:GridView ID="GridView1" 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="City" HeaderText="City" />
                    <asp:BoundField DataField="Description" HeaderText="Description" />
    
                    <asp:TemplateField HeaderText="View" ItemStyle-HorizontalAlign="Center">
                        <ItemTemplate>
                            <asp:Button ID="cmdView" runat="server" Text="View" CssClass="btn"
                                OnClick="cmdView_Click" />
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
    
            </asp:GridView>
        </div>
    

    Our code to fill is this:

    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()
    
        Using conn As New SqlConnection(My.Settings.TEST4)
    
            Dim strSQL As String = "SELECT * FROM tblHotelsA ORDER BY HotelName"
    
            Using cmdSQL As New SqlCommand(strSQL, conn)
                conn.Open()
                Dim rstData As New DataTable
                rstData.Load(cmdSQL.ExecuteReader())
                GridView1.DataSource = rstData
                GridView1.DataBind()
            End Using
        End Using
    
    End Sub
    
    Protected Sub cmdView_Click(sender As Object, e As EventArgs)
    
        Dim btn As Button = sender
        Dim gRow As GridViewRow = btn.NamingContainer
    
        Debug.Print("Row click index = " & gRow.RowIndex)
    
        ' get primary key (hidden by datakeys
        Dim PKID As Integer = GridView1.DataKeys(gRow.RowIndex).Item("ID")
        Debug.Print("Data base row PKID = " & PKID)
        Debug.Print("hotel name of this row = " & gRow.Cells(2).Text)
    
    
    End Sub
    

    And now if we click on view button (for the row), then we have this:

    enter image description here

    Ok, so that so far - very easy.

    Now, what about a row click anywhere - not necessary on the button?

    Gee, all controls? I would dump "cells" and drop in standard asp.net controls into that GV - (such as I did for the button). However, with a lot of columns, this becomes a pain - (too many messy "templatefield" tags - I don't like those).

    As a result, I would dump the GV, and go to a listview.

    However, lets just force code our way though this. But, I been doing this for a long time - never had to just get the cell information.

    But, you can say do this:

    In our row data bound, just add a click event to each cell, say like this:

    Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
    
        If e.Row.RowType = DataControlRowType.DataRow Then
            For i As Integer = 0 To e.Row.Cells.Count - 2   ' skip last button cell
                Dim MyCell As TableCell = e.Row.Cells(i)
                Dim RowCol As string = e.Row.RowIndex & "," & i
                MyCell.Attributes.Add("onclick", "MyClick('" & RowCol & "')")
            Next
        End If
    End Sub
    

    All we did was add a row, and col value.

    So, now in our markup, we have this:

            </asp:GridView>
    
            <asp:HiddenField ID="HRowInfo" runat="server" />
            <asp:Button ID="cmdRowClick" runat="server" Text="Row click" ClientIDMode="Static"
                onclick="cmdRowClick_Click" CommandArgument="0" />
            <script>
    
                function MyClick(RowInfo) {
    
                    $("#HRowInfo").val(RowInfo)
                    $('#cmdRowClick').click()
                }
            </script>
    

    So, we shove value into hidden field, and click our button - just a plane jane button below the grid (we would hide it with style="display:none"

    And code behind is this:

    Protected Sub cmdRowClick_Click(sender As Object, e As EventArgs)
    
        Debug.Print(HRowInfo.Value)
    
    End Sub
    

    Now, when I click on any cell, I get a output of the row, colum, say like this:

    enter image description here

    And from above, I can index into the Gridview.Rows and then cells the two values.

    However, I am not convinced that we really need/want a click event on the cells - we "might", but then again, might be better to put some controls on that form - as a repating grid, and work on a row by row basis.

    As noted, I tend to use ListView for this.

    Say we have the above hotel list, but I want the user to enter a value in a "cell" for say the number of nights. But the grid displays the nightly rate, and when I enter the number of nights, I want another column to show the total cost.

    We COULD try and deal and wire up say a cell, but is is VERY rare i need to do this.

    But, with a standard control, it does become somewhat easy.

    As I stated, I never really liked GV for doing this kind of thing, but lets add to above the nights text box, the night rate, and a total column.

    (We ARE better to use a listview, but no big deal).

    so, lets assume this markup:

            <asp:GridView ID="GridView1" 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="City" HeaderText="City" />
                    <asp:BoundField DataField="Description" HeaderText="Description"  />
                    <asp:TemplateField HeaderText="Nights">
                        <ItemTemplate>
                            <asp:TextBox ID="txtNights" runat="server" TextMode="Number"
                                Text = '<%# Eval("Nights") %>'  Width ="40">
                            </asp:TextBox>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField HeaderText="Per night">
                        <ItemTemplate>
                            <asp:Label ID="LPrice" runat="server" Text='<%# Eval("Price", "{0:C2}") %>' >
                            </asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                    <asp:TemplateField HeaderText="Total">
                        <ItemTemplate>
                            <asp:Label ID="txtAmount" runat="server"
                                Text='<%# Eval("Tamount", "{0:c}") %>'
                                width="50"
                                >
                            </asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
    
                </Columns>
    
            </asp:GridView>
    

    So, same code to fill - (no row data bound event).

    Ok, so we now have this:

    enter image description here

    Now, lets assume I want to change the number of nights. I don't REALLY need a cell click, do I? I just want the user to type in the number of nights, and then say update the Total amount.

    So, we just add a plane jane text changed event to that text box, (and auto post-back).

    So, the markup becomes this:

                    <asp:TemplateField HeaderText="Nights">
                        <ItemTemplate>
                            <asp:TextBox ID="txtNights" runat="server" TextMode="Number"
                                Text = '<%# Eval("Nights") %>'  Width ="40"
                                OnTextChanged="txtNights_TextChanged"
                                AutoPostBack="true"
                                >
                            </asp:TextBox>
                        </ItemTemplate>
                    </asp:TemplateField>
    

    We now can just click on any "nights" cell in the grid, enter a new value and hit tab, or move out and click on another row.

    the code for the text changed event looks like this:

    Protected Sub txtNights_TextChanged(sender As Object, e As EventArgs)
    
        Dim txtNights As TextBox = sender
        Dim gRow As GridViewRow = txtNights.NamingContainer
    
        Dim lPrice As Label = gRow.FindControl("LPrice")
        Dim TotalAmount As Label = gRow.FindControl("txtAmount")
    
        TotalAmount.Text = (lPrice.Text * txtNights.Text).ToString("C2")
    
    
    End Sub
    

    So, this is why I am suggesting that you in general don't needd a cell click event.

    You "might", but in most cases I don't think you do.