7. ASP Server Components - 1
7.1. Introduction
In this chapter, we describe the technology recommended in ASP.NET for building the user interface. We know that there are two distinct phases in the web server’s processing of an .aspx page:
- First, the page controller is executed. This consists of code located either within the .aspx page itself (WebMatrix solution) or in a separate file (Visual Studio.NET solution).
- Then the presentation code of the .aspx page is executed and converted into HTML code sent to the client.

ASP.NET offers three tag libraries for writing the page’s presentation code:
- Classic HTML tags. This is what we have been using so far.
- server-side HTML tags
- Web Forms tags
Regardless of which tag library is used, the role of the page controller remains the same. It must calculate the value of the dynamic parameters appearing in the presentation code. Up until now, these dynamic parameters were simple: they were objects of type [String]. So if in the presentation code we have a <%=name%> tag:
- the page controller declares a [name] variable of type [String] and calculates its value
- when the page controller has finished its work and the presentation code is executed to generate the HTML response, the <%=name%> tag is replaced by the value calculated by the controller code
We know that the controller/view separation is arbitrary and that controller code and view code can be mixed on the same page. We have explained why this approach is discouraged, and we will continue to adhere to the controller/view separation.
The [server HTML] and [WebForms] tags allow you to include objects in the presentation code that are more complex than the simple [String] object. This can sometimes be very useful. Let’s take the example of a form with a list. This list must be presented to the client with HTML code that looks like this:
<select name="uneListe" size="3">
<option value="opt1">option1</option>
<option value="opt2">option2</option>
<option value="opt3" selected>option3</option>
</select>
The list content and the option to be selected are dynamic elements and must therefore be generated by the page controller. We have already encountered this problem and solved it by including the tag
This tag will be replaced by the [String] value of the [uneListeHTML] variable. This value, calculated by the controller, must be the HTML code for the list, i.e., "<select name=..>...</select>". This isn’t particularly difficult to do and seems like an elegant solution that avoids putting generation code directly in the page’s presentation layer. Here, there would be a loop with tests to be inserted into it, which would thus be heavily "cluttered." Nevertheless, this method has a drawback. The separation of control and presentation in a page also serves to delineate two areas of responsibility:
- that of the .NET developer, who handles the page controller,
- that of the graphic designer, who handles the presentation part of the page
Here, we see that the generation of the HTML code for the list has been moved to the controller. The graphic designer may want to modify this HTML code to change the "visual" appearance of the list. They would be forced to work in the [controller] section and thus step outside their area of expertise, with the associated risk of inadvertently introducing errors into the code.
Server tag libraries solve this problem. They provide an object representing an HTML list. For example, the [WebForms] library offers the following tag:
This tag represents a [ListBox] object that can be manipulated by the page controller. This object has properties to represent the various options in the HTML list and to designate the selected option. The page controller will therefore assign the appropriate values to these properties. When the presentation layer is executed, the tag
will be replaced by the HTML code representing the [uneListe] object, i.e., the code "<select ..>...</select>". For now, there is no fundamental difference from the previous method other than an object-oriented coding approach, which is interesting. Let’s return to our graphic designer who needs to modify the “look” of the list. Server tags have style attributes (BackColor, Bordercolor, BorderWidth, ...) that allow you to set the visual appearance of the corresponding HTML object. So we can write:
<asp:ListBox id="ListBox1" runat="server" BackColor="#ffff99"></asp:ListBox></P>
The advantage is that the graphic designer works directly within the presentation code to make these changes. This is a clear advantage over the previous method. Server-side tag libraries thus simplify the construction of the presentation layer of web pages—what we have referred to in previous chapters as the user interface. The purpose of this chapter is to introduce them. We will see that they sometimes offer complex objects such as calendars or tables linked to data sources. They are extensible, meaning that the user can create their own tag library. They can thus create a tag that generates a banner on a page. All pages using this tag will then have the same banner.
The HTML generated for a tag adapts to the type of client browser. When the browser sends a request to the web server, it includes an [User-Agent: xx] header among its HTTP headers, where [xx] identifies the client. Here is an example:
With this information, the web server can determine the client’s capabilities, particularly the type of HTML code it can handle. Over time, there have indeed been several versions of the HTML language. Modern browsers support the latest versions of the language, whereas older browsers do not. Based on the HTTP header [User-Agent:] sent by the client, the server will send back an HTML version that the client can understand. This is a useful and practical approach because it means the developer doesn’t have to worry about the specific type of client browser used by their application.
Finally, advanced IDEs such as Visual Studio.NET, WebMatrix, etc., allow for a "Windows-style" design of the web interface. While these tools are not essential, they provide significant assistance to the developer. The developer designs the web interface using graphical components that they place on the interface. They have direct access to the properties of each interface component, which they can configure as desired. These properties are translated into the interface’s HTML presentation code as attributes of the component’s <asp:> tag. The benefit for the developer is that they do not have to memorize either the list or the syntax of each tag’s attributes. This is a significant advantage when one is not fully familiar with the server tag libraries offered by ASP.NET. Once this syntax is mastered, some developers may prefer to code the tags directly into the page’s presentation code without going through the graphical design phase. An IDE is then no longer necessary; a simple text editor suffices. Depending on your workflow, the focus is then on components (using an IDE) or tags (using a text editor). These two terms are equivalent. A component is the object that will be manipulated by the page’s control code. The IDE gives us access to its properties during the design phase. The values assigned to these properties are immediately translated into the component’s tag attributes in the presentation code. During the runtime phase, the page’s control code will manipulate the component and assign values to some of its properties. The presentation code will generate the component’s HTML code by using, on the one hand, the attributes set during design for the corresponding server tag, and on the other hand, the values of the component’s properties calculated by the control code.
7.2. The execution context of the examples
We will illustrate the design of web interfaces based on server components using programs whose execution context will mostly be as follows:
- the web application will consist of a single page P containing a form F,
- the client will make its first request directly to this page P. This will involve requesting the URL of page P using a browser. It is therefore a GET request that will be made to this URL P. The server will deliver page P and thus the form F it contains,
- the user will fill it out and submit it, i.e., they will perform an action that forces the browser to submit form F to the server. The browser’s POST operation will always be directed at page P. The server will again deliver page P with form F, the contents of which may have been modified by the user’s action.
- Then steps 2 and 3 will resume.
This is a very specific execution process, outside of which certain concepts discussed below no longer function. We are no longer in an MVC architecture context where a multi-page application is controlled by a specific page we have called the "application controller." In this type of architecture, form POSTs target the controller and not the forms themselves. However, we will see that building a form with server-side components implies that this form is posted to itself.
7.3. The Label Component
7.3.1. Usage
The <asp:label> tag allows you to insert dynamic text into a page’s presentation code. It therefore does no more than the <%=variable%> tag used up to now. Studying this first tag will help us understand how server tags work. We’ll create a page with a control section [form1.aspx.vb] and a presentation section [form1.aspx]. The goal is to display the time:

