4

I want to create an amount of controls dynamically based on a number the user enters into a textbox. This part I have working fine, but I also need the dynamically created textboxes to have event handlers of their own, so the user can enter a number into them, and have more controls created.

My problem is that the event handler I've setup for these controls to use does not fire. Maybe I'm not understanding the ASP.NET page life cycle correctly, but I am currently having the dynamic controls generated inside the OnInit event (before I was generating them inside the TextChanged event of the very first textbox, but switched to using the OnInit event based on advice from Oded I found here: Dynamically Added Event Handler Not Firing).

EDIT

I removed the code I originally posted, because this post would just be too long otherwise.

I'm going to post my entire .aspx code and the code behind here, so you guys know this is exactly what I'm looking at. Again, the dynamic TextBox that gets generated by this code, when text is changed inside it, does not fire the event handler that is bound to it, and it simply disappears. Interesting to note - I think a postback actually does occur when you change the text, but it doesn't fire the event handler...

ASPX front end:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div id="dynamicControlDiv">
            <asp:Label ID="lblEnter" runat="server" Text="Enter the amount of textboxes you want:"></asp:Label>
            <asp:TextBox ID="txtEnter" runat="server" AutoPostBack="true"></asp:TextBox>
            <asp:Label ID="lblConfirm" runat="server" Text=""></asp:Label>
        </div>
    </form>
</body>
</html>

CODE BEHIND:

Partial Class _Default Inherits System.Web.UI.Page

Dim numOfDesiredControls As Int16

Protected Sub txtEnter_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtEnter.TextChanged

    Try
        numOfDesiredControls = Convert.ToInt16(txtEnter.Text)
        If Not numOfDesiredControls = 0 Then
            For i As Int16 = 1 To numOfDesiredControls
                Dim txtTest As New TextBox
                txtTest.Text = "dynamicTextBox"
                txtTest.ID = "dynamicTextBox" + i.ToString
                txtTest.AutoPostBack = True
                Form.Controls.Add(txtTest)
                AddHandler txtTest.TextChanged, AddressOf dynamicEventHandler
            Next
        End If
    Catch ex As Exception

    End Try
End Sub

Protected Sub dynamicEventHandler(ByVal sender As Object, ByVal e As System.EventArgs)
    ' If the event handler gets fired, reflect this by changing the text of lblConfirm
    lblConfirm.Visible = True
    lblConfirm.Text = "Event handler fired!"
End Sub

End Class

Try making your own project with this code, and see if you get the same problems I am - the dynamically created TextBox posts back, but does not fire its event handler, and then the TextBox disappears from the page... Thank you again for the help!

Community
  • 1
  • 1
