TRULY Understanding Dynamic Controls (Part 1)

Part 1: Dynamic vs. Static
Part 2: Creating Dynamic Controls
Part 3: Adding Dynamic Controls to the Control Tree
Part 4: Because you don't know to render at design time

Just like ViewState, dynamic controls seem to be fodder for much debate, and a source of many confusing issues. This article will be the first of a multi-part series to detail just about everything you could ever want to know about how Dynamic Controls fit in with the framework.

ASP.NET has been out for half a decade. Maybe this article is a little late in the making. But with all of the new technologies coming down the pipe (e.g., [Atlas]), I thought it would be nice to get back to the basics. After all, not all of us have been in the game since the beginning :)

Actually, this article should probably be titled "TRULY Understanding ASP.NET". Because as you will see, dynamic controls really aren't that special. In learning how they fit in with the page life cycle, and how they are similar to static controls, you will gain an understanding of how ASP.NET works in general.

I decided to write this article as a direct consequence of the article I wrote on ViewState. Over the course of a few months I received dozens upon dozens of comments on that article from developers needing help. They assumed the trouble they were having was a ViewState related issue. But 75% of the time, it was actually a dynamic control issue!

Part of the problem is that so many developers think they need to create controls dynamically when there's often much more elegant or easier solutions. There's a tendency for developers to try and do "too much" themselves, and that leads to complex problems that seem to have no good solution. It's frustrating and discouraging. I've been there myself -- but I've learned that, almost always, when I find myself in that situation it's because I'm failing to see a bigger picture or I'm failing to approach the problem in an "asp.net way". I hope this article helps those who are experiencing the same thing.

I also decided to break this article up into multiple episodes, because the ViewState article is quite long, and I think this article is going to be even longer. Maybe if I give it to you in more bite size pieces it will be easier to read, no?

PART 1


 

Dynamic vs. Static: What's the difference?

Normally you "declare" controls on a form via markup and the ubiquitous runat="server" attribute. Like this:

<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="MyPage.aspx.cs" Inherits="Infinity.Examples.MyPage" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>TRULY Understanding Dynamic Controls</title>
</head>
<body>
    <form id="form1" runat="server">
        Your name: <asp:TextBox ID="txtName" runat="server" Text="Enter your name" />
    </form>
</body>
</html>

That TextBox is a "static" control. Static controls are declared in an xml-like syntax in ASPX or ASCX files. When ASP.NET parses that markup, it generates a class on the fly that does the dirty work for you -- one by one, the code creates the controls and adds them to the control tree. The TextBox declared above results in the following auto-generated code:

private global::System.Web.UI.WebControls.TextBox @__BuildControltxtName() {
    global::System.Web.UI.WebControls.TextBox @__ctrl;
 
    #line 11 "c:\projects\Truly\MyPage.aspx"
    @__ctrl = new global::System.Web.UI.WebControls.TextBox();
 
    #line default
    #line hidden
    this.txtName = @__ctrl;
    @__ctrl.ApplyStyleSheetSkin(this);
 
    #line 11 "c:\projects\Truly\MyPage.aspx"
    @__ctrl.ID = "txtName";
 
    #line default
    #line hidden
 
    #line 11 "c:\projects\Truly\MyPage.aspx"
    @__ctrl.Text = "Enter your name";
 
    #line default
    #line hidden
    return @__ctrl;
}

The code may look scary, but it's just a really careful way of creating a TextBox, setting the ID and Text properties, and returning it. It has to be careful because it's possible that user code define things that make change the intended purpose. The #line pragmas are so the compiler can alert you to any compile errors and still give you the correct line number in the declared markup, rather than the line number of the generated code (say thank you to the compiler next time that happens -- its a great feature).

There will be one of these "build control" methods for each control you statically declare on the page. If a control exists inside another control, then the parent control's "build control" method will add them to it's own control collection. In this example, the form control contains three child controls:

private global::System.Web.UI.HtmlControls.HtmlForm @__BuildControlform1() {
    global::System.Web.UI.HtmlControls.HtmlForm @__ctrl;
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__ctrl = new global::System.Web.UI.HtmlControls.HtmlForm();
 
    #line default
    #line hidden
    this.form1 = @__ctrl;
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__ctrl.ID = "form1";
 
    #line default
    #line hidden
    System.Web.UI.IParserAccessor @__parser = ((System.Web.UI.IParserAccessor)(@__ctrl));
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n  Your name: "));
 
    #line default
    #line hidden
    global::System.Web.UI.WebControls.TextBox @__ctrl1;
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__ctrl1 = this.@__BuildControltxtName();
 
    #line default
    #line hidden
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(@__ctrl1);
 
    #line default
    #line hidden
 
    #line 10 "c:\projects\Truly\MyPage.aspx"
    @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n    "));
 
    #line default
    #line hidden
    return @__ctrl;
}

