The Page Class
Most requests to an ASP.NET page start out as a URL that includes an ASPX extension in the filename. The common language runtime class that handles this type of a request is System::Web::UI::Page. To start, here's the simplest "Hello World"-style ASP.NET page in a file named HelloWorld.aspx :
<%@ Page %>
Hello World from ASP.NET
There's not too much going on with this page—all it does is print out "Hello World from ASP.NET" to the browser requesting the file. However, it illustrates the fundamental page architecture behind ASP.NET. If you put this file in a virtual directory somewhere and then surf to it using a browser, ASP.NET will generate an assembly based on the ASP.NET syntax. If you start up ILDASM, you can find the assembly generated by ASP.NET. On Windows 2000, the assembly lands in the Winnt\Microsoft.NET\Framework\v1.0.3705\Temporary ASP.NET Files directory. If you hunt around for the virtual directory hosting the file, you'll see something like \vcppnet\cc541602\245fc247\uiya6evk.dll. This is the assembly generated by ASP.NET when a request is made for the page. Figure 34-1 shows ILDASM reflecting the page.
Figure 34-1: A very simple ASPX page reflected by ILDASM.
When you pop open the nodes within ILDASM, you'll notice that the assembly includes a namespace called ASP and a class named HelloWorld_aspx. If you look even closer, you'll see that the class extends System::Web::UI::Page and includes various member functions that are obviously necessary for rendering the page.This exercise shows that when you surf to an ASPX page, ASP.NET automatically creates an assembly for you and generates a class for you—and that class derives from System::Web::UI::Page. The class is responsible for rendering HTML to the client. So, if ASP.NET inserts the class for you, is there any way you can replace the System::Web::UI::Page class with one of your own so you can provide your own processing? Yes, you can—and the technique is known formally as the code-behind technique.
Code-Behind
Every ASPX file generates a corresponding assembly when a request is made for the file. The ASP.NET page syntax includes a directive for defining the class that an ASPX page will use. You've seen that by default an ASPX page uses a class derived from System::Web::UI::Page. However, you can write your own page-derived class and insert it into the hierarchy.
The Ex34a Example: Defining an ASP.NET Code-Behind Page
Chapter 4, which generates a simple ASPX file and a code-behind page class.) The application wizard generates a class library project that will compile into an assembly that is similar to the assemblies we looked at in Chapter 32. Here is the code produced by the wizard:Ex34a.h
// Ex34a.h
#pragma once
#using <system.dll>
#using <system.web.dll>
using namespace System;
using namespace System::Web;
using namespace System::Web::UI;
using namespace System::Collections;
namespace Ex34a
{
public __gc class ManagedCPPPage : public Page
{
protected:
ArrayList* m_arrayList;
void AssembleChoices()
{
m_arrayList = new ArrayList();
String* str = "Just-in-time Compiling";
m_arrayList->Add(str);
str = "Common runtime environment";
m_arrayList->Add(str);
str = "Multiple language support";
m_arrayList->Add(str);
str = "Simplified component model";
m_arrayList->Add(str);
str = "Excellent backwards compatibility";
m_arrayList->Add(str);
str = "ASP.NET";
m_arrayList->Add(str);
}
void DisplayFeatures()
{
for(int i = 0; i < m_arrayList->Count; i++)
{
Response->Write("<li>");
Response->Write(m_arrayList->get_Item(i));
Response->Write("</li>");
Response->Write("</br>");
}
}
void Page_Load(Object* o, EventArgs* ea)
{
AssembleChoices();
}
};
}
Recall that .NET managed code needs to live on the garbage-collected heap. The class listed above is defined as a __gc class, and it derives from System::Web::UI::Page. The page handles the Page_Load event by assembling a list of favorite .NET features into an ArrayList (which is also a common language runtime class). Also notice the DisplayFeatures function, which simply cycles through the array of features and directs the list of choices to the browser that's making the request through the Page.Response.Write method.
Important | The code-behind assembly must live in the \bin directory beneath the virtual directory hosting the page. |
To use the code-behind assembly, you simply refer to the assembly within the Inherits directive on the page. Here's the ASP.NET page that uses the ManagedCPPPage class:
<%@ Page Language="c#" Inherits="Ex34a.ManagedCPPPage" %>
<html>
<body>
<h3> Favorite .NET Features </h3>
<% DisplayFeatures(); %>
</body>
</html>
Figure 34-2 shows the output to the browser.
Figure 34-2: Output generated by the code-behind page.
To confirm that ASP.NET brought your class in, you can look at the resulting assembly within the Temporary ASP.NET Files directory. Figure 34-3 shows ILDASM reflecting the assembly generated by ASP.NET. Notice that the class used to define the page derives from Ex34a.ManagedCPPPage.
Figure 34-3: The code-behind DLL as reflected by ILDASM.
We cannot cover the entire scope of the Page class here. However, you should know that the Page class is the basis for two of the most powerful and convenient features of ASP.NET: Web Forms and server-side controls.
Web Forms
Traditionally, ASP development has involved a great deal of grunge code for coding a user interface. The primary difficulty in getting a user interface to work correctly over the Web is keeping track of the state of the user interfaces between posts. As an MFC developer, you're used to having all the user interface code for an application reside within a single process space. That means, for example, that when you define a combo box in a window or dialog box, the combo box will always show its correct state. For instance, if you select Oregon from a combo box that lists states, the combo box will continue to show Oregon until you select something else. (The combo box won't pop back and show Alabama). Windows itself keeps track of the state of the control.The Web does not work this way. HTTP is a connectionless protocol, which means that once a response is sent from the server back to the client, the connection (and any state associated with that connection) disappears. (In a Windows-based desktop application, the state does not disappear.) The bottom line is that a browser only ever sees a snapshot of the state of the server. So user interface programming over the Web becomes problematic because it involves sending some HTML out to the client browser over a connection that doesn't stick around. The HTML that was sent to the browser can contain tags that will eventually render as Windows-style controls (such as combo boxes and list boxes). However, the state of the control (for example, which item in a combo box was selected) has to be handled manually.The following listing is an example of some typical classic ASP-style code that maintains the state of a combo box on a Web page. The file is in Ex34a and is named Raw.asp .
<%@ Language="javascript" %>
<html>
<body>
Feature: <select name="Feature">
<option
<% if (Request("Feature") == "Garbage collection") {
Response.Write("selected");
}%> >Garbage collection</option>
<option
<% if (Request("Feature") == "Multiple languages") {
Response.Write("selected");
}%> >Multiple languages</option>
<option
<% if (Request("Feature") == "No more GUIDS") {
Response.Write("selected");
}%> >No more GUIDS</option>
</select>
</body>
</html>
This code checks the request coming from the client, finds out which item in the combo box was selected, and makes sure that it's the item appearing in the combo box by including selected with that option. When the browser gets the HTML back from the server, the browser will render a combo box with the correct selection showing. This is just a simple example, but it shows the kinds of machinations that ASP developers have had to go through to make even simple user interfaces work.It turns out that most of the code necessary to keep a user interface consistent over a disconnected protocol can be pushed down into the runtime. That's exactly what ASP.NET Web Forms is designed to do—to handle control state management for you using server-side controls.In terms of ASP.NET syntax, the easiest way to use server-side controls is to include the runat=server attribute in the tag, as shown in the following listing:
<html><body>
<form runat=server>
Feature: <select name="Feature" runat=server>
<option>Garbage collection</option>
<option>Multiple languages</option>
<option>No more GUIDS</option>
</select>
</form>
</body></html>
There are two other important points here. First, the select tag that runs at the server is included between a form tag that also runs at the server. Second, the file includes an ASPX extension. This file is named SelectMe.aspx in the Ex34a directory on the companion CD.When ASP.NET processes the ASPX page, it creates an instance of the .NET Framework class System::Web::UI::HtmlControls::HtmlSelect. The HtmlSelect control keeps track of the state of the combo box between posts. (You can look up the class in the help system, and you'll see it's just another .NET Framework class.) Two kinds of server-side controls ship with ASP.NET: HTML controls and Web controls. We'll look more closely at Web controls here because they're more consistent and flexible. Keep in mind, though, that the only thing the browser ever really sees is some HTML. The HTML controls and Web controls are classes that live on the server and are responsible for rendering HTML to the client. The Page architecture and the server-side controls are known collectively as Web Forms.
With the Web Forms model of programming, you feel like you're building a local user interface (as you might when using MFC). However, in reality you're programming a widely distributed user interface that's generated almost entirely by pushing HTML tags from the server to the client browser. ASP.NET keeps track of the state of the controls for you.The following listing shows the Ex34a.aspx enhanced to use some server-side controls:
<%@ Page Language="c#" Inherits="Ex34a.ManagedCPPPage" %>
<html>
<body>
<form runat=server>
<h3> Favorite .NET Features </h3>
<% DisplayFeatures(); %>
</br>
<asp:Label text="Type your name:" runat=server />
<asp:TextBox id="m_name" runat=server/> </br> </br>
<asp:Label Text="Select your favorite .NET feature:" runat=server /> </br>
<asp:CheckBoxList id="m_cblFeatureList" runat=server/> </br></br>
<asp:Button id="Submit" OnClick="SubmitInfo" Text="Submit" runat=server />
</br>
<asp:Label id="m_labelInfo" runat=server />
</form>
</body>
</html>
The page includes several Web controls: three asp:Label controls, an asp:TextBox control, an asp:CheckBoxList control, and an asp:Button control. Notice that all the controls run at the server, and that the button seems to be wired up to some sort of handler. Here's the Ex34a code-behind class modified to handle the new controls on the form:
// Ex34a.h
#pragma once
#using <system.dll>
#using <system.web.dll>
using namespace System;
using namespace System::Web;
using namespace System::Web::UI;
using namespace System::Web::UI::WebControls;
using namespace System::Collections;
using namespace System::ComponentModel;
namespace Ex34a
{
public __gc class ManagedCPPPage : public Page
{
protected:
ArrayList* m_arrayList;
CheckBoxList* m_cblFeatureList;
Label* m_labelInfo;
TextBox* m_name;
void AssembleChoices()
{
m_arrayList = new ArrayList();
String* str = "Just-in-time compiling";
m_arrayList->Add(str);
str = "Common runtime environment";
m_arrayList->Add(str);
str = "Multiple language support";
m_arrayList->Add(str);
str = "Simplified component model";
m_arrayList->Add(str);
str = "Excellent backwards compatibility";
m_arrayList->Add(str);
str = "ASP.NET";
m_arrayList->Add(str);
}
void DisplayFeatures()
{
for(int i = 0; i < m_arrayList->Count; i++)
{
Response->Write("<li>");
Response->Write(m_arrayList->get_Item(i));
Response->Write("</li>");
}
}
void Page_Load(Object* o, EventArgs* ea)
{
AssembleChoices();
if(!this->IsPostBack)
{
m_cblFeatureList->DataSource = m_arrayList;
m_cblFeatureList->DataBind();
}
}
void SubmitInfo(Object* o, EventArgs* ea)
{
String* s;
s = s->Concat(S"Hello ", m_name->Text);
s = s->Concat(s, S". You selected ");
for(Int32 i = 0;
i < m_cblFeatureList->Items->get_Count(); i++)
{
if(m_cblFeatureList->Items->get_Item(i)->get_Selected())
{
s = s->Concat(s, S"<li>");
s = s->Concat(s, m_cblFeatureList->Items->
get_Item(i)->get_Text());
s = s->Concat(s, S"</li>");
}
}
s = s->Concat(s, S"</br>");
s = s->Concat(s, S" as your favorite .NET feature");
m_labelInfo->Text=s;
}
};
}
The same list of features is displayed as with the earlier version of this example. However, notice that there's now a check box list from which to select your favorite feature, and a button to submit your choice back to the server. The check box list (m_cblFeatureList) is populated during the Page_Load event. Notice that the m_cblFeatureList includes a member named DataSource to which is assigned the ArrayList containing the feature list. This is an example of a data binding control. The check box list will render a check box tag for each element in the data source. This cuts down on a great deal of coding.
The HTML rendered by this ASPX page places raw text, a text input box, some check box tags, and a button on the browser. Clicking the Submit button causes a postback to the server, and execution is routed to the SubmitInfo method on the page. (SubmitInfo is a member of the code-behind page.) Figure 34-4 shows the Web page in action.
Figure 34-4: Ex34a with server-side controls.
What Happened to ActiveX?
At this point, many people will ask, "Why is all this processing being pushed back to the server? Won't this severely restrict my ability to create rich interactive sites?" This question is often followed by another one: "What happened to ActiveX?" The answers to these questions lie in understanding the problem that ASP.NET is trying to solve: how to get software out to as many people as possible using the wire that's already there.First, consider where rich user interfaces came from—the desktop. Users are accustomed to rich GUI interfaces. A sophisticated user interface is almost a requirement for any site. One of the most valuable assets consumers can provide for a company is their attention to the company's Web site. Naturally, companies want to create compelling, useful Web sites. Therefore, Web users need sophisticated controls with which they can interact with the site.
Most browsers support standard controls such as buttons and list boxes. However, the standard controls can take you only so far. The earliest attempts to create sophisticated browser-centric applications were centered around ActiveX controls on the Microsoft platform.When the user hits a page containing an ActiveX control, the browser proceeds to download an ActiveX DLL. You saw how ActiveX controls work in Chapter 9 and Chapter 26. The browser calls CoCreateInstance on the object, negotiates some interfaces, and renders the control in the host application. (In the case of the Internet, the host program is a browser.)The ActiveX control approach has some specific advantages. The most significant advantage is that you can provide much more natural and intuitive ways for the user and Web site to talk to one another.However, there's a problem with extending the browser to enrich the user interface using client-side technologies such as ActiveX: Client browsers have to support that technology. For example, if you want the browser to use an ActiveX control to interact with the site, that browser must have the infrastructure to support ActiveX controls, which is complex.Ensuring that specific user interface technology (the COM infrastructure) is available on the client machine is impossible, especially with the advent of Web-enabled personal digital assistants (PDA)s and Web phones. A huge number of browsers are out there, and some might not support a special user interface infrastructure required by your site. If you make ActiveX controls integral to your Web site, you cannot reach certain clients.This is why ASP.NET moves user interface's generation to the server—to increase the audience of your Web site by helping you manage (or eliminate) your Web site's dependence on a specific browser. The server can look out at the browser, figure out what kind of browser it is, and send out the appropriate HTML based on information coming from the browser's headers. It's also becoming much easier to develop sophisticated user interfaces based on HTML tags.Next, we'll take a look at the HTTP pipeline and the lifetime of a request. Along the way, you'll see a couple of very useful extensibility points provided by ASP.NET.