2.4 A Day in the Life of a PageThe analogy between the desktop control model and Web Forms is not complete. Although the fundamental elements behave similarly, such as rendering of state and event propagation, the Web Forms model has an imposed sequencing on the rendering process that does not exist in the desktop model. It is critical for Web developers using ASP.NET to understand this sequencing in order to use the model effectively.
Pages are short-lived objects, as are all the elements they contain. A page is created for the sole purpose of processing a request, and once that request processing has completed, the page is discarded. This means that each request to a page is
Listing 2-9 Sample Page with Incorrect Control State Manipulation
<! File: sample.aspx >
<%@ Page Language="C#" %>
<html>
<body>
<form runat=server>
<h2>ASP.NET sample page</h2>
<h3>Page type: <span id=_message runat=server/></h3>
<%
// Error - modifying a property of a server-side
// control within a script block like this can lead
// to inconsistencies
_message.InnerText = this.GetType().ToString();
%>
</form>
</body>
</html>
As noted in the comments, the assignment to the
InnerText
property of the server-side span element has no effect because it
Figure 2-5 shows the events that occur during a page's lifetime. As a page developer, you have hooks into most of these events, either by defining event handlers for a particular event or by providing overridden versions of virtual functions defined in the Page base class. Most of the programmatic manipulation of server-side controls should occur either in the Load event handler for a page or within a server-side control event handler. The Load event fires after the all the server-side controls have been created and had their state restored from the POST body of the request. This gives you an opportunity to both view the values submitted by the client, as well as a chance to populate controls with values you want the client to see in the response to this request. Figure 2-5. Page Event Sequence
|
2.5 Web Forms and Code-Behind
Perhaps the most appealing aspect of the Web Forms model is that in combination with code-behind, it enables true separation of page logic from page layout and rendering. For example, recall the page shown in Listing 2-1, where the input and select elements were
Listing 2-10 Sample Page with Server-Side Controls and Code-Behind
<! WebFormPage2.aspx >
<%@ Page Language="C#"
Inherits="EssentialAspDotNet.WebForms.Page2"
src="Page2.cs" AutoEventWireUp="false" %>
<html>
<body>
<form runat=server>
<h3>Enter name:
<input type=text id=_name runat=server/></h3>
<h3>Personality:
<select id=_personality runat=server /></h3>
<input type=button id=_enterButton
value="Enter" runat=server/>
<p runat=server id=_messageParagraph />
</form>
</body>
</html>
Listing 2-11 Code-Behind File for Sample Page
// Page2.cs
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace EssentialAspDotNet.WebForms
{
public class Page2 : Page
{
protected HtmlSelect _personality;
protected HtmlInputText _name;
protected HtmlInputButton _enterButton;
protected HtmlGenericControl _messageParagraph;
override protected void OnInit(EventArgs e)
{
// Wire up handler to ServerClick event of button
_enterButton.ServerClick += new EventHandler(OnEnter);
}
override protected void OnLoad(EventArgs e)
{
// On initial access, populate select with items
if (!IsPostBack)
{
_personality.Items.Add(new ListItem("extraverted"));
_personality.Items.Add(new ListItem("introverted"));
_personality.Items.Add(new ListItem("in-between"));
}
}
protected void OnEnter(object src, EventArgs e)
{
// When the user presses enter, print a message
string msg = string.Format("Hi {0}, you selected {1}",
_name.Value, _personality.Value);
_messageParagraph.InnerText = msg;
}
}
}
Note that we were able to manipulate server-side controls on the form from fields declared in our code-behind class. In fact, looking just at the code-behind file, it appears that we are manipulating
This process of associating server-side controls with fields in a
Page
-derived class occurs during the parsing of the .aspx page. The parser must be careful not to redefine fields in the class that is generated from the .aspx file, because this would mask any fields declared with the same name in the code-behind base class. Instead, the parser uses reflection to query the code-behind base class during the parsing of the .aspx file. If there is a public or protected field declared in the code-behind base class whose name matches the identifier of the server-side control, it will not generate a field for that control in the generated class. If there is no such field in the code-behind base class, it will create a field in the newly generated class. This
Figure 2-6 shows this field binding. In this example, the Test.aspx file contains three server-side controls: the form, which has no explicit identifier, the server-side span element named _foo , and the server-side input element named _bar . The code-behind class, BasePage , contains a single protected field named _foo of type HtmlGenericControl , which is what a span element maps to. Thus, when the parser generates the class from the Test.aspx file, it adds two fields to the derived class: one for the form control with an artificially generated identifier ( _ctl0 ) and the other using the _bar identifier assigned in the page. Figure 2-6. Binding Fields to Server-Side Controls
|