The child controls are:

  1. LiteralControl containing the straight-up text "Your name:"
  2. The txtName TextBox
  3. Another LiteralControl containing the whitespace before the end of the form tag.

Notice to create the TextBox control, this method calls that control's "build method" that we just looked at:

global::System.Web.UI.WebControls.TextBox @__ctrl1;
#line 10 "c:\projects\Truly\MyPage.aspx"
@__ctrl1 = this.@__BuildControltxtName.@__BuildControltxtName();

And notice this strange method it calls, passing the TextBox:

@__parser.AddParsedSubObject(@__ctrl1);

Because of the fact the "object" being added is a control, all that method does is call Controls.Add(@__ctrl1), thus adding the TextBox to the control tree (I say "because of the fact it is a control", because non-controls can be added as well, but how they are handled is unique to each control type).

This isn't shown in this example, but haven't you ever wondered why you declare control variables as protected? Why not private? The class that is auto-generated inherits from your code-behind (that's why you need an "inherits" attribute in the page directive). It's up to this code to create the declared controls and assign them to the variables you declare (if it exists) so that you can easily access them. Since it is derived from your code-behind class, if the variable were private, it couldn't do that!

So that is how the framework adds declared controls into the control tree. A dynamic control in the traditional sense is one that you as the page or control developer create at some point during the page lifecycle, and add to the static control tree yourself. Something like this:

protected override void OnInit(EventArgs e) {
    TextBox txtName = new TextBox();
    txtName.ID = "txtName";
    txtName.Text = "Enter your name";
    this.form1.Controls.Add(txtName);
 
    base.OnInit(e);
}

This code is not nearly as scary looking as the auto-generated code from the page parser. However, it does the same thing!

So you see, this whole silly ASPX-markup-runat-server-thing exists solely to make it easier on you to create a control tree! That's all it's there for! If it didn't exist, you could still achieve all the same functionality by creating the controls and setting their properties yourself, just like the auto-generated code does! When I first realized this, it was a eureka moment for me. So if you don't feel the same way, either you don't understand what I'm saying, or I'm crazy when compared to you. Maybe it's a little of both :)

This really blurs the line between static and dynamic controls, as it should. What you think of as a static control, is really just a control that is being dynamically created by the framework instead of by you. But neither the control or the framework really care who creates it -- they can participate in the page lifecycle one and the same, and both the control and the page it sits on don't even know the difference.

But wait!

If you have ever dealt with dynamic controls before, you know that they really are different. It's true that your experience with them may differ from that of static controls, but it's important to understand exactly why there's a difference. The difference has to do with when the control enters the control tree. When the framework adds controls, it does it extremely early in the event sequence. The only thing that happens first is the control or page constructor. That's why in OnInit or OnPreInit, the controls already exist and are ready for use (well, master pages can 'mess' with that process, but this is still a true statement).

But when you dynamically create a control, you don't have the ability to do it as early as the framework does. And that has consequences...

Stay tuned. :)

22 Comments

  • I&#39;m a regular poster on AspMessageboard and while there are various specific issues, I have found that many people&#39;s issues are due to willful ignorance. Some decide that a dynamic table is better than a repeater &quot;just because&quot;.
    Shouldn&#39;t you use CreateChildControls instead of OnInit for the sample?

  • Good starting article for the series. I really enjoyed the viewstate article too.
    more more more!

  • AndrewSeven -- ignorance perhaps, but I know exactly what you are talking about. In one of the parts (I haven't got it all planned out completely yet :P) I'm going to detail real world situations people have been in where they were doing exactly what you describe, where a repeater or other databound control would have been much better. Willful? I don't know... I'm still holding out hope that once they see the light, they will switch :)

    Thanks.

  • I mean &quot;willful&quot; in the sense that they made a concious decision to go with what worked in asp classic (or other) rather than look for a better way.
    Kind of like chosing DataReaders instead of DataSets because the &quot;feel&quot; of readers is much closer to that of an Ado recordset.
    A built in mechanism that would permit some dynamic control persistence (like what Denis Baur created http://www.denisbauer.com/ASPNETControls/DynamicControlsPlaceholder.aspx ) could be a nice addition. :D
    Early on (1.0) a fair number of people expected that behavior from the asp:placeholder control. :S

  • I've seen the Dynamic Placeholder... but I consider it evil :) Because you're forcing ViewState to remember things that don't need to be remembered. If the framework had this built in, ViewState would have to store not only the state of the controls on the page, but the page itself. Yikes. Dynamic Placeholder has its uses, but its always favorable to go the extra mile and recreate the controls every request yourself.

  • Just my luck. I&#39;m stuck on some stupid dynamic controls issue, and I&#39;ve got to wait who knows how long before I get an explaination as to what may be going wrong.
    That said, I am really looking forward to the rest of this article.

  • Jonathan -- why don't you send me the details of your issue and I'll see if I can't help? Perhaps it will be a good example that I could use in the episodes to come.

  • As always, excellent stuff! Keep it up.

  • I want to make a checkbox that once checked automatically opens a textbox and another checkbox with the same code to do it again. Can you please help me.

  • Good Article to learn the state of the controls during creation in aspx page dynamically ...

  • I have a grid in usercontrol, i want to handle the selectedindexchanged of the grid in the parent page.
    I add the usercontrol to a webPart dynamically and want to bind the event handling.
    Gridx.gridSelectionChanged += new EventHandler(MainPage_gridxSelectionChanged);
    but it didnt work.

  • Hello hello -- you can add the event handler from the main page, you'll just have to do a FindControl on the user control to get to the grid it contains, then hook into the handler on that.

  • I think it to add a row when i click the button of page .but it does't work
    protected System.Web.UI.WebControls.TableRow newRow()
    {
    System.Web.UI.WebControls.TableRow myr = new TableRow();
    System.Web.UI.WebControls.TableCell mycell1 = new TableCell();
    System.Web.UI.WebControls.TableCell mycell2 = new TableCell();
    mycell2.Text = "sdfsdf";

    myr.Cells.Add(mycell1);
    myr.Cells.Add(mycell2);
    return myr;

    }

    protected void Button1_Click(object sender, EventArgs e)
    {
    System.Web.UI.WebControls.TableRow myrow = this.newRow();
    this.Table1.Rows.Add(myrow);


    }

  • Nice Shot...........:)

  • Ох ты ебанаврот сколько всего написанО!!!

  • P.S. I forgot to add that I am using ASP.Net 1.1 with VB.Net

  • Reading articles like this (good one)
    I really have a good reason to hate "MSDN website"

  • Hi

    I have a web user control with dropdowns and listboxes populated dynamically with in a table. I define the control type based on the results from the database.Also the number of controls is defind in the database.

    The user control exposes two methods :

    BuildTable();

    GetSelectedValues();

    I consume this in another control.

    I realized that the web control has to be built everytime in the parent control's page load function using the BuildTable() method, as it get destroyed after every request.

    There is a save button in the parent control which calls the GetSelectedValues() method to get the selected values from the dropdowns and listboxes. What I dont understand is that the dynamic table in the user control is built every time in the page load of parent control but it still remembers the selected values in the dropdowns and listboxes ?

    I tried setting the EnableViewState for the dropdowns and listboxes to false to check if viewstate is maintaining the state.

    Am I missing something in the life cycle of the user control or the way it is consumed ?

    I have read you article but I need to read it again to understand it thoroughly but I wanted to know the answer to my question as I wanted to make sure I understand the details of the page life cycle, if i'm missing any

    Please let me know.

    Thanks

    kiran

  • Hi,

    I Have a requirement like this...

    I am my from i have 2 textboxes side by side.

    Below that I have one link button named as AddMore.

    When i click on AddMore(link button) I need to get another 2 text boxes below of above textboxes.

    And in Code Behind I need to take id's of those dynamically created textboxes.

    Please Help me.

    Thanks
    hasini

  • hasini: Do you really need to create those two textboxes dinamically?

  • yes i need to create those two textboxes dynamically...
    Pls help me..
    HOw to enter the obtained values in the database.


  • Thats quite a nice break down of dynamic controls. Though it did inspire me to write a post which solves a lot more peoples problems using a viewstate and following a few golden rules normally gets it done :)

Comments have been disabled for this content.