asp.netvb.net

Connect textbox in gridview and dropdownlist with a single datatable so that values can change in both at once without requiring a postback in ASP.NET


I would like to bound textbox with dropdownlist by a single datatable or datasource like we bound controls in winform by using BindingSource. I don't want to use postback to do so. My dropdownlist:

Dim ddlPack As DropDownList = DirectCast(e.Row.FindControl("ddlPackaging"), DropDownList)
Dim dt as new Datatable()
dt=dl.getDefData(Resc.DL.FlagE.Packaging)     /////it return datatable with 5 columns
ddlPack.DataSource = dt
ddlPack.DataTextField = "EName"
ddlPack.DataValueField = "Id"
ddlPack.DataBind()

Above code works fine.

But I want to also bound textbox to same datatable of dropdownlist (that is dt).

<asp:TemplateField HeaderText="Quantity">
<ItemTemplate>
<asp:TextBox ID="txtQty" CssClass="form-control-sm" runat="server" Text="0" Width="64px"  MaxLength="6"></asp:TextBox> =
<asp:TextBox ID="txtTotalWeight" CssClass="form-control-sm" ReadOnly="true" Width="84px" runat="server" Text="0" MaxLength="6"></asp:TextBox><br />

<asp:TextBox ID="txtMul" runat="server" Text="0"></asp:TextBox>    //////////This Textbox to bound
Munn(<asp:TextBox ID="txtMunn" CssClass="form-control-sm" runat="server" Width="72px" Text="0" MaxLength="6"></asp:TextBox>)
Per KG Rate <asp:TextBox ID="txtPerKG" CssClass="form-control-sm" Width="98px" runat="server" Text="0" MaxLength="6"></asp:TextBox>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center"></HeaderStyle>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>

The above dt contains 5 columns

SELECT [Id], [EName], [UName], [Multiplier], [Created Date]  FROM  PackagingT

One more thing these controls are in gridview which have multiple dropdown with different datatables so I can't bound gridview to datatable, Using VB.net with DotNet 4.0.

