asp.net Gridview模板字段内的TextBox OnTextChanged事件在更新后丢失下一个字段焦点

mm9b1k5b  于 2022-12-20  发布在  .NET
关注(0)|答案(1)|浏览(178)

我有一个包含文本框的GridView<asp:TemplateField />,GridView位于 AJAX 更新面板中。使用TextChanged事件处理GridView中的文本框,当用户在模板字段中输入内容时,该事件将触发。到目前为止,这还可以,但问题是,一旦我在更新后退出文本框,光标就会失去焦点,而不是移动到下一个控件。
当我再次按Tab键时,它从模板字段的第一行第一个单元格开始。如何处理这个问题?如何使光标移动到下一个字段而不失去当前行的焦点。
ASPX代码

<asp:TemplateField HeaderText="Amount"
    HeaderStyle-Width="255"  ItemStyle-Width="255">
    <ItemTemplate>
        <asp:TextBox ID="txtAmount" runat="server" OnTextChanged = "OnTextChanged" AutoPostBack = "true"
            Visible="TRUE" Height="30">
        </asp:TextBox>
    </ItemTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="Qty"
    HeaderStyle-Width="255"  ItemStyle-Width="255">
    <ItemTemplate>
        <asp:TextBox ID="txtQty" runat="server" OnTextChanged = "OnTextChanged" AutoPostBack = "true"
            Visible="TRUE" Height="30">
        </asp:TextBox>
    </ItemTemplate>
</asp:TemplateField>

etc.,

代码隐藏

Protected Sub OnTextChanged(sender As Object, e As EventArgs)
    Dim textBox As TextBox = CType(sender, TextBox)
    Dim id As String = textBox.ID

    UpdateTotalValue(Val(textBox.Text))
End Sub
wdebmtf2

wdebmtf21#

这可能会很困难,因为如果你重新绑定gv,那么你会导致回发。即使引入了更新面板,我们仍然会得到一个叫做部分回发的东西。这意味着页面类被重新创建,页面加载再次启动,你的代码在后面运行。所以,虽然这看起来像是没有回发发生,这在某种程度上是在愚弄你。虽然更新面板确实/可以解决许多浏览器重新绘制的问题,但请记住,UP仍然是一种回发,只是一种有限的回发。(因此,页面生命周期和往返确实会发生-只是更有限,但不是"零")。
事实上,正确的说法是"部分回邮"。
您没有显示UpdateTotalValue代码的功能,但是如果它重新绑定了GV,那么您就有了一个正在被重新加载的GV。
如果你的onchange/update代码只修改了当前行,然后可能重新计算了最终的总和,但是没有重新绑定gv,那么这可能会工作得更好。
如果没有,那么我们可能不得不引入ajax,或者一些客户端js代码。
我想,这在很大程度上取决于UpdateTotal例程做什么。
如果你可以运行/得到gv的总数,插入一些总值(比如在gv的底部,而不是重新绑定gv,那么你可能没问题)。
然而,如果你重新绑定了gv,那么我们可能不得不尝试加入一些设置焦点的代码,但是这样的代码往往有点"hacky"。

编辑:因此,GV未被重新绑定

我成功地使用了两种方法。
《易》:
我所做的是在文本框上执行一个控件SetFocus。这意味着用户输入一个值,点击Tab,所有内容都更新了,但焦点返回到文本框。实际上,如果用户在GV中点击Tab,则Tab起作用-(几乎就像在Excel工作表中跳来跳去)。但是,如果他们输入/更改那个值,那么你会发现你必须点击Tab键2次。通常,用户并不在意,因为他们只改变了一个值,然后继续。但是,假设鼠标点击了下面几行的文本框,他们将不得不点击两次。
我建议以上,因为这是最少的努力和接近零代码更改您所拥有的。
然而,经过时间和乱搞尝试各种方法?
我发现使用一些客户端JavaScript会带来更好的用户体验,因此标签也不会搞砸。总的来说,代码总量大致相同,甚至可能比服务器端代码少一点。然而,不利的一面是我们现在必须编写JavaScript代码,这可能会很"痛苦",或者至少比你发布的Simple + Clean + Easy服务器端代码更难。
I'll later today post both samples as a vb.net gv. (set focus, and 100% client JavaScript. With the JavaScript, then that means we can dump the update panel. (not a huge deal, but it does reduce page post-backs, and the user experience is much better).

Edit2:设置焦点,100%服务器端代码。

好的,我们能做的就是在运行代码之后,重新设置控件的焦点。
在本例中,我们有2列(Number of Nights X Price)用于酒店房间列表。
因此,有2列需要"更新" CURRENT行的数学运算,然后当然还要更新GV底部的总计。
因此,我们有这样的标记:

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" 
    ShowFooter="True" 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="Nights">
            <ItemTemplate>
                <asp:TextBox ID="txtNights" runat="server" Text='<%# Eval("Nights") %>' Width="50px"
                    OnTextChanged="txtNights_TextChanged" AutoPostBack="true"
                    style="text-align:right">
                </asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Price" SortExpression="Price">
            <ItemTemplate>
                <asp:TextBox ID="txtPrice" runat="server" Text='<%# Eval("Price", "{0:f2}") %>' Width="70px"
                    style="text-align:right"
                    OnTextChanged="txtPrice_TextChanged"  AutoPostBack="true"
                        ></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Tamount" 
            ItemStyle-HorizontalAlign="Right"  FooterStyle-HorizontalAlign="Right" >
            <ItemTemplate>
                <asp:Label ID="lblAmount" runat="server" Text='<%# Eval("Tamount", "{0:f2}") %>'></asp:Label>
            </ItemTemplate>
                <FooterTemplate >
                <asp:Label ID="LblTotal" runat="server" Text="0"></asp:Label>
            </FooterTemplate>                        
        </asp:TemplateField>                
    </Columns>                
</asp:GridView>

要加载的代码如下:

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)
        Using cmdSQL As New SqlCommand(
            "SELECT * FROM tblHotelsA ORDER BY HOtelName", conn)
            conn.Open()
            Dim rstData As New DataTable
            rstData.Load(cmdSQL.ExecuteReader)
            GridView1.DataSource = rstData
            GridView1.DataBind()
            SumRows()
        End Using
    End Using

End Sub

我们有一套舞蹈要表演给萨姆看
这一点:

Sub SumRows()
    ' sum value - set footer total value
    Dim MyTotal As Double

    For Each gRow As GridViewRow In GridView1.Rows
        Dim lblAmount As Label = gRow.FindControl("lblAmount")
        MyTotal += Nz(lblAmount.Text, 0)
    Next

    Dim lblTotal As Label = GridView1.FooterRow.FindControl("lblTotal")
    lblTotal.Text = MyTotal.ToString("C2")

End Sub

好的,现在我们看到了这个:
我将跳过第一行(左右),然后更改一个值,注意光标确实停留在原地。
所以这个:

因此,技巧很简单,只需使用control.SetFocus。
因此,如果我更改"夜晚",则会将焦点设置为"价格"。
如果更改价格,我会将焦点设置为"下一行"和"夜"。
所以,这个代码:

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

    Dim tQty As TextBox = sender
    Dim gRow As GridViewRow = tQty.NamingContainer
    Call RowCalc(gRow)

    ' we were in Qty control - set focus to next (price) control
    Dim tPrice As TextBox = gRow.FindControl("txtPrice")
    tPrice.Focus()

    SumRows()

End Sub

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

    Dim tPrice As TextBox = sender
    Dim gRow As GridViewRow = tPrice.NamingContainer
    Call RowCalc(gRow)
    SumRows()

    ' we in Price - we jump to next row below (or cycle to first row)

    Dim gRowNext As GridViewRow = Nothing
    If gRow.RowIndex + 1 < GridView1.Rows.Count Then
        gRowNext = GridView1.Rows(gRow.RowIndex + 1)
    Else
        ' jump to top row
        gRowNext = GridView1.Rows(0)
    End If
    Dim tQty As TextBox = gRowNext.FindControl("txtNights")
    tQty.Focus()

End Sub

因为行计算是针对2个值的,所以我将其分解为一个名为rowcalc的例程(这样两个文本框都可以调用同一个例程)。
这个

Sub RowCalc(gRow As GridViewRow)

    Dim tQty As TextBox = gRow.FindControl("txtNights")
    Dim tPrice As TextBox = gRow.FindControl("txtPrice")
    Dim lblAmount As Label = gRow.FindControl("lblAmount")

    lblAmount.Text = Nz(tQty.Text, 0) * Nz(tPrice.Text, 0)

End Sub

所以,它不是完美的,但它不是所有的坏如果一个用户改变了一个值,但然后代替制表符决定使用鼠标,那么他们将在大多数情况下必须点击两次,因为失去焦点将触发我们的计算代码,然后设置焦点(强制用户再次点击,如果使用鼠标点击一行)。
不过,上面的效果还不错。
我出去休息一下,但是当我回来的时候,我们可以尝试/看看上面同样的JavaScript示例。js示例稍微好一点,因为我们根本没有使用post-back,所以焦点和单击(鼠标)不会变得混乱。

相关问题