From the Client, with Data

Let ASP.NET Handle Controls Created on the Client

CoreCoder

LANGUAGES: C# | VB.NET

ASP.NET VERSIONS: 1.x | 2.0

 

From the Client, with Data

Let ASP.NET Handle Controls Created on the Client

 

By Dino Esposito

 

ASP.NET pages are made of controls that output markup to the browser. An invisible thread ties a server-side control with a subtree of HTML elements created on the client. The browser exposes these elements to client-side script code through its own document object model (DOM). What happens if the user interacts with the client code and modifies the state of some elements? Are these changes reflected to the server and update the state of corresponding server-side controls? To properly address this question, you need to know a little more about how ASP.NET generates pages and processes requests.

 

Understanding the Postback Mechanics

When you request a page, the ASP.NET runtime instantiates a server-side class that represents the requested ASPX resource. This class inherits directly or indirectly from Page and is a collection of controls inside a form. Which controls? The page parser analyzes the source of the .aspx resource and maps any tags with the runat=server attribute to a particular control. At the end of the request processing, any control added to the page s collection is required to generate its own markup. It is mandatory that each server-side control has a unique ID. If you omit the ID, either Visual Studio.NET at design time, or the ASP.NET machinery at run time, figure out a valid and unique ID for that control.

 

The role of the control ID is essential because it represents the aforementioned invisible thread that ties together a server-side control with its own client-side markup. To understand the mechanics of ASP.NET postbacks, consider what happens with a TextBox control. The rendering of the TextBox control generates the following markup for the browser:

 

 name="TextBox1" />

 

On the client, the user enters some text and modifies the value attribute to Some more text . Then, it clicks a button to post back. On the server, the ASP.NET runtime first instantiates all controls found in the .aspx source code that instantiates these controls is hardcoded in the dynamically created page class. TextBox1 is instantiated at this stage, before the page receives the Init event. Next, if there s any viewstate associated with the request, it is restored and passed to the various controls so they can rebuild the state they had when the page was last served. Next, the ASP.NET runtime passes to process the data posted with the HTTP request. The body of the HTTP packet is similar to this:

 

TextBox1=Some more text

 

The ASP.NET runtime invokes an internal method of the TextBox control to give it a chance to process posted data. The TextBox takes the posted value (the string Some more text in the example) and updates its Text property. At this point, the state of the TextBox1 control on the server reflects perfectly the client-side changes. The initialization step ends with the Load event being fired to the page object.

 

Dynamic Server Controls

The procedure previously described can t work if the control to restore doesn t exist in the .aspx source code; that is, the control is dynamically created by some piece of code. Imagine your page has a push button to let users create a textbox. The code would resemble the following snippets:

 

