Event Handling with Dynamically Created Controls
Prologue
Not too long ago, I set forth to rewrite some code that I had created and add some additional functionality. I had created a management interface for some photos that we display on our website. The way I originally created it was to have only one level of categories and then each category could have photos. The display page would list each of the categories and all the thumbnails of the photos in the category. Well, I needed to change it to support multiple levels of categories. (Each category was a folder so I needed to support nested folders). For some reason, I was reticent to use querystrings to help so I was attempting to create a breadcrumb but have the click event of the breadcrumb link do the work. I had some interesting results that I probably should have expected, but for some reason didn't. I thought I'd share my insights on handling events for dynamically created controls.
Problem
I wanted to dynamically create a control, then handle it's events. In that event, I wanted to generate another control (additional bread crumbs) and handle that new control's events. I found that I had a devil of a time trying to get the page to respond to the control's events. I finally figured out why. The solution portion is really just an experiment, I finally decided to use a querystring and save myself the hassle of trying to do it the other way (good choice on my part). The point boils down to this. If you don't create the control on page load, it doesn't exist to respond to events so they never get run. Keeping track of all the controls and trying to recreate them on load needed to be done somehow, and I found that querystring was by far easier for me, thus the switch to querystring.
Solution
For this experiment, we just need one page. You can use an existing website if you like, just create a new web form and name it dynamControls.aspx. Set it up by just adding a panel to the page and naming the panel pnlHoldsStuff. Now let's switch to the code behind.
A little setup here will make our code cleaner when we get to the experiment, so we're going to add a helper function to create and return a dynamic control. So, add to your code the following function:
Private Function CreateLinkButton(ByVal vsID As String, _
ByVal vsText As String, _
Optional ByVal vsCssClsass As String = "") As LinkButton
Dim lb As New LinkButton
lb.ID = vsID
lb.Text = vsText
lb.CssClass = vsCssClsass
Return lb
End Function
Basically, this creates a linkbutton control. We will pass all the details such as Id and linktext in and get back a linkbutton control. This will make it much cleaner as we work later, as we don't have to have all the gory details every time we create one in our experiment code.
Ok, let's get started. In our page_load, let's add a new hyperlink control and then we'll tell the system to handle events for it. So, in the page_Load event, add the following code:
If Not IsPostBack Then
Dim lbTemp As LinkButton = _
CreateLinkButton("lnk1", "my 1st dynam control")
AddHandler lbTemp.Click, AddressOf linkbutton_click
pnlHoldsStuff.Controls.Add(lbTemp)
End If
If the page isn't posting back, then we're going to create a new linkbutton add a handler for it, and then add the linkbutton to the panel on the webpage. You may have to change the width of the panel so that it doesn't wrap out text, but that's not a big concern. Now we need a sub to handle the button click, so add the following code to after your page load subroutine:
Private Sub linkbutton_click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("I got clicked")
End Sub
Earlier we added a handler to our linkbutton, this is the handler. When clicked, our page should add "I got clicked" to the page. Run your page, click the linkbutton and see how it works. Hmm... our response.write didn't seem to work. Put a break point on your handler sub and try it again. You'll see that we never get our handler subroutine to run. There's a simple reason for this (unfortunately it took me like 4 hours to figure it out). We specified that the page_load stuff only runs when NOT posting back, when we click on the linkbutton, it launches a post back, so the page_load stuff DOESN'T run. This means that the linkbutton doesn't get created, and as such, the handler doesn't get added and so there's nothing to catch the event. Now comment out the if then statement in the page_load event and try it again. Viola, it works.
I got a little more complex when I did it, I wanted the event handler to actually create another linkbutton and add a handler for it too. So let's modify our code. In our linkbutton_click sub, add the following below the Response.write line:
Dim lb As LinkButton = CreateLinkButton("lnk2", _
"my 2nd dynam control")
AddHandler lb.Click, AddressOf linkbutton_click
pnlHoldsStuff.Controls.Add(lb)
So when we click on our linkbutton, we'll create a new linkbutton and add it to the page. The theory is that when we click on the 2nd link button, it will again run this subroutine. Run your project, click on the linkbutton and see if it creates a second one. It should. Now click on the 2nd linbutton and see what happens... it disappears and the I got clicked message goes away too. So what happened? Well, right back to the previous answer. We don't create the 2nd control in the page_load, so it doesn't exist, so it can't run an event handler. So somehow, we have to record what buttons were created dynamically, and recreate them every time the page is loaded. UGH. Realistically I gave up.
Epilogue
So what did I do? Well, my original intention was to try to do all the dynamic buttons exclusively through code. I found it to be too much work, so I finally just added a querystring then I didn't have to add event handlers, I put all the info into the querystring, and then used that to generate hyperlinks on the next page. The hyperlinks could then just be links to the same page with a new querystring. I know, I'm a quitter, but what I found is that realistically I was trying way to hard to do something that I could do easier and much cleaner another way. So I did it the other way. I also learned a valuable lesson about dynamically created controls and event handling.
Final code for this page is as follows:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
'If Not IsPostBack Then
Dim lbTemp As LinkButton = _
CreateLinkButton("lnk1", "my 1st dynam control")
AddHandler lbTemp.Click, AddressOf linkbutton_click
pnlHoldsStuff.Controls.Add(lbTemp)
'End If
End Sub
Private Function CreateLinkButton(ByVal vsID As String, _
ByVal vsText As String, _
Optional ByVal vsCssClsass As String = "") As LinkButton
Dim lb As New LinkButton
lb.ID = vsID
lb.Text = vsText
lb.CssClass = vsCssClsass
Return lb
End Function
Private Sub linkbutton_click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Response.Write("I got clicked")
Dim lb As LinkButton = CreateLinkButton("lnk2", _
"my 2nd dynam control")
AddHandler lb.Click, AddressOf linkbutton_click
pnlHoldsStuff.Controls.Add(lb)
End Sub