This issue was already covered in Chapter 2, and the reader is encouraged to refer to it if they wish to know how it was handled. The presentation code [form1.aspx] is as follows:
<%@ page src="form1.aspx.vb" inherits="form1" AutoEventWireup="false" %>
<HTML>
<HEAD>
<title>Web Forms</title>
</HEAD>
<body>
<asp:Label Runat="server" ID="lblHeure" />
</body>
</HTML>
We are introducing the <asp:label> tag. In tag libraries, the [runat="server"] attribute is required. The ID attribute identifies the component. The controller must reference it using this identifier. The controller code [form1.aspx.vb] is as follows:
Imports System.Web.UI.WebControls
Public Class form1
Inherits from System.Web.UI.Page
Protected lblTime As Label
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Init
' saves the current request to request.txt in the page's directory
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' sets the time in lblHeure
lblTime.Text = "It is " + Date.Now.ToString("T")
End Sub
End Class
The controller must assign a value to the [lblHeure] object of type [System.Web.UI.WebControls.Label]. All objects displayed by <asp:> tags belong to the [System.Web.UI.WebControls] namespace. Therefore, we can systematically import this namespace:
Imports System.Web.UI.WebControls
The [Label] object has various properties, including the [Text] property, which represents the text that will be displayed by the corresponding <asp:label> tag. Here, we set this property to the current time. We do this in the controller’s [Form_Load] procedure, which is always executed. In the [Form_Init] procedure, which is also always executed but before the [Form_Load] procedure, we store the client’s request in a [request.txt] file in the application folder. We will have the opportunity to examine this file to understand certain aspects of how pages using server tags work.
The [Label] object has many properties, methods, and events. The reader is encouraged to consult the documentation on the [Label] class to explore them. This will be the case throughout the rest of the book. For each tag, we present only the few properties we need.
7.3.2. Tests
We place the files (form1.aspx, form1.aspx.vb) in a folder <application-path> and launch Cassini with the parameters (<application-path>,/form1). Then we request the URL [http://localhost/form1/form1.aspx]. We get the following result:

The HTML code received by the browser is as follows:
<HTML>
<HEAD>
<title>Webforms</title>
</HEAD>
<body>
<span id="lblHeure">It is 7:39:37 p.m.</span>
</body>
</HTML>
We can see that the server tag
<asp:Label Runat="server" ID="lblHeure" />
has been converted into the following HTML code:
It is the [Text] property of the [lblHeure] object that has been placed between the <span> and </span> tags. The request made by the client and stored in [request.txt] is as follows:
GET /form1/form1.aspx HTTP/1.1
Cache-Control: max-age=0
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
Nothing out of the ordinary.
7.3.3. Building the application with WebMatrix
We built the presentation code for the [form1.aspx] page by hand. This method is useful if you know the tags. A simple text editor is then sufficient to build the user interface. To get started, a graphical design tool combined with automatic code generation is often necessary because you don’t know the syntax of the tags you need . We will now build the same application using the WebMatrix tool. Once WebMatrix is launched, we select the [File/New File] option:

We create an ASP.NET page named [form2.aspx]. After confirming the previous wizard, we see the design window for the [form2.aspx] page:


Note that WebMatrix places both the page’s control code and presentation code in a single file, in this case [form2.aspx]. The [All] tab displays the contents of this text file. We can already see that it is not empty:

The downside of this type of tool is that it often generates unnecessary code. This is the case here, where WebMatrix has generated an HTML <form> tag even though we aren’t going to build a form... Additionally, we can see that the document lacks a <title> tag. We’ll fix both of these issues right away to get the following updated version:

What we call the controller code will be inserted between the <script> and </script> tags, ensuring at least a visual separation between the two types of code: control and presentation. We return to the [Design] tab to design our interface. A list of components is available in a tool window to the left of the design window:

The tool window provides access to two types of components:
- [WebControls] components, which translate to <asp:> tags
- [HTML Elements] components, which translate to standard HTML tags. However, you can add the [runat="server"] attribute to an HTML tag’s attributes. In this case, the HTML tag and its attributes are accessible to the controller via an object whose properties correspond to those of the HTML tag it represents. We previously referred to these tags as server-side HTML tags.
Double-click the [Label] component in the [WebControls] list. In the [Design] tab, you get the following result:

In the [All] tab, the code has become the following:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="Label1" runat="server">Label</asp:Label>
</body>
</html>
First, you’ll notice that the <script> tag is gone. An <asp:label> tag has been generated. It has a name [Label1] and a value [Label]. Let’s go back to the [Design] tab to change these two values. Click once on the [Label] component to bring up its properties window in the bottom-right corner:

The reader is invited to review the properties of the [Label] object. Two of them are of interest here:
- Text: this is the text the label should display—we set the string to empty (i.e., nothing)
- ID: this is its identifier—we set it to lblHeure
The [Design] tab now looks like this:

and the code for [All] becomes:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>
The layout of the page is complete. We still need to write the control code responsible for setting the time in the [Text] property of [lblHeure]. We add the following code in the [All] tab:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' sets the time in lblHeure
lblHeure.Text = "It is " + Date.Now.ToString("T")
End Sub
</script>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblTime" runat="server"></asp:Label>
</body>
</html>
Note that in the controller code, the [lblHeure] object is not declared as it was previously:
Protected lblHeure As New System.Web.UI.WebControls.Label
In fact, all <asp:> server components in the presentation layer are implicitly declared in the controller code. Declaring them explicitly causes a compilation error, indicating that the object is already declared. We are ready to run the code. We select the [View/Start] option or press the [F5] shortcut. Cassini launches automatically with the following parameters:

We accept these values. The system’s default browser automatically opens to request the URL [http://localhost/form2.aspx]. We get the following result:

From now on, we will mainly use the WebMatrix tool to facilitate the development and testing of the short programs we will write.
7.4. The Literal Component
7.4.1. Usage
The <asp:literal> tag allows you to insert dynamic text into a page’s presentation code, similar to the <asp:label> tag. Its main attribute is [Text], which represents the text that will be inserted as-is into the page’s HTML flow. This tag is sufficient if you do not intend to format the text you want to insert into the HTML flow. In fact, while the [Label] class allows for formatting using attributes such as [BorderColor, BorderWidth, Font, ...], the [Literal] class has none of these attributes. The reader can reproduce the previous example in its entirety by replacing the [Label] component with a [Literal] component.
7.5. The Button component
7.5.1. Usage
The <asp:Button> tag allows you to insert a [Submit]-type button into a form, which brings with it event handling similar to that found in Windows applications. This is the point we want to explore further here. We create the following [form3.aspx] page:

This page, built with WebMatrix, has three components:
No. | name | type | properties | role |
1 | Button | text=Button1 | submit button | |
2 | Button | text=Button2 | submit button | |
3 | Label | text= | information message |
The code generated by WebMatrix for this section is as follows:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<
<asp:Button id="Button1" runat="server" Text="Button1"></asp:Button>
<asp:Button id="Button2" runat="server" Text="Button2"></asp:Button>
<
<asp:Label id="lblInfo" runat="server"></asp:Label>
</form>
</body>
</html>
We see the [Button] and [Label] components used in the page’s graphical design within <asp:> tags. Note the <form runat="server"> tag, which was generated automatically. This is a server-side HTML tag—that is, a standard HTML tag represented by an object that can be manipulated by the controller. The HTML code for the <form> tag will be generated based on the value the controller assigns to this object.
Let’s add the [Page_Init] procedure to the controller section of the code, which handles the page’s [Init] event. We’ll place the code there that saves the client’s request to the [request.txt] file. We’ll need this to understand how the buttons work.
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' saves the current request to request.txt in the page's directory
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
</script>
Note that we did not include the [Handles MyBase.Init] clause after the [Page_Init] procedure declaration. This is because the [Init] event of the [Page] object has a default handler named [Page_Init]. If we use this handler name, the [Handles Page.Init] clause becomes unnecessary. However, including it does not cause an error.
7.5.2. Testing
We run the application in WebMatrix by pressing [F5]. We get the following page:

The HTML code received by the browser is as follows:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button2" value="Button2" id="Button2" />
<span id="lblInfo"></span>
</form>
</body>
</html>
Note the following points:
- the <form runat="server"> tag has been converted to an HTML tag
Two attributes have been set: [method="post"] and [action="form3.aspx"]. Can we assign different values to these attributes? We will try to clarify this point a little later. Note here that the form will be submitted to the URL [form3.aspx]. Two other attributes [name, id] have also been set. Most of the time, they are ignored. However, if the page contains browser-side JavaScript code, the [name] attribute of the <form> tag is useful.
- The <asp:button> tags have become HTML [submit] button tags. Clicking any of these buttons will therefore trigger a "post" of the [_ctl10] form to the URL [form3.aspx].
- The <asp:label> tag has become an HTML <span> tag
- A hidden field [__VIEWSTATE] has been generated with a strange value:
This field represents, in an encoded form, the state of the form sent to the client. This state represents the value of all its components. Since [__VIEWSTATE] is part of the form, its value will be posted along with the rest to the server. This will allow the server to know which form component has changed its value and, if necessary, make decisions. These decisions will take the form of events such as "the TextBox with such-and-such a name has changed its value."
7.5.3. Client requests
Once the [form3.aspx] page has been loaded in the browser, let’s request it again and examine the request sent by the browser to retrieve it. Recall that our application stores this in the [request.txt] file within the application folder:
GET /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
This is a standard GET request. Now let’s click the [Button1] button on the page in the browser. Nothing seems to happen. However, we know that the form has been submitted. The page’s HTML code confirms this. This is confirmed by the new content of [request.txt]:
POST /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 80
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form3.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button1=Button1
The first HTTP header clearly indicates that the client sent a POST request to the URL [/form3.aspx]. The last line shows the posted values:
- the value of the hidden field __VIEWSTATE
- the value of the button that was clicked
If we click on [Button2], the values posted by the browser are as follows:
The value of the hidden field is always the same, but it is the value of [Button2] that has been posted. The server can therefore determine which button was used. It will use this to trigger an event that can be handled by the page once it has loaded.
7.5.4. Handling the Click Event of a Button Object
Let’s review how an .aspx page works. It is an object derived from the [Page] class. Let’s call the derived class [aPage]. When the server receives a request for such a page, an object of type [unePage] is instantiated via the operation new unePage(...). The server then generates two events called [Init] and [Load] in that order. The [unePage] object can handle these by providing the [Page_Init] and [Page_Load] event handlers. Other events will be generated later. We’ll have the opportunity to revisit them. If the client’s request is a POST, the server will generate the [Click] event for the button that triggered this POST. If the [unePage] class has provided a handler for this event, it will be called. Let’s explore this mechanism with WebMatrix. In the [Design] tab of [form3.aspx], double-click the [Button1] button. We are then automatically taken to the [Code] tab, inside the body of a procedure called [Button1_Click]. To better understand, let’s go to the [All] tab and look at the entire code. The following changes have been made:
<%@ Page Language="VB" %>
<script runat="server">
...
Sub Button1_Click(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
<form runat="server">
...
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Button1"></asp:Button>
</form>
</body>
</html>
A new attribute [onclick="Button1_Click"] has been added to the <asp:Button> tag for [Button1]. This attribute specifies the procedure responsible for handling the [Click] event on the [Button1] object, in this case the [Button1_Click] procedure. All that remains is to write it:
Sub Button1_Click(sender As Object, e As EventArgs)
' Click on Button 1
lblInfo.Text="You clicked [Button1]"
End Sub
The procedure displays an informational message in the [lblInfo] label. We proceed in the same way for the [Button2] button to obtain the following new page [form3.aspx]:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' saves the current request to request.txt in the page's directory
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub Button1_Click(sender As Object, e As EventArgs)
' Click on Button 1
lblInfo.Text = "You clicked [Button1]"
End Sub
Sub Button2_Click(sender As Object, e As EventArgs)
' Click on Button 2
lblInfo.Text = "You clicked [Button2]"
End Sub
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Button1"></asp:Button>
<asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Button2" BorderStyle="None"></asp:Button>
<
<asp:Label id="lblInfo" runat="server"></asp:Label>
</form>
</body>
</html>
We press [F5] to run the code and get the following page:

If we look at the HTML code received, we will see that it has not changed from the previous version of the page:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button2" value="Button2" id="Button2" />
<span id="lblInfo"></span>
</form>
</body>
</html>
If we click on [Button1], we get the following response:

The HTML code received for this response is as follows:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFZvdXMgYXZleiBjbGlxdcOpIHN1ciBbQm91dG9uMV07Pj47Pjs7Pjs+Pjs+Pjs+4oO98Vd244kj0lPMXReWOwJ1WW0=" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button1" value="Button1" id="Button1" />
<input type="submit" name="Button2" value="Button2" id="Button2" />
<span id="lblInfo">You clicked [Button1]</span>
</form>
</body>
</html>
We can see that the hidden field [__VIEWSTATE] has changed its value. This reflects the change in value of the [lblInfo] component.
7.5.5. Events in the Lifecycle of an ASP.NET Application
The ASP.NET documentation lists the events generated by the server during the life cycle of an application:
- When the application receives its very first request, the [Start] event on the application’s [HttpApplication] object is generated. This event can be handled by the [Application_Start] procedure in the application’s [global.asax] file.
Next, we will have a series of events that will repeat for each request received:
- if the request did not send a session token, a new session is started and a [Start] event on the [Session] object associated with the request is generated. This event can be handled by the [Session_Start] procedure in the application’s [global.asax] file.
- The server generates the [BeginRequest] event on the [HttpApplication] object. It can be handled by the [Application_BeginRequest] procedure in the application’s [global.asax] file.
- The server loads the page requested by the request. It will instantiate a [Page] object and then generate two events on this object: [Init] and then [Load]. These two events can be handled by the [Page_Init] and [Page_Load] procedures of the page.
- Based on the received posted values, the server will generate other events: [TextChanged] for a [TextBox] component whose value has changed, [CheckedChanged] for a radio button whose value has changed, [SelectedIndexChanged] for a list whose selected item has changed, ... We will have the opportunity to mention the main events for each of the server components we will present. Each event E on an object named O can be handled by a procedure named O_E.
- The order in which the preceding events are handled is not guaranteed. Therefore, handlers must not make any assumptions about this order. However, we can be certain that the [Click] event of the button that triggered the POST is processed last.
- Once the page is ready, the server will send it to the client. Before doing so, it generates the [PreRender] event, which can be handled by the page’s [Page_PreRender] procedure.
- Once the HTML response has been sent to the client, the page will be unloaded from memory. Two events will be generated at this time: [Unload] and [Disposed]. The page can use these events to free up resources.
The application can also receive events outside of a client request:
- The [End] event on an application [Session] object occurs when a session ends. This can happen either upon an explicit request from a page’s code or because the session’s lifetime has expired. The [Session_End] procedure in the [global.asax] file handles this event. It typically releases resources obtained in [Session_Start].
- The [End] event on the application's [HttpApplication] object occurs when the application terminates. This happens, for example, when the web server is shut down. The [Application_End] procedure in the [global.asax] file handles this event. It is typically used to release resources acquired in [Application_Start].
The following points should be noted:
- The previous event model relies on standard client-server HTTP exchanges. This is clearly evident when examining the exchanged HTTP headers.
- The processing of the previous events always takes place on the server side. Clicking a button can, of course, be handled by a server-side JavaScript script. However, this is not a server event, and we are dealing with a technology independent of ASP.NET.
When are events processed, whether on the server side (events related to server components) or on the browser side by JavaScript scripts?
Let’s take the example of a drop-down list. When the user changes the selected item in it, the event (change of the selected item) may or may not be processed, and if it is processed, it can be processed at different times.
- If you want to process it immediately, there are two solutions:
- It can be handled by the browser using a JavaScript script. The server does not intervene in this case. For this to be possible, the page must be able to be reconstructed using values present on the page.
- It can be processed by the server. For this, there is only one solution: the form must be sent to the server for processing. We therefore have a [submit] operation. We will see that in this case, we use a server component called [DropDownList] and set its [AutoPostBack] attribute to [true]. This means that if the selected item in the drop-down list changes, the form must be immediately posted to the server. In this case, the server generates HTML code for the [DropDownList] object that is associated with a JavaScript function responsible for performing a [submit] as soon as the "selected item changed" event occurs. This [submit] will post the form to the server, and hidden fields will be included in the post to indicate that the [post] resulted from a selection change in the drop-down list. The server will then generate the [SelectedIndexChanged] event, which the page can handle.
- If you want to handle it but not immediately, set the [AutoPostBack] attribute of the [DropDownList] server component to [false]. In this case, the server generates the standard HTML code for a <select> list for the [DropDownList] object, without an associated JavaScript function. Nothing happens when the user changes the selection in the drop-down list. However, when the user submits the form using a [submit] button, for example, the server will be able to recognize that a selection change has occurred. We have indeed seen that the form sent to the server contains a hidden field [__VIEWSTATE] that represents, in encoded form, the state of all elements in the submitted form. When the server receives the new form posted by the client, it can verify whether the selected item in the drop-down list has changed. If so, it will trigger the [SelectedIndexChanged] event, which the page can then handle. To distinguish this mechanism from the previous one, some authors say that the "selection change" event is "cached" when it occurs in the browser. It will only be processed by the server when the browser posts the form to it, often following a click on a [submit] button.
- Finally, if you do not wish to handle the event, set the [AutoPostBack] attribute of the [DropDownList] server component to [false] and do not write the handler for its [SelectedIndexChanged] event.
Once the event-handling mechanism is understood, the developer will not design a web application like a Windows application. Indeed, while a selection change in a combo box within a Windows application can be used to immediately update the appearance of the form containing it, one is more likely to hesitate to handle this event immediately in a web application if it involves a form "post" to the server—and thus a round-trip between the client and the server. This is why the [AutoPostBack] property of server-side components is set to [false] by default. Furthermore, the [AutoPostBack] mechanism, which relies on JavaScript scripts automatically generated by the web server in the form sent to the client, can only be used if one is certain that the client’s browser has enabled the execution of JavaScript scripts. Forms are therefore often constructed as follows:
- the form’s server-side components have their [AutoPostBack] property set to [false]
- the form has one or more buttons responsible for performing the [POST] operation
- in the page’s controller code, you write handlers for only the events you want to handle, most often the [Click] event on one of the buttons.
7.6. The TextBox component
7.6.1. Usage
The <asp:TextBox> tag allows you to insert an input field into a page’s presentation code. We create a page [form4.aspx] to achieve the following layout:

4321This page, built with WebMatrix, has the following components:
No. | Name | type | properties | role |
1 | TextBox | AutoPostback=true Text= | input field | |
2 | TextBox | AutoPostback=false Text= | input field | |
3 | Label | text= | Information message about the contents of [TextBox1] | |
3 | Label | text= | Information message about the contents of [TextBox2] |
The code generated by WebMatrix for this section is as follows:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
Text 1:
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox>
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:
Text 2:
<asp:TextBox id="TextBox2" runat="server"></asp:TextBox>
<
<asp:Label id="lblInfo1" runat="server"></asp:Label>
<asp:TextBox id="TextBox2" runat="server"></asp:TextBox>
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</form>
</body>
</html>
On the [Design] tab, double-click the [TextBox1] component. The skeleton of the [TextChanged] event handler for this object is then generated (on the [All] tab):
<%@ Page Language="VB" %>
<script runat="server">
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
...
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
....
</form>
</body>
</html>
The attribute [OnTextChanged="TextBox1_TextChanged"] has been added to the <asp:TextBox id="TextBox1"> tag to specify the handler for the [TextChanged] event on [TextBox1]. We are now writing the [TextBox1_Changed] procedure.
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' text change
lblInfo1.Text = Date.Now.ToString("T") + ": [TextChanged] event on [TextBox1]. Text 1 = [" + TextBox1.Text + "]"
End Sub
In the procedure, we write a message in the [lblInfo1] label indicating the event and showing the contents of [TextBox1]. We do the same for [TextBox2]. We also include the time to better track event handling. The final code for [form4.aspx] is as follows:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' saves the current request to request.txt in the page's directory
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' Text change
lblInfo1.Text = Date.Now.ToString("T") + ": [TextChanged] event on [TextBox1]. Text 1 = [" + TextBox1.Text + "]"
End Sub
Sub TextBox2_TextChanged(sender As Object, e As EventArgs)
' text change
lblInfo2.text = Date.Now.ToString("T") + ": [TextChanged] event on [TextBox2]. Text 2 = [" + textbox2.Text + "]"
End Sub
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
Text 1:
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
Text 2:
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
<
<asp:Label id="lblInfo1" runat="server"></asp:Label>
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</form>
</body>
</html>
We have added the [Page_Init] procedure to store the client's request, as in the previous example.
7.6.2. Testing
We launch the application in WebMatrix by pressing [F5]. We get the following page:

The HTML code received by the browser is as follows:
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
Text 1:
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" id="TextBox2" />
<
<span id="lblInfo1"></span>
<
<span id="lblInfo2"></span>
</form>
</body>
</html>
There are many elements in this code that were automatically generated by the server. Note the following points:
- There are three hidden fields: [__VIEWSTATE], which we have already encountered, [__EventTarget], and [__EventArgument]. The latter two fields are used to handle the browser's "change" event on the input field [TextBox1]
- The <asp:textbox> server tags generated the HTML tags <input type="text" ...> that correspond to the input fields
- the server tag <asp:textbox id="TextBox1" AutoPostBack="true" ...> generated an <input type="text" ...> tag with an attribute [onchange="__doPostBack('TextBox1','')"]. This attribute specifies that if the content of [TextBox1] changes, the JavaScript function [_doPostBack(...)] must be executed. It is as follows:
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
What does the function above do? It assigns a value to each of the two hidden fields [__EventTarget] and [__EventArgument], then submits the form. The form is therefore sent to the server. This is the [AutoPostBack] effect. The browser "change" event triggers the execution of the code "__doPostBack('TextBox1','')". We can deduce that in the submitted form, the hidden field [__EventTarget] will have the value 'TextBox1' and the hidden field [__EventArgument] will have the value ''. This allows the server to identify the component that triggered the POST.
- The server tag <asp:textbox id="TextBox2"...> generated a standard <input type="text" ...> tag because its [AutoPostBack] attribute was not set to [true].
- The <form> tag indicates that the form will be posted to [form4.aspx]:
Let’s run our first test. Type some text into the first input field:

then move the cursor to the second input field. Immediately, a new page appears:

What happened? When the cursor left the first input field, the browser checked whether its content had changed. It had. So, on the browser side, it triggered the [change] event on the HTML field [TextBox1]. We then saw that a JavaScript function executed and posted the form to [form4.aspx]. This page was then reloaded by the server. The values submitted by the form allowed the server to recognize that the content of the server-side [TextBox1] tag had changed. The [TextBox1_Changed] procedure was therefore executed on the server side. It placed a message in the [lblInfo1] label. Once this procedure finished, [form4.aspx] was sent back to the browser. This is why we now have text in [lblInfo1]. That said, it may seem surprising that there is anything in the [TextBox1] input field. Indeed, no server-side procedure assigns a value to this field. This is a general mechanism of ASP.NET web forms: the server returns the form in the state in which it received it. To do this, it reassigns to the components the value that was posted for them by the client. For some components, the client posts no value. This is the case, for example, with <asp:label> components, which are rendered as HTML <span> tags. Recall that the form has a hidden field [__VIEWSTATE] that represents the form’s state when it is sent to the client. This state is the sum of the states of all the form’s components, including any <asp:label> components. Since the hidden field [__VIEWSTATE] is posted by the client browser, the server is able to restore the previous state of all the form’s components. All that remains is to modify those whose values have been changed by the POST request.
Let’s now look in [request.txt] at the request made by the browser:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 137
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form4.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=first+text&TextBox2=
We can clearly see the POST request and the parameters being sent. Let’s go back to our browser and enter some text in the second input field:

Let’s go back to input field #1 to enter new text: nothing happens this time. Why? Because the [TextBox2] input field does not have its [AutoPostBack] property set to [true], so the <input type="text"...> tag generated for it does not handle the [Change] event, as shown in its HTML code:
Therefore, no event occurs when you leave input field #2. Now let’s enter new text into field #1:

Let’s leave input field #1. Immediately, the [Change] event on this field is detected, and the form is posted to the server, which returns the following page:

What happened? First, the browser submitted the form. This is reflected in the client request stored in [request.txt]:
POST /form4.aspx HTTP/1.1
....
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPDU%2BOz47bDx0PHA8cDxsPFRleHQ7PjtsPHByZW1pZXIgdGV4dGU7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPDE4OjQyOjI5OiBldnQgW1RleHRDaGFuZ2VkXSBzdXIgW1RleHRCb3gxXS4gVGV4dGUgMT1bcHJlbWllciB0ZXh0ZV07Pj47Pjs7Pjs%2BPjs%2BPjs%2BxLOermpUUUz5rTAa%2FFsjda6lVmo%3D&TextBox1=third+text&TextBox2=second+text
The server begins by restoring the components to their previous values using the hidden field [__VIEWSTATE] that the client sent to it. Using the posted fields [TextBox1] and [TextBox2], it assigns the values that were posted to the components [TextBox1] and [TextBox2]. It is through this mechanism that the client retrieves the form as it was posted. Then, again using the posted fields [__VIEWSTATE], [TextBox1], and [TextBox2], the server detects that the values of the input fields [TextBox1] and [TextBox2] have changed. It therefore triggers the [TextChanged] events for these two objects. The [TextBox1_TextChanged] and [TextBox2_TextChanged] procedures will be executed, and the [labelInfo1] and [labelInfo2] labels will receive new values. Then the modified [form4.aspx] page is sent back to the client.
Now let’s modify input field #1 again:

When we move the cursor out of field 1, the [Change] event occurs in the browser. Then the sequence of events already explained (post from the browser to the server, ..., sending the server’s response) takes place. We get the following response:

Because of the timestamp displayed for each message, we can see that only the [TextBox1_Changed] procedure was executed on the server. The [TextBox2_TextChanged] procedure was not executed because the value of [TextBox2] did not change. Finally, let’s enter new text into field #2:

Then place the cursor on field #1 and move it back to field #2. The page does not change. Why? Because we are not changing the value of field #1, the browser [Change] event does not occur when we leave that field. Consequently, the form is not submitted to the server. Therefore, nothing changes on the page. It is the fact that the content of [lblInfo2] does not change that shows us there is no POST. If there were one, the server would detect that the content of [TextBox2] has changed and should reflect this in [lblInfo2].
The lesson from this example is that there is no point in setting the [AutoPostBack] property of a [TextBox] to [true]. This causes an unnecessary client-server round trip most of the time.
7.6.3. The Role of the __VIEWSTATE Field
We have seen that the server systematically places a hidden field called __VIEWSTATE in the form it generates. We noted that this field represents the form’s state and that if this hidden field is returned to the server, it can reconstruct the form’s previous value. A form’s state is the sum of the states of its components. Each component has an [EnableViewState] property with a Boolean value indicating whether or not the component’s state should be placed in the hidden [__VIEWSTATE] field. By default, this property is set to [true], meaning that the state of all components in a form is placed in [__VIEWSTATE]. Sometimes, this is not desirable.
Let’s run a few tests to better understand the role of the [EnableViewState] property. Let’s set this property to [false] for both input fields:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True" EnableViewState="False"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged" EnableViewState="False"></asp:TextBox>
...
Now let’s run the application and type some text into field #1 before moving to field #2. A POST request is then sent to the server, and we get the following response:

Type some text into field #2, modify the text in field #1, then return to field #2 (in that order). A new POST request is sent to the server, and we receive the following response:

For now, everything is as before. Now let’s modify the content of field #1 and then move to field #2. A new POST request is sent. The server’s response is as follows:

This time, there is a change. The server detected a [TextChanged] event on field #2 because the value of [lblInfo2] was modified. However, there was no actual change. This is due to the [EnableViewState=false] property of [TextBox2]. This causes the server not to store the previous state of [TextBox2] in the form’s [__VIEWSTATE] field. This is equivalent to assigning the empty string as the previous state. When the POST triggered by the change in the content of [TextBox1] occurred, the server compared the current value of [TextBox2], which was [two], to its previous value (the empty string). It concluded that [TextBox2] had changed its value and generated the [TextChanged] event for [TextBox2]. We can verify this behavior by entering an empty string into [TextBox2]. Based on what has just been explained, the server should not generate the [TextChanged] event for [TextBox2]. Let’s try it:

That is exactly what happened. The time and the content of [lblInfo2] show that the [TextBox2_TextChanged] procedure was not executed. With that in mind, let’s examine the [EnableViewState] property of the four form components:
We want to preserve the state of this component so that the server knows whether or not it has changed | |
same | |
We do not want to preserve the state of this component. We want the text to be recalculated with every new POST. If it is not recalculated, it must be empty. All of this is achieved with [EnableViewState=false] | |
same |
Our home page now looks like this:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
....
<asp:Label id="lblInfo1" runat="server" enableviewstate="False"></asp:Label>
...
<asp:Label id="lblInfo2" runat="server" enableviewstate="False"></asp:Label>
...
Let's run the same series of tests as before. Where we got the screen
version 1

, we now get:
version 2

In this step, we changed the content of field 1 without changing that of field 2, ensuring that the [TextBox2_TextChanged] procedure was not executed on the server side, which meant that the [lblInfo2] field did not receive a new value. It is therefore displayed with its previous value. In version 1 [EnableViewState=true], this previous value was the entered value. In version 2 [EnableViewState=false], this previous value is the empty string.
It is sometimes unnecessary to preserve the previous state of components. Rather than setting [EnableViewState=false] for each of them, you can specify that the page should not maintain its state. This is done in the [Page] directive of the presentation code:
In this case, regardless of the value of its [EnableViewState] property, a component’s state is not stored in the hidden field [__VIEWSTATE]. Everything then behaves as if its previous state were the empty string.
Let’s now use the [curl] client to highlight other mechanisms. First, we request the URL [http://localhost/form4.aspx]:
dos>curl --include --url http://localhost/form4.aspx
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Sun, 04 Apr 2004 17:51:14 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1077
Connection: Close
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
Text 1:
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" id="TextBox2" />
<
<span id="lblInfo1"></span>
<
<span id="lblInfo2"></span>
</form>
</body>
</html>
We receive the HTML code for [form4.aspx] from the server. It is no different from the one the browser received. Recall here the request made by the browser when it submitted the form:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
...
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=first+text&TextBox2=
Let's perform the same POST but without sending the [__VIEWSTATE] field:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=first+text --data TextBox2=
...................
Text 1:
<input name="TextBox1" type="text" value="first text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" id="TextBox2" />
<span id="lblInfo1">19:57:48: event [TextChanged] on [TextBox1]. Text 1=[first text]</span>
<span id="lblInfo2"></span>
..............
Note the following points:
- The server detected a [TextChanged] event on [TextBox1] since it generated the text [lblInfo1]. The absence of [__VIEWSTATE] did not prevent this. In its absence, it assumes that the previous value of an input field is the empty string.
- It was able to restore the text posted for [TextBox1] into the [value] attribute of the [TextBox1] tag so that the [TextBox1] field reappears with the entered value. To do this, it does not need [__VIEWSTATE] but only the value posted for [TextBox1]
Now, let’s make the same request again without changing anything. We get the following new response:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=first+text --data TextBox2=
Text 1:
<input name="TextBox1" type="text" value="first text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" id="TextBox2" />
<span id="lblInfo1">20:05:47: event [TextChanged] on [TextBox1]. Text 1=[first text]</span>
<span id="lblInfo2"></span>
In the absence of [__VIEWSTATE], the server could not see that the value of the [TextBox1] field had not changed. It therefore acts as if the previous value were the empty string. Consequently, it generated the [TextChanged] event on [TextBox1] here. Let’s run the same request again, this time with the [TextBox1] field empty and [TextBox2] not empty:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox2=second+text --data TextBox1=
......
Text 1:
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" value="second text" id="TextBox2" />
<
<span id="lblInfo1"></span>
<
<span id="lblInfo2">20:11:54: event [TextChanged] on [TextBox2]. Text 2=[second text]</span>
......
In the absence of [__VIEWSTATE], the previous value of [TextBox1] was treated as the empty string. Since the posted value of [TextBox1] was also the empty string, the [TextChanged] event on [TextBox1] was not generated. The [TextBox1_TextChanged] procedure was not executed, and therefore the [lblInfo1] field did not receive a new value. We know that in this case, the component retains its old value. However, that is not the case here; [lblInfo1] has lost its previous value. This is because this value is retrieved from [__VIEWSTATE]. Since this field is missing, the empty string was assigned to [lblInfo1]. For the [TextBox2] , the server compared its posted value [second text] to its previous value. In the absence of [__VIEWSTATE], this previous value is equal to the empty string. Since the posted value of [TextBox2] is different from the empty string, the [TextChanged] event on [TextBox2] was triggered. The [TextBox2_TextChanged] procedure was executed, and the [lblInfo2] field received a new value.
One might wonder whether the [__EVENTTARGET] and [__EVENTARGUMENT] parameters are actually useful. By not sending these parameters, the server will not know which event triggered the [submit]. Let’s try:
dos>curl --include --url http://localhost/form4.aspx --data TextBox2=second+text --data TextBox1=first+text
..............................
Text 1:
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" id="TextBox2" />
<
<span id="lblInfo1"></span>
<
<span id="lblInfo2"></span>
</form>
.....................
We can see that no [TextChanged] event was handled. Furthermore, the posted fields [TextBox1] and [TextBox2] do not retrieve their posted values. In fact, everything behaves as if a GET request had been made. Everything returns to normal if the [__EVENTTARGET] field is included in the posted fields, even if it has no value:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET= --data TextBox2=second+text --data TextBox1=first+text
.......
Text 1:
<input name="TextBox1" type="text" value="first text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
Text 2:
<input name="TextBox2" type="text" value="second text" id="TextBox2" />
<span id="lblInfo1">20:34:14: event [TextChanged] on [TextBox1]. Text 1=[first text]</span>
<span id="lblInfo2">20:34:14: [TextChanged] event on [TextBox2]. Text 2=[second text]</span>
........
7.6.4. Other properties of the TextBox component
The [TextBox] server component also allows you to generate the HTML tags <input type="password"..> and <textarea>..</textarea>, i.e., the tags corresponding respectively to a password field and a multi-line input field. This generation is controlled by the [TextMode] property of the [TextBox] component. It has three possible values:
value | Generated HTML tag |
<input type="text" ...> | |
<textarea>...</textarea> | |
<input type="password ...> |
We will examine the use of these properties with the following example [form5.aspx]:

No. | name | type | properties | role |
1 | Button | [submit] button - used to add the contents of [TextBox1] to those of [TextBox2] | ||
2 | TextBox | TextMode=Password Text= | password-protected input field | |
3 | TextBox | TextMode=Multi-line Text= | combines the entries made in [TextBox1] |
The page's [EnableViewState] property is set to [false]. On the server side, we handle the click event on the [btnAjouter] button:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' we add the contents of [textBox1] to those of [TextBox2]
textbox2.text = textbox2.text + textbox1.text + controlchars.crlf
End Sub
To understand this code, you need to recall how a form’s POST request is processed. The [Page_Init] and [Page_Load] procedures are executed first. Next come all the cached event procedures. Finally, the procedure handling the event that triggered the [POST] is executed—in this case, the [btnAjouter_Click] procedure. When the event handlers run, all page components that have a value in the POST request have taken on that value. The others have reverted to their previous value if their [EnableViewState] property was set to [true], or to their design-time value if their [EnableViewState] property was set to [false]. Here, the values of the [TextBox1] and [TextBox2] fields will be part of the POST sent by the client. Therefore, in the previous code, [textbox1.text] will have the value posted by the client, and the same applies to [textbox2.text]. The [btnAjouter_Click] procedure sets the value of the [TextBox2] field to the value posted for [TextBox2] added to the value posted for [TextBox1], plus the line break character [ControlChars.CrLf] defined in the [Microsoft.VisualBasic] namespace. It is not necessary to import this namespace, as the web server imports it by default.
The final code for [form5.aspx] is as follows:
<%@ Page Language="VB" EnableViewState="False" %>
<script runat="server">
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' Append the contents of [textBox1] to those of [TextBox2]
textbox2.text += textbox1.text + controlchars.crlf
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<
<asp:Button id="btnAdd" onclick="btnAdd_Click" runat="server" Text="Add" EnableViewState="False"></asp:Button>
<asp:TextBox id="TextBox1" runat="server" TextMode="Password" Width="353px" EnableViewState="False"></asp:TextBox>
<asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Width="419px" Height="121px" EnableViewState="False"></asp:TextBox>
</form>
</body>
</html>
A little earlier, we provided a screenshot of a run.
7.7. The DropDownList component
The <asp:DropDownList> tag allows you to insert a drop-down list into a page's presentation code. We create a page [form6.aspx] to produce the following layout:

No. | Name | type | properties | role |
1 | DropDownList | AutoPostback=true EnableViewState=true | drop-down list | |
2 | Label | EnableViewState=false | information message |
The generated presentation code is as follows:
Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
<
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</form>
</body>
</html>
For now, the drop-down list contains no items. We will populate it in the [Page_Load] procedure. To do this, we need to know some of the properties and methods of the [DropDownList] class:
A collection of type [ListItemCollection] containing the items in the drop-down list. The members of this collection are of type [ListItem]. | |
The number of items in the [Items] collection | |
Element number i in the list - of type [ListItem] | |
to add a new element [ListItem] to the [Items] collection | |
to remove all items from the [Items] collection | |
to remove item number i from the [Items] collection | |
The first [ListItem] in the [Items] collection whose [Selected] property is true | |
Index of the [SelectedItem] element in the [Items] collection |
The items in the [Items] collection of the [DropDownList] class are of type [ListItem]. Each [ListItem] generates an HTML <option> tag:
We describe some properties and methods of the [ListItem] class:
constructor - creates a [ListItem] element with the [text] and [value] properties. A ListItem(T,V) element will generate the HTML tag <option value="V">T</option>. The [ListItem] class therefore allows us to describe the elements of an HTML list | |
boolean. If true, the corresponding option in the HTML list will have the [selected="selected"] attribute. This attribute tells the browser that the corresponding element should appear selected in the HTML list | |
the text T of the HTML option <option value="V" [selected="selected"]>T</option> | |
the value V of the [Value] attribute of the HTML option <option value="V" [selected="selected"]>T</option> |
We have enough information to write the code to populate the [DropDownList1] drop-down list in the page's [Page_Load] procedure:
Sub Page_Load(sender As Object, e As EventArgs)
' populate the combo box if this is the first time the procedure is called
if not IsPostBack then
dim values() as String = {"1","2","3","4"}
dim texts() as String = {"one", "two", "three", "four"}
Dim i As Integer
for i = 0 to valeurs.length - 1
DropDownList1.Items.Add(new ListItem(texts(i),values(i)))
next
end if
end sub
Once the [DropDownList1] component has been initialized, its HTML will be as follows:
<select name="DropDownList1" id="DropDownList1" onchange="__doPostBack('DropDownList1','')" language="javascript">
<option value="1">one</option>
<option value="2">two</option>
<option value="3">three</option>
<option value="4">four</option>
</select>
We know that the [Page_Load] procedure is executed every time the [form6.aspx] page is loaded. It is called the first time via a GET request, and then via a POST request each time the user selects a new item from the drop-down list. Should the code to populate this list be executed every time in [Page_Load]? The answer depends on the [EnableViewState] attribute of the [DropDownList1] component. If this attribute is set to true, we know that the state of the [DropDownList1] component will be preserved across requests in the hidden field [__VIEWSTATE]. This state includes two things:
- the list of all values in the drop-down list
- the value of the selected item in this list
It might seem tempting to set the [EnableViewState] property of the [DropDownList1] component to [true] so as not to have to recalculate the values to be placed in the list. The problem, however, is that since the [Page_Load] procedure is executed every time the [form6.aspx] page is requested, these values will still be calculated. The [Page] object, of which [form6.aspx] is an instance, has a [IsPostBack] attribute with a Boolean value. If this attribute is true, it means the page was requested via a POST. If false, it means the page was requested via a GET. In our client-server round-trip system, the client always requests the same page [form6.aspx] from the server. The first time, it requests it with a GET; subsequent times with a POST. We conclude that the [IsPostBack] property can be used to detect the client’s first GET request. We generate the values of the drop-down list only during this first request. For subsequent requests, these values will be generated by the [VIEWSTATE] mechanism. In other situations, the content of a list may vary from one request to another and must therefore be recalculated for each one. In this case, we will set the [EnableViewState] attribute of the list to [false] to avoid unnecessary double calculation of the list’s content, unless we need to know the previously selected items in the list, as this information is stored in the [VIEWSTATE].
The [AutoPostBack] attribute of the [DropDownList1] list has been set to true. This means that the browser will post the form as soon as it detects the "selected item changed" event in the drop-down list. The server, in turn, will detect—using [VIEWSTATE] and the posted values—that the selected item in the [DropDownList1] component has changed. It will then trigger the [SelectedIndexChanged] event on this component. We will handle it with the following procedure:
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' selection change
lblInfo.text = "Selected item: text=" + dropdownlist1.selecteditem.text + _
" value=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
When this procedure runs, the [DropDownList1] object has retrieved its [ListItem] elements via [VIEWSTATE]. Furthermore, one of these [ListItem] elements has its [Selected] attribute set to true—the one whose value was submitted by the browser. There are several ways to access this element:
is the first [ListItem] in the list with its [Selected] attribute set to true | |
corresponds to the [text] part of the HTML tag for the <option value="...">text</option> element selected by the user | |
corresponds to the [value] part of the HTML tag for the <option value="...">text</option> element selected by the user | |
The index in the [DropDownList1.Items] collection of the first [ListItem] element whose [Selected] attribute is true |
The final code for [form6.aspx] is as follows:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' populate the combo box if this is the first time the page is loaded
if not IsPostBack then
dim values() as String = {"1","2","3","4"}
dim texts() as String = {"one", "two", "three", "four"}
Dim i As Integer
for i = 0 to valeurs.length - 1
DropDownList1.Items.Add(new ListItem(texts(i),values(i)))
next
end if
end sub
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' selection changed
lblInfo.text = "Selected item: text=" + dropdownlist1.selecteditem.text + _
" value=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</form>
</body>
</html>
7.8. The ListBox Component
The <asp:ListBox> tag allows you to insert a list into a page's presentation code. We create [form7.aspx] to obtain the following layout:

No. | name | type | properties | role |
1 | TextBox | EnableViewState=false | input field | |
2 | Button | [submit] button that transfers the contents of txtSaisie to List 1 if it is not empty. | ||
3 | ListBox | EnableViewState=true SelectionMode=Single | list of values for single selection | |
4 | ListBox | EnableViewState=true SelectionMode=Multiple | list of values for multiple selection | |
5 | Button | [submit] button that transfers the selected item from [list 1] to [list 2] | ||
6 | Button | [submit] button that transfers the selected items from [list 2] to [list 1] |
The generated presentation code is as follows:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<html>
Type some text to include it in List 1:
<asp:TextBox id="txtSaisie" runat="server" EnableViewState="False"></asp:TextBox>
<asp:Button id="btnAdd" onclick="btnAdd_Click" runat="server" Text="Add"></asp:Button>
<tbody>
<p align="center">
List 1
<p align="center">
List 2
<asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
<asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
<asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat="server" Text="-->"></asp:Button>
<asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat
<asp:Button id="btn2vers1" onclick="btn2vers1_Click" runat="server" Text="<--"></asp:Button>
<
<asp:ListBox id="ListBox2" runat="server" SelectionMode="Multiple"></asp:ListBox>
<p align="center">
<asp:Button id="btnRaz1" onclick="btnRaz1_Click" runat="server" Text="Clear"></asp:Button>
<p align="center">
<asp:Button id="btnRaz2" onclick="btnRaz2_Click" runat="server" Text="Clear"></asp:Button>
</tbody>
</form>
</body>
</html>
The [ListBox] class is derived from the same [ListControl] class as the [DropDownList] class discussed earlier. It includes all the properties and methods seen for [DropDownList] because they actually belonged to [ListControl]. A new property appears:
sets the selection mode of the HTML <select> list that will be generated from the component. If SelectionMode=Single, then only a single item can be selected. If SelectionMode=Multiple, multiple items can be selected. To achieve this, the [multiple="multiple"] attribute will be generated in the <select> tag of the HTML list. |
Let’s handle the events. A click on the [Add] button will be handled by the following [btnAdd_Click] procedure:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' Add to list 1
dim text as string = txtSaisie.text.trim
if text <> "" then ListBox1.Items.Add(New ListItem(text))
' Clear txtSaisie
txtInput.Text = ""
End Sub
If the text entered in [txtSaisie] is not an empty or blank string, a new item is added to the [ListBox1] list. We know that we need to add an item of type [ListItem]. Previously, we used the [ListItem(T as String, V as String)] constructor to perform a similar task. Such an item generates the HTML tag [<option value="V">T</option>]. Here, we use the constructor [ListItem(T as String)], which generates the HTML tag [<option value="T">T</option>], i.e., the text [T] of the option is used to form the option’s value. Once the content of [txtSaisie] is added to the [ListBox1] list, the [txTSaisie] field is cleared.
Clicks on the [Clear] buttons will be handled by the following procedures:
Sub btnClear1_Click(sender As Object, e As EventArgs)
' clear list 1
ListBox1.Items.Clear
End Sub
Sub btnClear2_Click(sender As Object, e As EventArgs)
' Clear list 2
ListBox2.Items.Clear
End Sub
Clicks on the buttons for transferring between lists are handled by the following procedures:
Sub btn1to2_Click(sender As Object, e As EventArgs)
' Transfer the selected item from List 1 to List 2
transfer(ListBox1, ListBox2)
End Sub
Sub btn2to1_Click(sender As Object, e As EventArgs)
' Transfer the selected item from List 2 to List 1
transfer(ListBox2, ListBox1)
End Sub
Sub transfer(l1 As ListBox, l2 As ListBox)
' Transfer the selected items from l1 to l2
' anything to do?
if l1.SelectedIndex = -1 then return
dim i as integer
' start from the end
for i = l1.items.count - 1 to 0, step -1
' selected?
if l1.items(i).selected then
' no longer selected
l1.items(i).selected = false
' move to l2
l2.items.add(l1.items(i))
' remove from l1
l1.items.removeAt(i)
end if
next
end sub
Since both buttons perform the same task—transferring items from one list to another—we can reduce this to a single transfer procedure with two parameters:
- l1 of type [ListBox], which is the source list
- l2 of type [ListBox], which is the destination list
First, we check whether there is at least one selected item in the list l1; if not, there is nothing to do. To do this, we examine the property [l1.selectedindex], which represents the index of the first selected item in the list. If there are none, its value is -1. If there is at least one selected item in l1, we transfer it to l2. To do this, we iterate through the entire list of items in l1 and check for each one whether its [selected] attribute is true. If so, its [selected] attribute is set to [false], then it is copied to the l2 list, and finally it is removed from the l1 list. This removal causes the elements in the l1 list to be renumbered. This is why the list of elements in l1 is traversed in reverse order. If we traverse it forward and remove element #10, element #11 becomes #10 and #12 becomes #11. After processing element #10, our forward loop will process element #11, which, as we just explained, is the former #12. The element that was #11 and is now #10 is overlooked. By traversing the elements of list l1 in the opposite direction, we avoid this problem.
7.9. The CheckBox and RadioButton Components
The <asp:RadioButton> and <asp:CheckBox> tags allow you to insert a radio button and a checkbox, respectively, into a page’s presentation code. We create a page [form8.aspx] to obtain the following layout:

No. | name | type | properties | role |
1 | RadioButton | RadioButton1.Checked = true RadioButton1.Text = 1 RadioButton2.Checked = false RadioButton2.Text = 2 RadioButton3.Checked = false RadioButton3.Text = 3 For all 3 buttons: GroupName = radio | radio buttons | |
2 | CheckBox | Checked=false for all CheckBoxA.Text = A CheckBoxB.Text = B CheckBoxC.Text = C | checkboxes | |
3 | Button | [submit] button | ||
4 | ListBox | information list |
To ensure the browser treats the three radio buttons as mutually exclusive, they must be grouped together in a radio button group. This is done using the [GroupName] attribute of the [RadioButton] class. The page state does not need to be maintained in this application. Therefore, we set the [EnableViewState="false"] attribute on the page. The presentation code is as follows:
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
Checkboxes
<
<asp:RadioButton id="RadioButton1" runat="server" Checked="True" EnableViewState="False" GroupName="radio" Text="1"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" GroupName="radio" Text="2"></asp:RadioButton>
<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="False" GroupName="radio" Text="3"></asp:RadioButton>
<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="Fal
<asp:CheckBox id="CheckBoxA" runat="server" EnableViewState="False" Text="A"></asp:CheckBox>
<asp:CheckBox id="CheckBoxB" runat="server" EnableViewState="False" Text="B"></asp:CheckBox>
<asp:CheckBox id="CheckBoxC" runat="server" EnableViewState="False" Text="C"></asp:CheckBox>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Send"></asp:Button>
<asp:Button id="btnTree" onclick="btnTree_Click" runat="server" Text="Controls"></asp:Button>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6" Height="131px"></asp:ListBox>
</form>
</body>
</html>
We need to write the [btnEnvoyer_Click] procedure to handle the [Click] event on this button. The state of a radio button or checkbox is determined by its [Checked] attribute, which is true if the box is checked and false otherwise. So we simply need to write the value of the [Checked] attribute for the six radio buttons and checkboxes into the [lstInfos] list. Since there is no particular difficulty in doing this, let’s try something a little different:
<script runat="server">
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' we place information in the listbox
for each c as control in FindControl("frmControls").controls
' Is the control derived from CheckBox
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c, CheckBox).Checked.ToString)
end if
next
End Sub
</script>
The page can be viewed as a tree structure of controls. In our example, our page contains text and server controls. Text is treated as a special control called [LiteralControl]. Any text creates this control, even a sequence of spaces between two controls. Each control has an ID attribute that identifies it. It is the ID attribute that appears in the tags:
If we ignore the [LiteralControl] controls, the page in question has the following controls:
- [HtmlForm], which is the form [ID=frmControls]. This, in turn, is a container for controls. It contains the following controls:
-- [ID=RadioButton1] of type [RadioButton]
-- [ID=RadioButton2] of type [RadioButton]
-- [ID=RadioButton3] of type [RadioButton]
-- [ID=CheckBoxA] of type [CheckBox]
-- [ID=CheckBoxA] of type [CheckBox]
-- [ID=CheckBoxA] of type [CheckBox]
-- [ID=btnEnvoyer] of type [Button]
A control has the following properties:
returns the collection of [Control]'s child controls, if any | |
returns the control identified by ID located at the root of the tree of child controls of [Control]. In the example above: Page.FindControl("frmControls") refers to the [HtmlForm] container. To access the radio button [RadioButton1], you must write Page.FindControl("frmControls").FindControl("RadioButton1") | |
[Control] identifier |
Let’s return to the code for the [btnEnvoyer_Click] procedure:
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' we place information in the listbox
for each c as control in FindControl("frmControls").controls
' Is the control derived from CheckBox?
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c, CheckBox).Checked.ToString)
end if
Next
End Sub
We want to display the state of the radio buttons and checkboxes on the form. We iterate through all the controls on the form. If the current control is of a type derived from [CheckBox], we display its [Checked] property. Since the [RadioButton] class is derived from the [CheckBox] class, the test applies to both types of controls. The screenshot shown above displays an example of the output.
7.10. The CheckBoxList and RadioButtonList components
Sometimes, we want to allow the user to choose between values that are unknown at the time the page is designed. These choices come from a configuration file, a database, etc., and are only known at runtime. There are solutions to this problem, and we have encountered them. The single-selection list is suitable when the user can make only one choice, and the multi-selection list when they can make several. From an aesthetic standpoint, and if the number of choices is not large, you may want to use radio buttons instead of the single-selection list or checkboxes instead of the multi-selection list. This is possible with the [CheckBoxList] and [RadioButtonList].
The [CheckBoxList] and [RadioButtonList] classes are derived from the same [ListControl] class as the [DropDownList] and [ListBox] classes discussed earlier. Therefore, we will find some of the properties and methods seen for those classes—specifically, those that actually belong to [ListControl].
A collection of type [ListItemCollection] containing the items in the drop-down list. The members of this collection are of type [ListItem]. | |
The number of items in the [Items] collection | |
Element number i in the list - of type [ListItem] | |
to add a new element [ListItem] to the [Items] collection | |
to remove all items from the [Items] collection | |
to remove item number i from the [Items] collection | |
The first [ListItem] in the [Items] collection whose [Selected] property is true | |
Index of the previous element in the [Items] collection |
Some properties are specific to the [CheckBoxList] and [RadioButtonList] classes:
[horizontal] or [vertical] for horizontal or vertical lists. |
The elements in the [Items] collection are of type [ListItem]. Each [ListItem] element will generate a different tag depending on whether it is a [CheckBoxList] or [RadioButtonList] object:
Or
We describe some properties and methods of the [ListItem] class:
constructor - creates a [ListItem] element with the text and value properties. A ListItem(T,V) element will generate the HTML tag <input type="checkbox" value="V">T or <input type="radio" value="V">T, as appropriate. | |
Boolean. If true, the corresponding option in the HTML list will have the [selected="selected"] attribute. This attribute tells the browser that the corresponding element should appear selected in the HTML list | |
the text T of the HTML option <input type=".." value="V" [selected="selected"]>T | |
the value of the Value attribute of the HTML option <input type=".." value="V" [selected="selected"]>T |
We propose to build the following page [form8b.aspx]:

No. | name | type | properties | role |
1 | RadioButtonList | EnableViewState=true RepeatDirection=horizontal | list of radio buttons | |
2 | CheckBoxList | EnableViewState=true RepeatDirection=horizontal | checkbox list | |
3 | Button | [submit] button that displays in [4] the list of items selected from both lists | ||
4 | ListBox | EnableViewState=false | list of values |
The page layout code is as follows:
<%@ Page Language="VB" autoeventwireup="false" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
Checkbox lists
<
<asp:RadioButtonList id="RadioButtonList1" runat="server" RepeatDirection="Horizontal"></asp:RadioButtonList>
<asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal
<asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal"></asp:CheckBoxList>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Send"></asp:Button>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" R
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6"></asp:ListBox>
</form>
</body>
</html>
The control code is as follows:
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs) handles MyBase.Load
' populate the lists if this is the first time the method is called
if not IsPostBack then
' text for the RadioButton list
dim textsRadio() as String = {"1","2","3","4"}
' text for the CheckBox list
dim checkBoxTexts() as String = {"one", "two", "three", "four"}
' populate the radio button list
dim i as integer
for i = 0 to radioText.length - 1
RadioButtonList1.Items.Add(new ListItem(textesRadio(i)))
next
' Select item #1
RadioButtonList1.SelectedIndex = 1
' populate checkbox list
for i = 0 to textesCheckBox.length - 1
CheckBoxList1.Items.Add(new ListItem(textesCheckBox(i)))
next
end if
end sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' we place information in the lstinfos listbox
display(RadioButtonList1)
display(CheckBoxList1)
End Sub
sub display(l1 as ListControl)
' displays the values of the selected items in l1
' anything to do?
if l1.selectedindex=-1 then return
dim i as integer
' start from the end
for i = 0 to l1.items.count - 1
' selected?
if l1.items(i).selected then
lstInfos.Items.Add("[" + TypeName(l1) + "] [" + l1.items(i).text + "] selected")
end if
next
end sub
</script>
In the [Page_Load] procedure, which runs every time the page loads, the two lists are initialized. To prevent them from being initialized every time, we use the [IsPostBack] property to do so only the first time. On subsequent loads, the lists will be automatically regenerated by the [VIEWSTATE] mechanism. Once the page is displayed, the user checks certain boxes and clicks the [Submit] button. The form values are then posted to the form itself. After [Page_Load] executes, the [btnEnvoyer_Click] procedure is executed. This procedure calls the [affiche] procedure to populate the [lstInfos] list. This procedure receives a [ListControl] object as a parameter, which allows it to accept either a [RadioButtonList] object or a [CheckBoxList] object—classes derived from [ListControl]. The [lstInfos] list can have its [EnableViewState] attribute set to [false] since its state does not need to be maintained between requests.
7.11. The Panel and LinkButton components
The <asp:panel> tag allows you to insert a container of controls into a page. The advantage of the container is that some of its properties apply to all the controls it contains. This is the case with its [Visible] property. This property exists for every server-side control. If a container has the [Visible=false] property, each of its controls will be controlled by its own [Visible] property. If it has the [Visible=false] property, then the container and everything it contains is not displayed. This can be simpler than managing the [Visible] property of each of the container’s controls.
The <asp:LinkButton> tag allows you to insert a link into a page’s presentation code. It serves a similar purpose to the [Button] control. It triggers a client-side POST using an associated JavaScript function. We create a page [form9.aspx] to produce the following layout:

No. | name | type | properties | role |
1 | Panel | EnableViewState=true | control container | |
2 | ListBox | EnableViewState=true | a list of three values | |
3 | LinkButton | EnableViewState=false | link to hide the container |
When the container is hidden, a new link appears:

No. | name | type | properties | role |
4 | LinkButton | EnableViewState=false | link to display the container |
The page layout code is as follows:
<html>
<head>
</head>
<body>
<form runat="server">
<
<asp:Panel id="Panel1" runat="server" BorderStyle="Ridge" BorderWidth="1px">
Container
<asp:ListBox id="ListBox1" runat="server">
<asp:ListBox id="ListBox1" runat="server">
<asp:ListItem Value="1">one</asp:ListItem>
<asp:ListItem Value="2">two</asp:ListItem>
<asp:ListItem Value="3" Selected="True">three</asp:ListItem>
</asp:ListBox>
</asp:Panel>
<asp:ListItem Value="3" Selected="True">three</asp:ListItem></asp:List
<asp:LinkButton id="lnkVoir" onclick="lnkVoir_Click" runat="server">View container</asp:LinkButton>
<asp:Panel>
<asp:LinkButton id="lnkHide" onclick="lnkHide_Click" runat="server">Hide container</asp:LinkButton>
</form>
</body>
</html>
Note that this code initializes the [ListBox1] list with three values. The [Click] event handlers for the two links are as follows:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
...
end sub
Sub lnkVoir_Click(sender As Object, e As EventArgs)
' displays container 1
panel1.Visible = true
' change links
lnkVoir.Visible = False
lnkHide.Visible = True
End Sub
Sub lnkHide_Click(sender As Object, e As EventArgs)
' hides container 1
panel1.Visible = false
' changes the links
lnkVoir.Visible = True
lnkHide.Visible = False
End Sub
</script>
We will use the [Page_Load] procedure to initialize the form. We will do this on the first request (IsPostBack=false):
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' the first time
if not IsPostBack then
' display the container
lnkVoir_Click(nothing, nothing)
end if
end sub
.....
</script>
7.12. To continue...
The preceding paragraphs have introduced a number of server-side components. Only a few of their properties were covered in each section. To explore these components in greater depth, the reader can proceed in several ways:
- explore a component’s properties using an IDE such as WebMatrix. This tool displays the main properties of the components used in a form
- consult the .NET documentation to explore all the classes corresponding to each server component. This is the preferred method for gaining full mastery of the component. There, you will find the class hierarchy leading to the components, as well as the properties, methods, constructors, and events of each. Additionally, the documentation sometimes provides examples.
In this chapter, we used the [WebMatrix] all-in-one approach, meaning we placed the presentation code and the control code for a page in the same file. Generally speaking, we do not recommend this method but rather the [codebehind] approach used previously, which places these two types of code in two separate files. It is worth noting that the advantage of this separation lies in the fact that the control code can be compiled without having to run the web application. Furthermore, as we explained at the beginning of this chapter, our examples had a very specific structure: they consisted of a single page that served as a form exchanged between the client and the server in successive request-response cycles, with the first client request being a GET and the subsequent ones being POST requests.
7.13. Server Components and Application Controller
In the previous chapters, we built several web applications. They were all built using the MVC (Model-View-Controller) architecture, which divides the application into distinct blocks and facilitates maintenance. We previously built our user interfaces using standard HTML tags. Given what we’ve just covered, it’s natural to now want to use server components. Let’s revisit a problem we’ve already studied at length: tax calculation. Its MVC architecture was as follows:

The application has two views: [form.aspx] and [errors.aspx]. The [form.aspx] view is displayed when the URL [main.aspx] is requested for the first time:

The user fills out the form:

and clicks the [Calculate] button to get the following response:

In an MVC application, every request must go through the controller, in this case [main.aspx]. This means that once the [form.aspx] form has been filled out by the user, it must be posted to [main.aspx] and not to [form.aspx]. This is simply not possible if we build the [form.aspx] user interface using ASP server components. To see this, let’s build a [formtest.aspx] form with an <asp:button> component:
<%@ Page Language="VB" EnableViewState="false"%>
<html>
<head>
<title>test</title>
</head>
<body>
<form action="main.aspx" runat="server">
<
<asp:Button id="btnTest" runat="server" EnableViewState="false" Text="Test"></asp:Button>
</form>
</body>
</html>
Note the [action="main.aspx"] attribute of the <form> tag. Let's run this application. The display page shows only a button:

Let’s look at the HTML code sent by the server:
<html>
<head>
<title>test</title>
</head>
<body>
<form name="_ctl0" method="post" action="formtest.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwtNTMwNzcxMzI0Ozs+" />
<
<input type="submit" name="btnTest" value="Test" id="btnTest" />
</form>
</body>
</html>
We can see that the form's POST request targets the form itself [action="formtest.aspx"], whereas we had written the server-side HTML tag in [formtest.aspx]:
The [runat="server"] attribute of the <form> tag is required when using server-side components. A compilation error occurs if we do not include this attribute. When we include it, the [action] attribute of the <form> tag is ignored. The server always generates an [action] attribute that points to the form itself. We can conclude that in an MVC application, we cannot use forms built with the <form ... runat="server"> tag. However, this tag is essential for all ASP server components that retrieve user input. In other words, we cannot use ASP server forms in an MVC application. This is a major revelation. Indeed, one of the key selling points of ASP.NET is that you can build a web application like a Windows application. This is true if your application does not follow the MVC architecture, but less so otherwise. Yet the MVC architecture appears to be a fundamental concept in modern web development that is hard to ignore.
It is possible to use the MVC architecture in conjunction with ASP component forms for applications with few different views using the following workaround:
- the application consists of a single page that acts as a controller
- the views are represented on this page by different containers, one container per view. To display a view, we make its container visible and hide the others
This is an elegant solution that we will now implement in a few examples
7.14. Examples of MVC applications with ASP server components
7.14.1. Example 1
In this first example, we implement the server components we have presented. The [form10.aspx] page will look as follows:
![]() | ![]() |
The screenshot on the left above shows the form as it appears to the user. The user fills it out and submits it by clicking [Submit]. The server returns a view showing a list of the entered values (right screenshot). A link allows the user to return to the form. They see it exactly as they submitted it. The presentation code for [form10.aspx] is as follows:
<html>
<head>
<title>Example</title> <script language="javascript">
function clear(){
alert("You clicked [Clear]")
}
</script>
</head>
<body>
Form Management
<hr />
<form runat="server">
<asp:Panel id="panelinfo" runat="server" EnableViewState="False">
List of values obtained
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></asp:ListBox>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></
<asp:LinkButton id="LinkButton1" onclick="LinkButton1_Click" runat="server">Back to form</asp:LinkButton>
<hr />
<hr />
</asp:Panel>
<hr/>
<asp:Panel id="panelform" runat="server" >
<tbody>
Are you married?
<asp:RadioButton id="rdYes" runat="server" GroupName="rdmarried"></asp:RadioButton>
Yes<asp:RadioButton id="rdNon" runat="server" GroupName="rdmarie" Checked="True"></asp:RadioButton>
No
Checkboxes
<asp:CheckBox id="chk1" runat="server"></asp:CheckBox>
1<asp:CheckBox id="chk2" runat="server"></asp:CheckBox>
2<asp:CheckBox id="chk3" runat="server"></asp:CheckBox>
3
Input field
<asp:TextBox id="txtSaisie" runat="server" MaxLength="20" Columns="20"></asp:TextBox>
Password
<asp:TextBox id="txtmdp" runat="server" MaxLength="10" Columns="10" TextMode="Password"></asp:TextBox>
Input box
<asp:TextBox id="txtArea" runat="server" Columns="20" TextMode="MultiLine" Rows="3"></asp:TextBox>
Drop-down list
<asp:DropDownList id="cmbValeurs" runat="server"></asp:DropDownList>
Single-select list
<asp:ListBox id="lstSimple" runat="server"></asp:ListBox>
<asp:Button id="btnRazSimple" onclick="btnRazSimple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
Dropdown list
<asp:ListBox id="lstMultiple" runat="server" SelectionMode="Multiple"></asp:ListBox>
<asp:Button id="razMultiple" onclick="razMultiple_Click" runat="server" EnableViewState="False" Text="Clear"></asp:Button>
Hidden field
<asp:Label id="lblSecret" runat="server" visible="False"></asp:Label>
Simple button
<input id="btnDelete" onclick="delete()" type="button" value="Delete" />
[Reset] button
<input id="btnReset" type="reset" value="Reset" />
[submit] button
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" EnableViewState="False" Text="Submit"></asp:Button>
</tbody>
</asp:Panel>
</form>
</body>
</html>
The page has two containers, one for each view: [panelform] for the form view, [panelinfo] for the information view. The list of components in the [panelForm] container is as follows:
name | type | properties | role |
Panel | EnableViewState=true | form view | |
RadioButton | EnableViewState=true GroupName=rdmarie | radio buttons | |
Checkbox | EnableViewState=true | checkboxes | |
TextBox | EnableViewState=true | input field | |
TextBox | EnableViewState=true | protected input field | |
TextBox | EnableViewState=true | multi-line text box | |
DropDownList | EnableViewState=true | drop-down list | |
ListBox | EnableViewState=true SelectionMode=Single | single-select list | |
Button | EnableViewState=false | deselects all items in lstSimple | |
ListBox | EnableViewState=true SelectionMode=Multiple | multiple selection list | |
Button | EnableViewState=false | Deselect all items in lstMultiple | |
Label | EnableViewState=true Visible=false | hidden field | |
Standard HTML | displays an alert | ||
Button | EnableViewState=false | [submit] button on the form | |
Standard HTML | Form [reset] button |
The role of [VIEWSTATE] for components is important here. All components except buttons must have the [EnableViewState=true] property. To understand why, we need to recall how the application works. Suppose the [txtSaisie] field has the [EnableViewState=false] property:
- The client requests the [form10.aspx] page for the first time. It receives the form view
- , fills it out, and submits it using the [Submit] button. The input fields are then posted, and the server assigns the posted value or their [VIEWSTATE] to the server-side components, if they had one. Thus, the [txtSaisie] field is assigned the value entered by the user. Therefore, for this step, its [VIEWSTATE] serves no purpose. As a result of the operation, the [informations] view is sent—which is actually still the [form10.aspx] page but with a different displayed container.
- The user views this new view and uses the [Back to Form] link to return to it. A POST is then sent to [form10.aspx]. At most, there is only one posted value: the value selected by the user from the information list, which is not used subsequently. In any case, there is no [txtSaisie] field posted.
- The server receives the POST and assigns the posted value to the server components or their [VIEWSTATE] if they had one. Here, [txtNom] has no posted value. If its [EnableViewState] attribute is set to [false], the empty string will be assigned to it. Since we want it to have the value entered by the user, it must have the property [EnableViewState=true].
The [panelinfo] container has the following controls:
name | type | properties | role |
Panel | EnableViewState=false | information view | |
ListBox | EnableViewState=false | List of information summarizing the values entered by the user | |
LinkButton | EnableViewState=false | link back to the form |
During testing, if we look at the HTML code generated by the presentation code above, we might be surprised by the code generated for the hidden field [lblSecret]:
The [lblSecret] component is not rendered as HTML because it has the [Visible=false] property. However, because it has the [EnableViewState=true] property, its value will still be stored in the hidden field [__VIEWSATE]. Therefore, we will be able to retrieve it, as the tests will demonstrate.
We still need to write the event handlers. In [Page_Load], we will initialize the form:
Sub page_Load(sender As Object, e As EventArgs)
' the first time, we initialize the elements
' on subsequent runs, these elements retrieve their values via VIEWSTATE
if IsPostBack then return
' initialize form
' panelinfo not displayed
panelinfo.visible=false
' paneform displayed
panelform.visible=true
' radio buttons
rdNon.Checked=true
' checkboxes
chk2.Checked=true
' text field
txtInput.Text="a few words"
' password field
txtPassword.Text="thisissecret"
' text box
txtArea.Text="line" + ControlChars.CrLf + "line2" + ControlChars.CrLf
' combo box
dim i as integer
for i = 1 to 4
cmbValues.Items.Add(new ListItem("choice" + i.ToString, i.ToString))
next
cmbValues.SelectedIndex = 1
' single-select list
for i = 1 to 7
lstSimple.Items.Add(new ListItem("single" + i.ToString, i.ToString))
next
lstSimple.SelectedIndex = 0
' multi-select list
for i = 1 to 10
lstMultiple.Items.Add(new ListItem("multiple" + i.ToString, i.ToString))
next
lstMultiple.Items(0).Selected = true
lstMultiple.Items(2).Selected = true
' hidden field
lblSecret.Text = "secret"
End Sub
Clicking the [lstRazSimple] and [lstMultiple] buttons:
Sub btnRazSimple_Click(sender As Object, e As EventArgs)
' clear single list
lstSimple.SelectedIndex=-1
End Sub
Sub razMultiple_Click(sender As Object, e As EventArgs)
' clear multiple list
lstMultiple.SelectedIndex = -1
End Sub
Clicking the [Send] button:
Sub btnSend_Click(sender As Object, e As EventArgs)
' The info panel is made visible and the form panel is hidden
infoPanel.Visible = true
formPanel.Visible = false
' retrieve the posted values and store them in lstInfos
' radio buttons
dim info as string="marital status: "+iif(rdoui.checked,"married","unmarried")
display(info)
' checkboxes
info = "Checked boxes: " + iif(chk1.checked, "1 yes", "1 no") + "," + _
iif(chk2.checked, "2 yes", "2 no") + "," + iif(chk3.checked, "3 yes", "3 no")
display(info)
' input field
display("input field: " + txtSaisie.Text.Trim)
' password
display("password: " + txtMdp.Text.Trim)
' text box
dim lines() as String
lines = New Regex("\r\n").Split(txtArea.Text.Trim)
dim i as integer
for i = 0 to lines.length - 1
lines(i) = "[" + lines(i).Trim + "]"
Next
display("Input box: " + String.Join(",",lines))
' combo
display("Items selected in combo: " + selection(cmbValues))
' simple list
display("Items selected in simple list: " + selection(lstSimple))
' multiple list
display("items selected in multiple list: " + selection(lstMultiple))
' hidden field
display("Hidden field: " + lblSecret.Text)
End Sub
Sub Display(msg As String)
' displays msg in lstInfos
lstInfos.Items.Add(msg)
End Sub
function selection(list as ListControl) as string
' iterate through the list items
' to find the selected ones
dim i as integer
dim info as string=""
for i = 0 to list.Items.Count - 1
if list.Items(i).Selected then info+="[" + list.Items(i).Text + "]"
next
return info
end function
Finally, clicking the [Back to form] link:
Sub LinkButton1_Click(sender As Object, e As EventArgs)
' display the form and hide the info panel
panelform.visible = true
panelinfo.visible = false
End Sub
7.14.2. Example 2
Here, we are revisiting an application previously covered that uses standard HTML forms. The application allows users to perform tax calculation simulations. It relies on an [impot] class, which we will not revisit here. This class requires data that it retrieves from an OLEDB data source. For this example, we will use an ACCESS data source.
7.14.2.1. The application's MVC structure
The application’s MVC structure is as follows:

The three views will be incorporated into the controller’s presentation code [main.aspx] as containers. Therefore, this application has a single page [main.aspx].
7.14.2.2. The views of the web application
The [form] view is the form for entering information used to calculate a user’s tax:

The user fills out the form:

They use the [Submit] button to request a tax calculation. They see the following [simulations] view:

They return to the form via the link above. They find it in the state in which they entered it. They may make data entry errors:

These are flagged by the [errors] view:

They return to the form via the link above. They find it in the state in which they entered it. They can perform new simulations:

He then sees the [simulations] view with one additional simulation:

Finally, if the data source is unavailable, the user is notified in the [errors] view:

7.14.2.3. The application's presentation code
Remember that the [main.aspx] page brings together all the views. It is a single form with three containers:
- [panelform] for the [form] view
- [panelerrors] for the [errors] view
- [panelsimulations] for the [simulations] view
We return to separating presentation code and control code into two separate files. The first will be in [main.aspx] and the second in [main.aspx.vb]. The code for [main.aspx] is as follows:
<%@ page codebehind="main.aspx.vb" inherits="vs.main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Tax Calculation</title>
</HEAD>
<body>
<P>Calculate Your Tax</P>
<HR width="100%" SIZE="1">
<FORM id="Form1" runat="server">
<asp:panel id="panelform" Runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
<TR>
<TD height="19">Are you married?</TD>
<TD height="19">
<asp:RadioButton id="rdYes" runat="server" GroupName="rdMarried"></asp:RadioButton>Yes
<asp:RadioButton id="rdNo" runat="server" GroupName="rdMarried" Checked="True"></asp:RadioButton>No</TD>
</TR>
<TR>
<TD>Number of children</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" MaxLength="3" Columns="3"></asp:TextBox></TD>
</TR>
<TR>
<TD>Annual salary (euros)</TD>
<TD>
<asp:TextBox id="txtSalary" runat="server" MaxLength="10" Columns="10"></asp:TextBox></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculate" runat="server" Text="Calculate"></asp:Button>
<asp:Button id="btnClear" runat="server" Text="Clear"></asp:Button></P>
</asp:panel>
<asp:panel id="errorPanel" runat="server">
<P>The following errors occurred:</P>
<P>
<asp:Literal id="HTMLErrors" runat="server"></asp:Literal></P>
<P></P>
<asp:LinkButton id="lnkForm1" runat="server">Back to form</asp:LinkButton>
</asp:panel>
<asp:panel id="panelsimulations" runat="server">
<P>
<TABLE>
<TR>
<TH>
Married</TH>
<TH>
Children</TH>
<TH>
Annual salary</TH>
<TH>
Tax due (euros)</TH></TR>
<asp:Literal id="simulationsHTML" runat="server"></asp:Literal></TABLE>
<asp:LinkButton id="lnkForm2" runat="server">Back to form</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
We have defined the three containers. Note that they are all within the <form runat="server"> tag. This is mandatory, because to take advantage of server components, they must be placed within such a tag. The key point to understand is that we have a single form here that will be exchanged between the client and the web server. We are therefore using the configuration described throughout this chapter on server components. Let’s break down the components of each container:
[panelform] container:
name | type | properties | role |
Panel | EnableViewState=true | form view | |
RadioButton | EnableViewState=true GroupName=rdmarie | radio buttons | |
TextBox | EnableViewState=true | number of children | |
TextBox | EnableViewState=true | annual salary | |
Button | [submit] button on the form - starts the tax calculation | ||
Button | form [submit] button - clears the form |
[errorPanel] container:
name | type | properties | role |
Panel | EnableViewState=true | error view | |
LinkButton | EnableViewState=true | link to the form | |
Literal | HTML code for the error list |
[panelsimulations] container:
name | type | properties | role |
Panel | EnableViewState=true | simulation view | |
LinkButton | EnableViewState=true | link to the form | |
Literal | HTML code for the list of simulations in an HTML table |
7.14.2.4. The application's control code
The application control code is distributed across the [global.asax.vb] and [main.aspx.vb] files. The [global.asax] file is defined as follows:
The [global.asax.vb] file is as follows:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Collections
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Create an 'impot' object
Dim objImpot As Impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("connectionString")))
' Add the object to the application
Application("objImpot") = objImpot
' no error
Application("error") = False
Catch ex As Exception
'An error occurred; we note it in the application
Application("error") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Start of session - create an empty list of simulations
Session.Item("simulations") = New ArrayList
End Sub
End Class
When the application starts (first request made to the application), the [Application_Start] procedure is executed. It attempts to create an object of type [impot] by retrieving its data from an OLEDB source. The reader is encouraged to review Chapter 5, where this class was defined, if they have forgotten it. The creation of the [impot] object may fail if the data source is unavailable. In this case, the error is stored in the application so that all subsequent requests know that it could not be initialized correctly. If the creation is successful, the created [impot] object is also stored in the application. It will be used by all tax calculation requests. When a client makes their first request, a session is created for them by the [Application_Start] procedure. This session is intended to store the various tax calculation simulations they will perform. These will be stored in an [ArrayList] object associated with the session key "simulations". When the session starts, this key is associated with an empty [ArrayList] object. The information required by the application is placed in its configuration file [wenConfig]:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="connectionString" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\webforms\vs\impots5\impots.mdb" />
</appSettings>
</configuration>
The [connectionString] key specifies the connection string to the OLEDB source. The rest of the control code is located in [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents rdYes As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdNo As System.Web.UI.WebControls.RadioButton
Protected WithEvents txtChildren As System.Web.UI.WebControls.TextBox
Protected WithEvents txtSalary As System.Web.UI.WebControls.TextBox
Protected WithEvents btnCalculate As System.Web.UI.WebControls.Button
Protected WithEvents btnDelete As System.Web.UI.WebControls.Button
Protected WithEvents panelform As System.Web.UI.WebControls.Panel
Protected WithEvents lnkForm1 As System.Web.UI.WebControls.LinkButton
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents errorPanel As System.Web.UI.WebControls.Panel
Protected WithEvents simulationPanel As System.Web.UI.WebControls.Panel
Protected WithEvents simulationsHTML As System.Web.UI.WebControls.Literal
Protected WithEvents HTMLErrors As System.Web.UI.WebControls.Literal
' local variables
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub displayForm()
...
End Sub
Private Sub displaySimulations(ByRef simulations As ArrayList, ByRef link As String)
...
End Sub
Private Sub btnCalculate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculate.Click
...
End Sub
Private Function checkData() As ArrayList
...
End Function
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
....
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
End Sub
Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
...
End Sub
Private Sub ClearForm()
...
End Sub
End Class
The first event handled by the code is [Page_Load]:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' First, we check the state of the application
If CType(Application("error"), Boolean) Then
' the application failed to initialize
' We display the errors view
Dim errors As New ArrayList
errors.Add("Application temporarily unavailable (" + CType(Application("message"), String) + ")")
displayErrors(errors, "")
Exit Sub
End If
' no errors - on the first request, display the form
If Not IsPostBack Then displayForm()
End Sub
Remember that when the [Page_Load] procedure runs on a client POST, all form components have a value: either the value posted by the client, if there is one, or the component’s previous value via [VIEWSTATE]. In this form- , all components have the [EnableViewState=true] property. Before processing the request, we ensure that the application has initialized correctly. If not, we display the [errors] view using the [displayErrors] procedure. If this is the first request (IsPostBack=false), we display the [form] view using [displayForm].
The procedure that displays the [errors] view is as follows:
Private Sub displayErrors(ByRef errors As ArrayList, ByRef link As String)
' displays the errors container
errorPanel.Visible = True
Dim i As Integer
errorsHTML.Text = ""
For i = 0 To errors.Count - 1
HTMLErrors.Text += "<li" + Errors(i).ToString + "" + ControlChars.CrLf
Next
lnkForm1.Text = link
' the other containers are hidden
panelform.Visible = False
panelsimulations.Visible = False
End Sub
The procedure has two parameters:
- a list of error messages in [errors]
- a link text in [link]
The HTML code to be generated for the error list is placed in the [errorsHTML] literal. The link text is placed in the [Text] property of the [LinkButton] object in the view.
The procedure that displays the [form] view is as follows:
Private Sub displayForm()
' displays the form
panelform.Visible = True
' the other containers are hidden
errorPanel.Visible = False
simulationPanel.Visible = False
End Sub
This procedure simply makes the [panelform] container visible. The components are displayed with their posted or previous value (VIEWSTATE).
When the user clicks the [Calculate] button in the [form] view, a POST request is sent to [main.aspx]. The [Page_Load] procedure is executed, followed by the [btnCalculate_Click] procedure:
Private Sub btnCalculate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculate.Click
' Check the validity of the entered data
Dim errors As ArrayList = checkData()
' if there are errors, we report them
If errors.Count <> 0 Then
' display the error page
displayErrors(errors, "Back to form")
Exit Sub
End If
' No errors - calculate the tax
Dim tax As Long = CType(Application("objTax"), tax).calculate( _
rdYes.Checked, CType(txtChildren.Text, Integer), CType(txtSalary.Text, Long))
' add the result to existing simulations
Dim simulation() As String = New String() {CType(IIf(rdYes.Checked, "yes", "no"), String), _
txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString}
' Add the result to the existing simulations
Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList)
simulations.Add(simulation)
' we put the simulations into the session and the context
Session.Item("simulations") = simulations
' Display the results page
displaySimulations(simulations, "Back to form")
End Sub
The procedure begins by validating the form fields using the [checkData] procedure, which returns an [ArrayList] of error messages. If the list is not empty, then the [errors] view is displayed and the procedure terminates. If the entered data is valid, then the tax amount is calculated using the [tax] object that was stored in the application upon startup. This new simulation is added to the list of simulations already performed and stored in the session.
The [CheckData] function verifies the validity of the data. It returns an [ArrayList] of error messages, which is empty if the data is valid:
Private Function checkData() As ArrayList
' No errors at first
Dim errors As New ArrayList
' number of children
Try
Dim nbChildren As Integer = CType(txtChildren.Text, Integer)
If nbChildren < 0 Then Throw New Exception
Catch
errors.Add("The number of children is incorrect")
End Try
' salary
Try
Dim salary As Long = CType(txtSalary.Text, Long)
If salary < 0 Then Throw New Exception
Catch
errors.Add("The annual salary is incorrect")
End Try
' return the list of errors
Return errors
End Function
Finally, the [simulations] view is displayed by the following [displaySimulations] procedure:
Private Sub displaySimulations(ByRef simulations As ArrayList, ByRef link As String)
' displays the simulations view
panelsimulations.Visible = True
' the other containers are hidden
errorPanel.Visible = False
panelForm.Visible = False
' content of the simulations view
' each simulation is an array of 4 string elements
Dim simulation() As String
Dim i, j As Integer
simulationsHTML.Text = ""
For i = 0 To simulations.Count - 1
simulation = CType(simulations(i), String())
simulationsHTML.Text += ""
For j = 0 To simulation.Length - 1
simulationsHTML.Text += "" + simulation(j) + ""
Next
simulationsHTML.Text += "" + ControlChars.CrLf
Next
' link
lnkForm2.Text = link
End Sub
The procedure has two parameters:
- a list of simulations in [simulations]
- a link text in [link]
The HTML code to be generated for the list of simulations is stored in the [simulationsHTML] literal. The link text is stored in the [Text] property of the [LinkButton] object in the view.
When the user clicks the [Clear] button in the [form] view, the [btnClear_click] procedure is executed (always after [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' displays the empty form
razForm()
displayForm()
End Sub
Private Sub clearForm()
' clears the form
rdYes.Checked = False
rdNo.Checked = True
txtChildren.Text = ""
txtSalary.Text = ""
End Sub
The code above is simple enough that it doesn't need to be commented. We still need to handle clicks on the [errors] and [simulations] view links:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' displays the form
displayForm()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' displays the form
displayForm()
End Sub
Both procedures simply display the [form] view. We know that the form’s fields will receive a value that is either the value posted for them or their previous value. Since, in this case, the client’s POST request does not send any values for the form fields, they will revert to their previous values. The form is therefore displayed with the values entered by the user. We recall that, in the version without server components, we performed this restoration ourselves.
7.14.2.5. Tests
All files required by the application are placed in a folder named <application-path>: ![]() | The [bin] folder contains the DLL containing the [impot], [impotsData], and [impotsOLEDB] classes required by the application: ![]() |
The reader may, if they wish, revisit Chapter 5, which explains how to create the [impot.dll] file mentioned above. Once this is done, the Cassini server is launched with the parameters (<application-path>,/impots5). We request the URL [http://impots5/main.aspx] using a browser:

If we rename the ACCESS file [impots.mdb] to [impots1.mdb], we will see the following page:

7.14.3. Example 3
We have shown in these two examples that it is possible to build web applications following the MVC architecture using server-side components. The last example demonstrates that the server-side component solution is simpler than the solution using standard HTML tags. Our two examples had only one page with multiple views within the same page. You can have an MVC architecture with multiple server-side ASP forms as long as the fact that these forms post their own values to themselves does not pose a problem. This is very often the case with applications that have menus. Let’s take the following example:

We have gathered links to the applications we have written so far on a single page. This type of application lends itself well to an MVC architecture. The only difference is that there is no longer one controller, but several.

The [main.aspx] controller acts as the main controller. It is the one called by the links on the application’s home page. It will perform operations common to all possible actions and then execute the specific action associated with the link used. It will then hand off to one of the secondary controllers, the one responsible for executing the action. From this point on, communication takes place between the client and this specific controller. We no longer go through the main controller [main.aspx]. We are therefore no longer in the MVC framework with a single controller that filters all requests. Each of the controllers above can present multiple views using the container mechanism within a single page, as we have described.
The fact that we no longer have a single controller choosing which views to send to the client has its drawbacks. Take error handling, for example. Each action exposed by the application may need to display an error view. Each controller [applix.aspx] will have its own [errors] view because this is simply a specific container within the controller’s page. There is no way to have a single [errors] view that would be used by all individual applications. In fact, such a view typically includes a link back to the form with errors, and that form must be restored to the state in which it was validated to allow the user to correct their errors. This restoration is performed via the [VIEWSTATE] mechanism, which does not work across different controllers. If applications are developed by different people, there is a risk of having error pages that look different depending on the action chosen by the user, which undermines the consistency of the overall application. We will see a little later that ASP.NET offers a solution to this specific problem of sharing views. This can be implemented as a new server component that we build ourselves. Simply using this component across the various applications ensures the consistency of the overall application. More difficult to manage is the issue of action order. When all requests pass through a single controller, it can verify that the requested action is compatible with the previous one. This validation code is located in a single place. Here, it will need to be distributed across the various controllers, complicating the maintenance of the overall application.
Let’s return to our application above. Its entry page is a standard HTML page [home.htm]:
<html>
<head>
<TITLE>Server-Side ASP Components</TITLE>
<meta name="pragma" content="no-cache">
</head>
<frameset rows="130,*" frameborder="0">
<frame name="banner" src="banner.htm" scrolling="no">
<frameset cols="200,*">
<frame name="contents" src="options.htm">
<frame name="main" src="main.htm">
</frameset>
<noframes>
<p id="p1">
This HTML frameset displays multiple web pages. To view this frameset
, use a web browser that supports HTML 4.0 and later versions
later.
</noframes>
</frameset>
</html>
This home page consists of three frames called banner, contents, and main:
![]() |
The page [bandeau.htm] placed in the [banner] frame is as follows:

Its HTML code is as follows:
<html>
<head>
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE" />
<title>banner</title>
</head>
<body>
<P>
<TABLE>
<TR>
<TD><IMG alt="University of Angers logo" src="univ01.gif"></TD>
<TD>ASP server components</TD>
</TR>
</TABLE>
</P>
<HR>
</body>
</html>
The [options.htm] page is placed in the [contents] banner. It is a set of links:
![]() | |
The various links all point to the main controller [main.aspx] with an [action] parameter indicating the action to be performed. We specify that the link target should be displayed in the [main] frame (target="main").
The first page displayed in the [main] frame is [main.htm]:
|
The main controller [main.aspx, main.aspx.vb] is as follows:
[main.aspx]
[main.aspx.vb]
Public Class main
Inherits System.Web.UI.Page
Private Sub Page\_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' retrieve the action to be performed
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "label"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' execute the action
Select Case action
Case "label"
Server.Transfer("form2.aspx")
Case "button"
Server.Transfer("form3.aspx")
Case "textbox1"
Server.Transfer("form4.aspx")
"textbox2" field
Server.Transfer("form5.aspx")
Case "dropdownlist"
Server.Transfer("form6.aspx")
Case "listbox"
Server.Transfer("form7.aspx")
Case "checkbox"
Server.Transfer("form8.aspx")
Case "checkboxlist"
Server.Transfer("form8b.aspx")
Case "panel"
Server.Transfer("form9.aspx")
Case Else
Server.Transfer("form2.aspx")
End Select
End Sub
End Class
Our controller is simple. Depending on the value of the [action] parameter, it transfers the request processing to the appropriate page. It offers no added value compared to an HTML page with links. However, simply adding an authentication page would demonstrate its usefulness. If the user had to authenticate (username, password) to access the applications, the [main.aspx] controller would be a good place to verify that this authentication has been completed.