(C#)

void Button1_Click(object sender, EventArgs e)

{

 TextBox txt = new TextBox();

 txt.ID = "TextBox1";

 txt.Text = "Some text";

 Form1.Controls.Add(txt);

}

 

(VB)

Sub Button1_Click(sender As Object, e As EventArgs)

 Dim txt As New TextBox

 txt.ID = "TextBox1"

 txt.Text = "Some text"

 Form1.Controls.Add(txt)

End Sub

 

When the page that has created the TextBox1 control is rendered to the browser, TextBox1 is part of the markup and its state is saved to the viewstate. The end user sees the textbox and can work with it as usual. Imagine that the user changes the text to Some more text and posts back. What happens on the server? The HTTP packet correctly contains the new value of the TextBox1 control:

 

TextBox1=Some more text

 

Likewise, the viewstate for TextBox1 exists because it was saved when the page was last served. The issue is that there s no living instance of TextBox1 on the server to update with viewstate and posted data.

 

Instances of server-side controls are normally created by the page class that serves the request. In this case, though, the tree of controls created by the page object doesn t include the TextBox1 control, because that control is not referenced in the .aspx source. ASP.NET recognizes the potential problem and makes a second attempt to process posted data after the Load event. If you could recreate the control during Page_Load, then ASP.NET will find it during the second try and restore viewstate and posted data for it. How do you know that an instance of a certain control must be manually recreated for the sake of postback? No choice; you have to take note of that. Typically, your code writes a value to the page s viewstate immediately after adding the control to the form:

 

(C#)

void Button1_Click(object sender, EventArgs e)

{

 TextBox txt = new TextBox();

 txt.ID = "TextBox1";

 txt.Text = "Some text";

 Form1.Controls.Add(txt);

 ViewState["TextBox1"] = "yes";

}

 

(VB)

Sub Button1_Click(sender As Object, e As EventArgs)

 Dim txt As New TextBox

 txt.ID = "TextBox1"

 txt.Text = "Some text"

 Form1.Controls.Add(txt)

 ViewState("TextBox1") = "yes"

End Sub

 

In Page_Load, you check the viewstate entry and recreate the control, if needed:

 

(C#)

void Page_Load(object sender, EventArgs e)

{

 object o = ViewState["TextBox1"];

 if (o != null)

 {

    TextBox txt = new TextBox();

    txt.ID = "TextBox1";

    txt.Text = "Some text";

    Form1.Controls.Add(txt);

 }

}

 

(VB)

Sub Page_Load(sender As Object, e As EventArgs)

 Dim o As Object = ViewState("TextBox1")

 If Not(o Is Nothing) Then

    Dim txt As New TextBox

    txt.ID = "TextBox1"

    txt.Text = "Some text"

    Form1.Controls.Add(txt)

 End If

Sub

 

The Add method on the ControlCollection class automatically restores the viewstate for any added control, if any. Following the Page_Load event, the ASP.NET runtime makes its second try to process posted data for the TextBox1 control. At this point, the TextBox1 control (dynamically created at some point in the page execution) is fully integrated in the page lifecycle, as if it were part of the original .aspx source code. I covered this topic in greater detail in Manage Dynamically Created Controls.

 

Dynamic Client Controls

Of all the possible changes that a control can undergo on the client, only a small subset is automatically reflected on the server. For example, if the user writes some new text in a textbox, that text is reflected with no extra costs. What if, instead, the user changes the max length or makes it read-only? There s no way this information can make it to the server without some made-to-measure code that provides for that. Another scenario that is worth mentioning is this: What if a new control is dynamically created on the client? Let s tackle this case first, then we ll get back to figure out ways to carry additional changes to the server.

 

The page shown in Figure 1 provides a client button to create a couple of textboxes using a piece of JavaScript code. Figure 2 shows the page in action. What do you think will happen when the page posts back? It s fairly easy to guess; the two textboxes are lost and disappear. Buy why?

 

<% @Page Language="C#" %>

 "CreateClientForm()" />

Figure 1A: Client-side code to create textboxes (C#).

 

<% @Page Language="VB" %>

 "CreateClientForm()" />

Figure 1B: Client-side code to create textboxes (VB).

 


Figure 2: The textboxes are created dynamically by JavaScript code.

 

The ASP.NET server environment knows nothing about the two client-side textboxes and certainly can t do much about them. Interestingly enough, though, the browser sends in the HTTP packet the text the user typed:

 

FirstName=[FirstName];LastName=[LastName]

 

If it were good old Active Server Pages, you would have had no hard time. The following code, in fact, works just fine:

 

<% = Request("FirstName") %>

 

The Request object, in ASP as well as in ASP.NET, is a simplified name/value interface for the values posted by the browser. No matter how the textboxes get created, they re definitely part of the posting client form. As such, their contents are sent and can be consumed on the server through the Request dictionary. This would be sufficient for result pages that simply need to get some input. In ASP.NET, you typically work with re-entrant pages that continuously post to themselves; for this reason, any dynamically created controls must become part of the page s tree, on the client as well as the server.

 

As you do for controls dynamically created on the server, the server environment also needs a little help from the code in this case. Figure 3 completes the code of Figure 1 to work out a functional page where controls created via JavaScript code are added to the ASP.NET page s control tree for the duration of the user interaction.

 

<% @Page Language="C#" %>

 "CreateClientForm()" />

Figure 3A: Controls created on the client are persisted on the server (C#).

 

<% @Page Language="VB" %>

 "CreateClientForm()" />

Figure 3B: Controls created on the client are persisted on the server (VB).

 

In the Page_Init event, the sample code checks the existence of controls whose ID matches that of dynamically created controls. If no such controls are found, the page creates and adds them to the page s tree. The newly created server controls are assigned the same text as the client elements.

 

Note that the sample code doesn t do anything to prevent users from repeatedly creating pairs of textboxes. If you do that, you end up with two or more client textboxes with the same name. This will create a problem when posting back because the contents of the HTTP packet take the following form:

 

FirstName=[FirstName],[FirstName];LastName=

  [LastName],[LastName]

 

Arrays of controls with the same name are allowed on the client, but are not supported by ASP.NET. Be aware of this if you happen to write pages that create new controls via JavaScript.

 

Passing Other Data

Changes applied on the client that go beyond the information automatically exchanged through the browser require an additional mechanism and protocol to be brought to the server. What if your JavaScript code adds a new item to a listbox or sets the max length property of a textbox? All this information can certainly be conveyed to the server, but only through a custom procedure that you have to design and implement. A possible approach entails using a hidden field to transport data to the server. Your JavaScript code is responsible for writing data to the hidden field whenever a change that must be tracked occurs. The format of the data written to the hidden field is up to you. Here s a suggestion:

 

TextBox1.MaxLength=10;DropDownList1.NewItem=

 Foo;DropDownList1.NewItem=Bar;

 

Needless to say, you need some code on the server to process the contents of the hidden field and apply changes to the involved server controls.

 

All the source code shown here assumes that the server knows the names of the controls being created on the client. This is a safe assumption to make in most cases, but not all. If users can determine the ID of new controls interactively, then the ID must be passed to the server too. This is also information that goes serialized to the hidden field in a format that you decide.

 

Conclusion

Creating controls dynamically is possible in ASP.NET both on the server and the client. The ASP.NET infrastructure is aware of this possibility and does all that it can to simplify your job. However, for a full and effective integration between dynamically created controls and the server-side ASP.NET object model, the programmer needs to write some code of his own. This article has provided an overview of the most commonly used techniques.

 

Dino Esposito is a trainer and consultant who specializes in ASP.NET and ADO.NET. Author of Programming Microsoft ASP.NET and Introducing ASP.NET 2.0, both from Microsoft Press, Dino also helped several companies architect and build effective products for ASP.NET developers. Dino is the cofounder of http://www.DotNet2TheMax.com, a popular portal for .NET programmers. Write to him at mailto:[email protected] or join the blog at http://weblogs.asp.net/despos.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish