More on Adding Controls Dynamically
Peter returns to last week's topic to go beyond the basics of how to add controls to a page to fully integrate it into your Page's lifecycle.
In my most recent column
, I discussed tactics for adding controls at runtime to a page, but I didn't discuss when you should do this or how to enable catching events from these controls. Before answering that question, it's worthwhile to provide some background information first. I'll walk through the lifecycle of an ASP.NET page from the time that your control is added until you access the data that the user has entered into the control.
I'll start by assuming that you've added a control using one of the techniques that I discussed either in the Page's PreRender event or some event that fires before that event. After the PreRender event, ASP.NET hands off the HTML for the page to IIS, which is responsible for getting the page back to the user. Once the page appears in the browser, the user can interact with it, probably filling in some of the controls that you've added with data.
Eventually, the user clicks on a Submit button (or triggers an AutoPostBack). At that point, the browser -- regardless of what browser is displaying the page -- gathers up all the data from the page and sends it back to the server with the name of the control that the data was displayed in. Assuming that you're not using cross page posting, this data is sent to the server with a request for the page currently displayed in the browser.
Back at the server, IIS finds the requested page. The page first rebuilds all the controls defined in the page's aspx file, which means that any controls added dynamically at run time by your code aren't added. The page then slots the data that came up from the browser into the controls, matching the control name sent up from the browser with the control's ID property. To get the data slotted into your control, therefore, you need to add your control to the page at the right moment.
The Page class has a method set aside specifically for adding controls to the page: the CreateChildControls method. ASP.NET guarantees that, no matter what mutations the Page class goes through as Microsoft extends and enhances it, the CreateChildControls method will be called at the right point in the Page's lifecycle for everything to work out for you. You just need to override the method and add your code. If you wire up an event handler to your control (e.g. a TextChanged event) in this method, the events for your control will also be fired correctly.
However, let's go in time to the point where you added the control to the page in the first place. In order to decide if you should add the control, you probably had to check the user's input: some selections the user made or some data they entered or some button they clicked. But, remember, you don't have access to any of that data until after the data is loaded into the control and, while you may not know exactly when the CreateChildControls method is called, the method has to be called before the data can be put into the control. So you need to add the control for the first time after the CreateChildControls method has been called and add the control on all subsequent occasions in the the CreateChildControls method.
The solution is to first write a method that adds your control (and, optionally, wires up any events you want to process). You then call that in some event between the Page Load and the PreRender event to add the control to the page for the first time. You should also set a flag that indicates that the control has been added. Since you only need this flag inside the page, you should store the flag in the ViewState. Then, in the CreateChildControls method you can check for that flag and, it's set, call the method to add the control back.
In the following code, I've assumed that in the Page Load event I'll check to see if a RadioButton is set to decide if a control is to be added. If the RadioButton is checked, I'll call my method that adds a TextBox control (which also wires up the TextChanged event) and sets a flag in the ViewState. In the CreateChildControls method, I check for the flag and call the same method to put the control back in place to catch the data being sent from the browser. I've also included the code to remove the control when the RadioButton isn't selected:
Dim txt As TextBox
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Me.IsPostBack = True Then
If Me.rdbAddControl.Checked Then
If Me.ViewState("TextBoxAdded") <> "true" Then
Private Sub AddTextBox()
txt = New TextBox
txt.ID = "AddedTextBox"
Me.ViewState("TextBoxAdded") = "true"
AddHandler txt.TextChanged, _
Private Sub RemoveTextBox()
If Me.ViewState("TextBoxAdded") = "true" Then
Me.ViewState("TextBoxAdded") = "false"
RemoveHandler txt.TextChanged, _
Protected Overrides Sub CreateChildControls()
If Me.ViewState("TextBoxAdded") = "true" Then
Private Sub AddedTextBox_TextChanged( _
ByVal sender As Object, ByVal e As System.EventArgs)
To simplify using the control, I declared it as a field at the top of the class. Normally, I'm opposed to using class-level declarations. But the regular controls are, effectively, declared at the class level, so I'm just following this pattern.
And that really does wrap up my exploration adding a control dynamically to page at runtime!
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.