The logic behind all of this work is whenever a user change value from dropdownlist and enter value in txtQty, txtTotalWeight = txtMul * txtQty (These are textboxes Ids), gives result (remember I don't need to use postback). If this can be done by JavaScript along with asp.net (Vb.net) it's ok to me.

I had tried:

<asp:TextBox ID="txtMul" runat="server" Text="<%# Eval("Multiplier") %>"></asp:TextBox>

and

<asp:TextBox ID="txtMul" runat="server" Text="<%#  Container.DataItemIndex("Multiplier") %>"></asp:TextBox>

Solution

  • One more thing these controls are in gridview which have multiple dropdown with different datatables so I can't bound gridview to datatable,

    Yes, you can bind the drop down to different data sources. And in fact, since you are starting to do "custom" GridView code, then I VERY much suggest you use code to bind and set the data source of the DDL anyway. (in other words, don't use nor bother with SqlData sources placed in the markup). SqlData sources in the markup are quick and easy, but the very instant you need custom control of the data binding, then don't use Sql data sources, as they are VERY limited, and worse yet you have no control over when such data sources trigger and fill out the controls. So, for a super simple page, then perhaps you can "risk" use of a SqlData source on a page, anything beyond a simple data binding, then SQL data source objects in the markup are not only messy, but lack flexibility.

    It also not at a all clear why you want both a ddl, and a text box showing the same information on a given row. Since this is SUCH VERY strange request, then you probably should explain why and what the goal here is?

    However, not knowing why you want both a ddl and text box to show the same information, let's just code up a working example.

    First up be it desktop (FoxPro, MS-Access, vb.net or whatever), the approach to display such information is to use SQL relations, and thus most of the work will be done for you. In other words, do a left join of the table that drives the DDL, and no extra code is required to display such values. However, as noted, in your case you suggest that each row may have a different data source for the DDL, and hence the following approach is required.

    First, we have to deal with the DDL.

    When we fill the GV with data, then for each row of the GV, we need to:

    Load up the DDL (set the data source). We THEN have to set the DDL to the current selected "ID" value for the given row. And if null/nothing, then of course we should set the DDL to show a "please select value" as a nice place holder. And of course if the current row has a null/empty value for the ID, then again we need to take that into consideration.

    Also, like near every platform since the dawn of the PC computer revolution, those DDL's (combo boxes) tend to support 2 values. A hidden "pk" value, and then a 2nd column of a nice "display text". You not made it clear if that is your use case, but I'll assume as such, since that's again a super common setup.

    One thing to keep in mind that un-like desktop in which I can bind a data source say with 5 columns to a DDL, but still only have the hidden PK value + 2nd column of text display, desktop DOES persist ALL OF the columns. Hence, if I bind say 5 columns to the DDL, I can later on by using of the index of the current DDL selected can retrive those other columns.

    However, with the web based DDL's, then those additional columns are NOT persisted, and once the databinding process is complete, then ONLY the 2 columns (PK + display text) are available.

    Ok, so with the above in mind, then let's assume a list of some people, and we will use a DDL to select a hotel.

    Of course in the data, the Hotel will be saved as a "pk id", but of course we want to display the hotel name text in the DDL.

    Then we add your VERY strange request that beside the DDL, we will ALSO have a text box that display the current selected (text) value of the drop down.

    Hence, this simple markup:

                <asp:GridView ID="GridView1" runat="server"
                    AutoGenerateColumns="False" DataKeyNames="ID"
                    OnRowDataBound="GridView1_RowDataBound"
                    CssClass="table table-hover">
                    <Columns>
                        <asp:TemplateField HeaderText="First Name">
                            <ItemTemplate>
                                <asp:TextBox ID="txtFirst" runat="server" Width="110px"
                                    Text='<%# Eval("FirstName") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                        <asp:TemplateField HeaderText="Last Name">
                            <ItemTemplate>
                                <asp:TextBox ID="txtLast" runat="server" Width="110px"
                                    Text='<%# Eval("LastName") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                        <asp:TemplateField HeaderText="City">
                            <ItemTemplate>
                                <asp:TextBox ID="txtCity" runat="server" Width="110px"
                                    Text='<%# Eval("City") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="HotelName">
                            <ItemTemplate>
                                <asp:DropDownList ID="ddlHotels" runat="server"
                                    DataValueField="ID"
                                    DataTextField="HotelName"
                                    onchange="mydropcopy(this);return false">
                                </asp:DropDownList>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="Crazy Repeated HotelName">
                            <ItemTemplate>
                                <asp:TextBox ID="txtHotel" runat="server" Width="220px"></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                    </Columns>
                </asp:GridView>
                <br />
                <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn myshadow" />
                <asp:Button ID="cmdAdd" runat="server" Text="Add Row" CssClass="btn myshadow"
                    Style="margin-left: 20px" />
                <br />
    

    Code to load the GV is thus:

    Dim rstPeople As New DataTable
    Dim rstHotels As New DataTable
    
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            LoadGrid()
            Session("MyTable") = rstPeople
        Else
            rstPeople = Session("MyTable")
        End If
    End Sub
    
    Sub LoadGrid()
    
    
        Using cmdSQL As New SqlCommand("", New SqlConnection(My.Settings.TEST4))
    
            cmdSQL.Connection.Open()
            cmdSQL.CommandText = "SELECT ID, HotelName FROM tblHotelsA 
                                  ORDER BY HotelName"
            rstHotels.Load(cmdSQL.ExecuteReader)
    
            cmdSQL.CommandText = "SELECT * from People
                                  ORDER BY FirstName, LastName"
            rstPeople.Load(cmdSQL.ExecuteReader)
            GridView1.DataSource = rstPeople
            GridView1.DataBind()
    
        End Using
    
    End Sub
    
    Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs)
    
        If e.Row.RowType = DataControlRowType.DataRow Then
    
            Dim rDataRow As DataRowView = e.Row.DataItem ' gets binding row data
    
            Dim ddlHotels As DropDownList = e.Row.FindControl("ddlHotels")
            Dim txtHotel As TextBox = e.Row.FindControl("txtHotel")
    
            ddlHotels.DataSource = rstHotels
            ddlHotels.DataBind()
            ddlHotels.Items.Insert(0, "Select Hotel")
    
            If rDataRow("Hotel_ID").ToString <> "" Then
                ddlHotels.Text = rDataRow("Hotel_ID")
                txtHotel.Text = ddlHotels.SelectedItem.Text  ' set text box to combo box
            End If
    
        End If
    
    End Sub
    

    So, note how we use the row data bind event (it runs for each row) to load up the DDL.

    The result is thus this:

    enter image description here

    So, the only extra part is when we select the DDL, then we need to copy the selected (text) value to the text box.

    And without a post-back, then we use JavaScript. This code (placed right after the GridView) is thus this:

    (using jQuery here)

            <script>
                function mydropcopy(ddl) {
                    var ddlHotels = $(ddl)
                    var txtHotels = $("#" + ddl.id.replace("ddlHotels", "txtHotel"))
                    var selectedText = ddlHotels.find("option:selected").text();
                    var selectedValue = ddlHotels.val();
                    txtHotels.val(selectedText)
                }
            </script>
    

    As a "FYI" the above jQuery code also shows how to get both the hidden ID value of the DDL, and also the text value.

    edit: Grabbing values from table without post-back

    So, looking at above, it quite much is a proof of concept of what you are looking for.

    However, let's assume I need the "rate" from the hotel drop down.

    Our new markup:

                <asp:GridView ID="GridView1" runat="server"
                    AutoGenerateColumns="False" DataKeyNames="ID"
                    OnRowDataBound="GridView1_RowDataBound"
                    CssClass="table table-hover">
                    <Columns>
                        <asp:TemplateField HeaderText="First Name">
                            <ItemTemplate>
                                <asp:TextBox ID="txtFirst" runat="server" Width="110px"
                                    Text='<%# Eval("FirstName") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                        <asp:TemplateField HeaderText="Last Name">
                            <ItemTemplate>
                                <asp:TextBox ID="txtLast" runat="server" Width="110px"
                                    Text='<%# Eval("LastName") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="City">
                            <ItemTemplate>
                                <asp:TextBox ID="txtCity" runat="server" Width="110px"
                                    Text='<%# Eval("City") %>'></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="HotelName">
                            <ItemTemplate>
                                <asp:DropDownList ID="ddlHotels" runat="server"
                                    DataValueField="ID"
                                    DataTextField="HotelName"
                                    onchange="mydropcopy(this);return false">
                                </asp:DropDownList>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="Nights">
                            <ItemTemplate>
                                <asp:TextBox ID="txtNights" runat="server" Width="40px"
                                    Text='<%# Eval("Nights") %>'
                                    onchange="nightcalc(this);return false;"
                                    ></asp:TextBox>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                        <asp:TemplateField HeaderText="Night Rate"  >
                            <ItemTemplate>
                                <div style="text-align:right">
                                <asp:TextBox ID="lblRate" runat="server" Text='<%#Eval("NightRate", "{0:n0}") %>'
                                    style="text-align:right"
                                    EnableViewState="true"
                                    >
                                 </asp:TextBox>
                                </div>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="Price"  >
                            <ItemTemplate>
                                <div style="text-align:right">
                                <asp:TextBox ID="lblPrice" runat="server" 
                                    Text='<%# Eval("Price", "{0:n0}") %>'
                                    style="text-align:right"
                                     EnableViewState="true"
                                    >
                                 </asp:TextBox>
                                </div>
                            </ItemTemplate>
                        </asp:TemplateField>
    
                    </Columns>
                </asp:GridView>
                <br />
                <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn myshadow" />
                <asp:Button ID="cmdAdd" runat="server" Text="Add Row" CssClass="btn myshadow"
                    Style="margin-left: 20px" />
                <br />
            </div>
            <br />
    
            <script>
                function mydropcopy(ddl) {
                    var ddlHotels = $(ddl)
                    var lblRate = $("#" + ddl.id.replace("ddlHotels", "lblRate"))
                    var selectedText = ddlHotels.find("option:selected").text();
                    var selectedValue = ddlHotels.val();
                    var nights = $("#" + ddl.id.replace("ddlHotels", "txtNights"))
                    var price = $("#" + ddl.id.replace("ddlHotels", "lblPrice"))
    
                    $.ajax({
                        type: "POST",
                        url: "EditPeopleGrid.aspx/GetPrice",
                        dataType: "json",
                        contentType: "application/json; charset=utf-8",
                        data: JSON.stringify({HotelID : selectedValue }),
                        success: function (data) {
                            lblRate.val(data.d)
                            rowcalc(nights, lblRate, price)
                        }
                    });
    
                }
                function nightcalc(ctrl) {
    
                    var nights = $(ctrl)
                    var lblRate = $("#" + ctrl.id.replace("txtNights", "lblRate"))
                    var price = $("#" + ctrl.id.replace("txtNights", "lblPrice"))
    
                    rowcalc(nights, lblRate, price)
    
                }
    
                function rowcalc(nights, rate, priceresult) {
                    // simple row calc - pass nights, rate and price controls
    
                    priceresult.val(nights.val() * rate.val())
    
                }
    
            </script>
    

    And the result now looks like this:

    enter image description here

    Note how we are free to tab around - edit any row.

    The code behind is now this:

    Dim rstPeople As New DataTable
    Dim rstHotels As New DataTable
    
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            LoadGrid()
            Session("MyTable") = rstPeople
        Else
            rstPeople = Session("MyTable")
        End If
    End Sub
    
    Sub LoadGrid()
    
    
        Using cmdSQL As New SqlCommand("", New SqlConnection(My.Settings.TEST4))
    
            cmdSQL.Connection.Open()
            cmdSQL.CommandText = "SELECT ID, HotelName,NightRate FROM tblHotelsA 
                                  ORDER BY HotelName"
            rstHotels.Load(cmdSQL.ExecuteReader)
    
            cmdSQL.CommandText = "SELECT * from People
                                  ORDER BY FirstName, LastName"
            rstPeople.Load(cmdSQL.ExecuteReader)
            GridView1.DataSource = rstPeople
            GridView1.DataBind()
    
        End Using
    
    End Sub
    
    Sub GridtoTable()
    
        ' this sends gv rows back to rstData
        ' does NOT save/send back to database
    
        ' pull grid rows back to table.
    
        For Each rRow As GridViewRow In GridView1.Rows
    
            Dim RecordPtr As Integer = rRow.RowIndex
    
            Dim OneDataRow As DataRow
    
            OneDataRow = rstPeople.Rows(RecordPtr)
    
            OneDataRow.Item("FirstName") = TryCast(rRow.FindControl("txtFirst"), TextBox).Text
            OneDataRow.Item("LastName") = TryCast(rRow.FindControl("txtLast"), TextBox).Text
            OneDataRow.Item("City") = TryCast(rRow.FindControl("txtCity"), TextBox).Text
    
            Dim ddlHotels As DropDownList = rRow.FindControl("ddlHotels")
            If ddlHotels.SelectedIndex = 0 Then
                OneDataRow.Item("Hotel_Id") = DBNull.Value
            Else
                OneDataRow.Item("Hotel_Id") = ddlHotels.SelectedItem.Value
            End If
    
            OneDataRow.Item("Nights") = TryCast(rRow.FindControl("txtNights"), TextBox).Text
            OneDataRow.Item("NightRate") = TryCast(rRow.FindControl("lblRate"), TextBox).Text
            OneDataRow.Item("Price") = TryCast(rRow.FindControl("lblPrice"), TextBox).Text
    
        Next
    
    
    End Sub
    Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click
    
        Call GridtoTable()            ' send any edits back to table
    
        ' now send table back to database with updates
        Dim strSQL As String = "SELECT * from People WHERE ID = 0"
    
        Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST4))
            cmdSQL.Connection.Open()
            Dim daupdate As New SqlDataAdapter(cmdSQL)
            Dim cmdBuild As New SqlCommandBuilder(daupdate)
            daupdate.Update(rstPeople)
        End Using
    
    End Sub
    
    Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs)
    
        If e.Row.RowType = DataControlRowType.DataRow Then
    
            Dim rDataRow As DataRowView = e.Row.DataItem ' gets binding row data
    
            Dim ddlHotels As DropDownList = e.Row.FindControl("ddlHotels")
            Dim lblNightRate As TextBox = e.Row.FindControl("lblRate")
    
            ddlHotels.DataSource = rstHotels
            ddlHotels.DataBind()
            ddlHotels.Items.Insert(0, "Select Hotel")
    
            If rDataRow("Hotel_ID").ToString <> "" Then
    
                ddlHotels.Text = rDataRow("Hotel_ID")
                'lblNightRate.Text = FormatNumber(rstHotels.Rows(ddlHotels.SelectedIndex)("NightRate"), 0)
    
            End If
    
        End If
    
    End Sub
    
    
    <WebMethod()>
    Public Shared Function GetPrice(HotelID As String) As String
    
        Dim sResult As String = "0"
        If IsNumeric(HotelID) Then
    
            Dim strSQL As String =
                    "SELECT ID, NightRate FROM tblHotelsA WHERE ID = @ID"
            Dim cmdSQL As New SqlCommand(strSQL)
            cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = HotelID
    
            Dim dtHotels As DataTable = MyRstP(cmdSQL)
            If dtHotels.Rows.Count > 0 Then
                sResult = FormatNumber(dtHotels.Rows(0)("NightRate"), 0)
            End If
    
        End If
    
        Return sResult
    
    End Function
    

    Note close in above the web method that runs after you change a hotel (the ddl). This calls the web method to get the night room rate for that given hotel.

    Note close how I used jQuery selectors to pick up the current row (this is a nice trick). Since each set of controls on a given row will be numbered, then we use the .replace method to simply "change" the name of the control, and the row numbering that ASP.NET applies to the controls thus will not matter.

    Note close the save routine - it does a update of any and all rows - including additions, and achieves this in one database operation.