Nate Major
  • 77
  • 1
  • 9
  • This code should work fine. Are you sure that the loop is iterated and that you perform any change (and validate it) in the corresponding textboxes? – varocarbas Oct 25 '13 at 19:59
  • Yeah - I've stepped through the loop, goes through, generates the textboxes, but when I change the text in any of those textboxes, their textChanged event (which is the event above in my code), does not fire. The dynamic textboxes show up on the page...it's just that their event handlers don't fire. – Nate Major Oct 25 '13 at 20:18
  • Bear in mind that in ASP, the behaviour is different than in Winforms. This event is only triggered when a change is posted to the server (http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.textbox.textchanged.aspx), that is, when the user press enter, for example. – varocarbas Oct 25 '13 at 20:21
  • Your event-adding part is fine. The only reason for not working is not being called (or being called from the wrong place, as suggested by nothingisnecessary, a bit sad nick, BTW :)); or that you are not doing the right checks. – varocarbas Oct 25 '13 at 20:24
  • I should have clarified - I'm not expecting the textChanged event handlers to fire, obviously, unless the user changes the text in the control, at which point it goes back to the server to call the event handler. – Nate Major Oct 25 '13 at 20:25
  • Can you post the code that sets the value of numOfDesiredControls? – nothingisnecessary Oct 25 '13 at 20:25
  • Yep, working on that. Will update my original question. – Nate Major Oct 25 '13 at 20:41
  • Hey everyone - I have to go now, but will be checking back on this later. Thank you for the responses so far! – Nate Major Oct 25 '13 at 20:56
  • OK thanks for posting the code, I posted an updated solution. Sorry, I misspoke before (I think?) - you want to build the dynamic controls in `Load` instead of `Init` – nothingisnecessary Oct 29 '13 at 00:05
  • Does this answer your question? [Event won't fire to dynamically added control](https://stackoverflow.com/questions/56281364/event-wont-fire-to-dynamically-added-control) – Peter O. May 11 '20 at 00:40

2 Answers2

3

Dynamic controls can be a bit tricky because of ViewState

Basically: you were adding the dynamic controls in response to the TextChanged event, which fires after Init and Load and after the ViewState was deserialized. Therefore you were experiencing the problem because the ViewState was not aware of the dynamic controls and was out of sync with your expectations. By checking the value of txtEnter.Text in the Load phase and creating the controls there, you can make your ViewState aware of the controls (which, remember, are created EVERY time the page loads!), and therefore your dynamicEventHandler now has a context from which to execute.

Here is the corrected code (but as a single file with embedded VB for simplicity, you could of course separate this out into a codebehind file):

<%@ Page Language="VB" AutoEventWireup="false" %>

<script runat="server">
    Dim numOfDesiredControls As Int16

    Protected Sub Page_Load() Handles form1.Load

        Try
            numOfDesiredControls = Convert.ToInt16(txtEnter.Text)
            If Not numOfDesiredControls = 0 Then
                For i As Int16 = 1 To numOfDesiredControls
                    Dim txtTest As New TextBox
                    txtTest.Text = "dynamicTextBox"
                    txtTest.ID = "dynamicTextBox" + i.ToString
                    txtTest.AutoPostBack = True
                    ' txtTest.EnableViewState = False
                    Form.Controls.Add(txtTest)
                    AddHandler txtTest.TextChanged, AddressOf dynamicEventHandler
                Next
            End If
        Catch ex As Exception

        End Try
    End Sub

    Protected Sub dynamicEventHandler(ByVal sender As Object, ByVal e As System.EventArgs)
        ' If the event handler gets fired, reflect this by changing the text of lblConfirm
        Dim txt As TextBox
        txt = CType(sender, TextBox)
        lblConfirm.Visible = True
        lblConfirm.Text = "Event handler " + txt.Id + " fired: " + txt.Text ' append ID and text so we know which one fired it.
    End Sub
</script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div id="dynamicControlDiv">
            <asp:Label ID="lblEnter" runat="server" Text="Enter the amount of textboxes you want:"></asp:Label>
            <asp:TextBox ID="txtEnter" runat="server" AutoPostBack="true"></asp:TextBox>
            <asp:Label ID="lblConfirm" runat="server" Text=""></asp:Label>
        </div>
    </form>
</body>
</html>

Here is a starting point from Msft about dynamic controls that explains the behavior. There are a lot of resources out there for this.

http://msdn.microsoft.com/en-us/library/hbdfdyh7.aspx

Pro-tip: don't use dynamic controls if you can help it. While this is arguably a decent way to support browsers that do not use JavaScript, these days the preferred approach is to build the controls dynamically with JavaScript and use AJAX to do updates. Otherwise, you are sending way more data than you need to. When you use PostBack, every time you you POST back to the server you are (1) uploading the entire ViewState PLUS the form data to the server, (2) rebuilding the entire page (parsing ViewState, rendering HTML, etc.) on the server, and (3) sending all the HTML (and ViewState) back to the client. This is especially troublesome for mobile devices where power usage and data plan rates apply.

nothingisnecessary
  • 6,099
  • 36
  • 60
  • Thanks for the suggestion. That's what I was doing before I started using OnInit, but it still doesn't work. What's weird is I tried replacing the dynamic textbox with a dynamic button, and when I click the dynamically added button, the event handler I added for it doesn't fire, but the button disappears... I'm confused. – Nate Major Oct 25 '13 at 20:36
  • Can you post the full code? also - thinking you may have better luck doing this with a `Repeater` control and using `ItemCommand` handler – nothingisnecessary Oct 25 '13 at 21:04
  • Have never used the Repeater control before, but I am going to learn about it this morning. The issues I'm having, originally posted in this thread, are still bugging the crap out of me, though :\. – Nate Major Oct 28 '13 at 12:44
  • Sorry, can't help you any further unless you post the full .aspx and codebehind code... too many unknowns about what exactly you are doing. – nothingisnecessary Oct 28 '13 at 16:27
  • Gotcha. I'm going to post my entire .aspx page and code behind, if you're still willing to help. I'd certainly appreciate it! I'm editing the original post. – Nate Major Oct 28 '13 at 19:22
  • posted my full code above. You can try it and see what you get. – Nate Major Oct 28 '13 at 19:32
  • Thank you so much! This finally did it! Your explanation of ViewState and the Page Life Cycle makes sense. I realize this is not a very efficient method of creating dynamic controls, but this has been the beginning of my journey in finding the best way to do this. And this application will not be used by mobile users, but I will be looking into your suggestions regarding JavaScript and AJAX, because you're right - this amount of postbacks is clearly not efficient, especially for mobile devices. I'd upvote this answer if I could - but I don't have enough reputation :( – Nate Major Oct 29 '13 at 14:04
  • You bet! Normally I wouldn't get on my high horse and suggest a different approach, but I feel strongly that the time invested in learning AJAX and zero-postback methods are well worth it (especially since they will also work in non-ASP.NET environments). – nothingisnecessary Oct 29 '13 at 15:39
  • +1 for the Pro-tip. That's going to be a better approach than the solution involving programmatically POSTing and trying to wire up events to handle the POST that I was pursuing. – Jon Schneider Jul 02 '14 at 21:42
2

You will need to set the AutoPostback property of the dynamic textboxes to True for them to fire their TextChanged event.

Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
    MyBase.OnInit(e)

    For i As Int16 = 0 To 5
        Dim txtTest As New TextBox
        txtTest.Text = "this is an OnInit generated textbox"
        txtTest.ID = "testOnInit" + i.ToString
        txtTest.AutoPostBack = True  
        Form.Controls.Add(txtTest)
        AddHandler txtTest.TextChanged, AddressOf txtTest_TextChanged
    Next
End Sub

Protected Sub txtTest_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim txt As TextBox = DirectCast(sender, TextBox)
    lblTest.Text = txt.ID & " value changed to " & txt.Text
End Sub
Josh M.
  • 26,437
  • 24
  • 119
  • 200
ajakblackgoat
  • 2,119
  • 1
  • 15
  • 10
  • Thank you so much for pointing this out! Setting the AutoPostBack property to True does allow txtTest to respond to text changed. I have no idea how I forgot about this! However, just as when I used a button instead of a textbox, when I change text, the dynamically generated control (be it a button or a textbox) simply disappears. It does not fire the event handler assigned to it (I set a break point to confirm - event handler was not executed). – Nate Major Oct 28 '13 at 12:34
  • I'm wondering - event handlers for controls not generated dynamically have "handles " at the end of their method definition. All the examples I've seen on dynamic controls/event handlers show to leave that part out. Obviously, the event doesn't know what control it's handling if the control doesn't exist yet, so if you try to define it, .NET will see this and not allow the program to run. Am I supposed to be declaring the event handler method itself differently? – Nate Major Oct 28 '13 at 12:35