Web Forms Architecture
A Web Form consists of two parts:
The visual content or presentation, typically specified by HTML elements.
Code that contains the logic for interacting with the visual elements.
A Web Form is physically expressed by a file with the extension .aspx. Any HTML page could be renamed to have this extension and could be accessed using the new extension with identical results to the original. Thus Web Forms are upwardly compatible with HTML pages.
The way code can be separated from the form is what makes a Web Form special. This code can be either in a separate file (having an extension corresponding to a .NET language, such as .vb for VB.NET) or in the .aspx file, within a <SCRIPT RUNAT="SERVER"> ... /SCRIPT> block. When your page is run in the Web server, the user interface code runs and dynamically generates the output for the page.
We can understand the architecture of a Web Form most clearly by looking at the code-behind version of our "Echo" example. The visual content is specified by the .aspx file HelloCodebehind.aspx.
<!-- HelloCodebehind.aspx --> <%@ Page Language="VB#" Src="HelloCodebehind.aspx.vb" Inherits= MyWebPage %> <HTML> <HEAD> </HEAD> <BODY> <FORM RUNAT="SERVER">YOUR NAME: <asp:textbox id=txtName Runat="server"></asp:textbox> <p><asp:button id=cmdEcho onclick=cmdEcho_Click Text="Echo" runat="server" tooltip="Click to echo your name"> </asp:button></p> <asp:label id=lblGreeting runat="server"></asp:label> <P></P> </FORM> </BODY> </HTML>
The user interface code is in the file HelloCodebehind.aspx.vb,
' HelloCodebehind.aspx.vb Imports System Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Public Class MyWebPage Inherits System.Web.UI.Page Protected txtName As TextBox Protected cmdEcho As Button Protected lblGreeting As Label Protected Sub cmdEcho_Click(Source As Object, _ e As EventArgs) lblGreeting.Text="Hello, " & txtName.Text End Sub End Class
The key namespace for Web Forms and Web services is System.Web. Support for Web Forms is in the namespace System.Web.UI. Support for server controls such as textboxes and buttons is in the namespace Sys-tem.Web.UI.WebControls. The class that dynamically generates the output for an .aspx page is the Page class, in the System.Web.UI namespace, and classes derived from Page, as illustrated in the code-behind page in this last example.
INHERITING FROM PAGE CLASS
The elements in the .aspx file, the code in the code-behind file (or script block), and the base Page class work together to generate the page output. This cooperation is achieved by ASP.NET's dynamically creating a class for the .aspx file, which is derived from the code-behind class, which in turn is derived from Page. This relationship is created by the Inherits attribute in the .aspx file. Figure 146 illustrates the inheritance hierarchy. Here MyWebPage is a class we implement, derived from Page.
The most derived page class, shown as My .aspx Page in Figure 146, is dynamically created by the ASP.NET runtime. This class extends the page class, shown as MyWebPage in the figure, to incorporate the controls and HTML text on the Web Form. This class is compiled into an executable, which is run when the page is requested from a browser. The executable code creates the HTML that is sent to the browser.
FIGURE 146 Hierarchy of page classes.
Web Forms Page Life Cycle
We can get a good high-level understanding of the Web Forms architecture by following the life cycle of our simple Echo application. We will use the code-behind version (the second example), HelloCodebehind.aspx.
User requests the HelloCodebehind.aspx Web page in the browser.
Web server compiles the page class from the .aspx file and its associated code-behind page. The Web server executes the code, creating HTML, which is sent to the browser. (In Internet Explorer you can see the HTML code from the menu View | Source.) Note that the server controls are replaced by straight HTML. The following code is what arrives at the browser, not the original code on the server.
The browser renders the HTML, displaying the simple form shown in Figure 147. To distinguish this example from the first one, we show "YOUR NAME" in all capitals. Since this is the first time the form is displayed, the text box is empty, and no greeting message is displayed.
Figure 14-7 The form for the Echo application is diplayed for the first time.
The user types in a name (e.g., Mary Smith) and clicks the Echo button. The browser recognizes that a Submit button has been clicked. The method for the form is POST1 and the action is HelloCodebehind.aspx. We thus have what is called a postback to the original .aspx file.
The server now performs processing for this page. An event was raised when the user clicked the Echo button, and an event handler in the MyWebPage class is invoked.
Protected Sub cmdEcho_Click(Source As Object, _ e As EventArgs) lblGreeting.Text="Hello, " & txtName.Text End Sub
The Text property of the TextBox server control txtName is used to read the name submitted by the user. A greeting string is composed and assigned to the Label control lblGreeting, again using property notation.
The server again generates straight HTML for the server controls and sends the whole response to the browser. Here is the HTML.
The browser renders the page, as shown in Figure 148. Now a greeting message is displayed.
Figure 14-8 After a round trip, a greeting message is displayed.
<!-- HelloCodebehind.aspx --> <HTML> <HEAD> </HEAD> <BODY> <form name="ctrl0" method="post" action="HelloCodebehind.aspx" id="ctrl0"> value="dDwxMzc4MDMwNTk1Ozs+" /> YOUR NAME: <input name="txtName" type="text" id="txtName" /> <p><input type="submit" name="cmdEcho" value="Echo" id="cmdEcho" title="Click to echo your name" /></p> <span id="lblGreeting"></span> <P></P> </form> </BODY> </HTML>
... <form name="ctrl0" method="post" action="HelloCodebehind.aspx" id="ctrl0"> <input type="hidden" name="__VIEWSTATE" value="dDwxMzc4MDMwNTk1O3Q8O2w8aTwyPjs+O2w8dDw7bDxpPDU+Oz47 bDx0PHA8cDxsPFRleHQ7PjtsPEhlbGxvLCBNYXJ5IFNtaXRoOz4+Oz47Oz4 7Pj47Pj47Pg==" /> YOUR NAME: <input name="txtName" type="text" value="Mary Smith" id="txtName" /> <p><input type="submit" name="cmdEcho" value="Echo" id="cmdEcho" title="Click to echo your name" /></p> <span id="lblGreeting">Hello, Mary Smith</span> ...
An important characteristic of Web Forms is that all information on forms is "remembered" by the Web server. Since HTTP is a stateless protocol, this preservation of state does not happen automatically but must be programmed. A nice feature of ASP.NET is that this state information, referred to as "view state," is preserved automatically by the framework, using a "hidden" control.
... <input type="hidden" name="__VIEWSTATE" value="dDwxMzc4MDMwNTk1O3Q8O2w8aTwyPjs+O2w8dDw7bDxpPDU+Oz47 bDx0PHA8cDxsPFRleHQ7PjtsPEhlbGxvLCBNYXJ5IFNtaXRoOz4+Oz47Oz4 7Pj47Pj47Pg==" /> ...
Later in the chapter we will examine other facilities provided by ASP.NET for managing session state and application state.
Web Forms Event Model
From the standpoint of the programmer, the event model for Web Forms is very similar to the event model for Windows Forms. Indeed, this similarity is what makes programming with Web Forms so easy. What is actually happening in the case of Web Forms, though, is rather different. The big difference is that events get raised on the client and processed on the server.2 Our simple form with one textbox and one button is not rich enough to illustrate event processing very thoroughly. Let's imagine a more elaborate form with several textboxes, listboxes, checkboxes, buttons, and the like. Because round trips to the server are expensive, events do not automatically cause a postback to the server. Server controls have what is known as an intrinsic event set of events that automatically cause a postback to the server. The most common such intrinsic event is a button click. Other events, such as selecting an item in a list box, do not cause an immediate postback to the server. Instead, these events are cached, until a button click causes a post to the server. Then, on the server the various change events are processed, in no particular order, and the button-click event that caused the post is processed.
Processing a page is a cooperative endeavor between the Web server, the ASP.NET runtime, and your own code. The Page class provides a number of events, which you can handle to hook into page processing. The Page class also has properties and methods that you can use. We cover some of the major ones here. For a complete description, consult the .NET Framework documentation. The example programs in this chapter will illustrate features of the Page class.
A number of events are raised on the server as part of the normal processing of a page. These events are actually defined in the Control base class and so are available to server controls also. The most important ones are listed below.
Init is the first step in the page's life cycle and occurs when the page is initialized. There is no view-state information for any of the controls at this point.
Load occurs when the controls are loaded into the page. View-state information for the controls is now available.
PreRender occurs just before the controls are rendered to the output stream. Normally this event is not handled by a page but is important for implementing your own server controls.
Unload occurs when the controls are unloaded from the page. At this point it is too late to write your own data to the output stream.
The Page class has a number of important properties. Some of the most useful are listed below.
EnableViewState indicates whether the page maintains view state for itself and its controls. You can get or set this property. The default is true, view state is maintained.
ErrorPage specifies the error page to which the browser should be redirected in case an unhandled exception occurs.
IsPostBack indicates whether the page is being loaded in response to a postback from the client or is being loaded for the first time.
IsValid indicates whether page validation succeeded.3
Request gets the HTTP Request object, which allows you to access data from incoming HTTP requests.
Response gets the HTTP Response object, which allows you to send response data to a browser.
Session gets the current Session object, which is provided by ASP.NET for storing session state.
Trace gets a TraceContext object for the page, which you can use to write out trace information.
We can illustrate some of these features of page processing with a simple extension to our Echo program. The page HelloPage.aspx (located in the top-level chapter directory) provides handlers for a number of page events, and we write simple text to the output stream, using the Response property. For each event we show the current text in the txtName and lblGreeting server controls. In the handler for Load we also show the current value of IsPostBack, which should be false the first time the page is accessed, and subsequently true.
<!-- HelloPage.aspx --> <%@ Page Language="VB" Debug="true" %> <HTML> <HEAD> <SCRIPT RUNAT="SERVER"> Sub cmdEcho_Click(Source As Object, e As EventArgs) lblGreeting.Text="Hello, " & txtName.Text End Sub Sub Page_Init(sender As Object, E As EventArgs) Response.Write("Page_Init<br>") Response.Write("txtName = " & txtName.Text & "<br>") Response.Write("lblGreeting = " & lblGreeting.Text _ & "<br>") End Sub Sub Page_Load(sender As Object, E As EventArgs) Response.Write("Page_Load<br>") Response.Write("IsPostBack = " & IsPostBack & "<br>") Response.Write("txtName = " & txtName.Text & "<br>") Response.Write("lblGreeting = " & lblGreeting.Text _ & "<br>") End Sub Sub Page_PreRender(sender As Object, E As EventArgs) Response.Write("Page_PreRender<br>") Response.Write("txtName = " & txtName.Text & "<br>") Response.Write("lblGreeting = " & lblGreeting.Text _ & "<br>") End Sub </SCRIPT> </HEAD> <BODY> <FORM RUNAT="SERVER">Your name: <asp:textbox id=txtName Runat="server"></asp:textbox> <p><asp:button id=cmdEcho onclick=cmdEcho_Click Text="Echo" runat="server" tooltip="Click to echo your name"> </asp:button></p> <asp:label id=lblGreeting runat="server"></asp:label> <P></P> </FORM> </BODY> </HTML>
When we display the page the first time the output reflects the fact that both the text box and the label are empty, since we have entered no information. IsPostBack is false. Now enter a name and click the Echo button. We obtain the following output from our handlers for the page events:
Page_Init txtName = lblGreeting = Page_Load IsPostBack = True txtName = Robert lblGreeting = Page_PreRender txtName = Robert lblGreeting = Hello, Robert
In Page_Init there is no information for either control, since view state is not available at page initialization. In Page_Load the text box has data, but the label does not, since the click-event handler has not yet been invoked. IsPostBack is now true. In Page_PreRender both controls now have data.
Click Echo a second time. Again, the controls have no data in Page_Init. This time, however, in Page_Load the view state provides data for both controls. Figure 149 shows the browser output after Echo has been clicked a second time.
Figure 14-9 Browser output after Echo has been clicked a second time.
An .aspx file may contain a page directive defining various attributes that can control how ASP.NET processes the page. A page directive contains one or more attribute/value pairs of the form
within the page directive syntax
<@ Page ... @>
Our example program HelloCodebehind.aspx illustrates an .aspxpage that does not have any code within it. The code-behind file HelloCode-behind.aspx.vb that has the code is specified using the Src attribute.
<!-- HelloCodebehind.aspx --> <%@ Page Language="VB" Src="HelloCodebehind.aspx.vb" Inherits=MyWebPage %> ...
The Src attribute identifies the code-behind file.
The Language attribute specifies the language used for the page. The code in this language may be in either a code-behind file or a SCRIPT block within the same file. Values can be any .NET-supported language, including C# and VB.NET.
The Inherits directive specifies the page class from which the .aspx page class will inherit.
The Debug attribute indicates whether the page should be compiled with debug information. If true, debug information is enabled, and the browser can provide detailed information about compile errors. The default is false.
The ErrorPage attribute specifies a target URL to which the browser will be redirected in the event that an unhandled exception occurs on the page.
The Trace attribute indicates whether tracing is enabled. A value of true turns tracing on. The default is false.
ASP.NET provides extensive tracing capabilities. Merely setting the Trace attribute for a page to true will cause trace output generated by ASP.NET to be sent to the browser. In addition, you can output your own trace information using the Write method of the TraceContext object, which is obtained from the Trace property of the Page. The page HelloTrace.aspx illustrates using tracing in place of writing to the Response object.
<!-- HelloTrace.aspx --> <%@ Page Language="C#" Debug="true" Trace = "true" %> <HTML> <HEAD> <SCRIPT RUNAT="SERVER"> Sub cmdEcho_Click(Source As Object, e As EventArgs) lblGreeting.Text="Hello, " & txtName.Text End Sub Sub Page_Init(sender As Object, E As EventArgs) Trace.Write("Page_Init<br>") Trace.Write("txtName = " & txtName.Text & "<br>") Trace.Write("lblGreeting = " & lblGreeting.Text _ & "<br>") End Sub ...
Figure 1410 shows the browser output after the initial request for the page. Notice that the trace output is shown after the form, along with trace information that is generated by ASP.NET itself.
Figure 14-10 Browser output showing trace information.