The Page Class


When a Web form is requested from the server “ a client requests a URL that has an aspx extension on it “ the components that make up that page are compiled into one unit. The components consist of:

  • The .aspx file being requested.

  • The .NET class file containing the code for that page.

  • Any user controls used by the page.

The unit the components are compiled into is a dynamically generated class derived from the .NET System.Web.UI.Page class. All the page's controls, presentation information, and logic are used to extend this class to provide an object supporting the functionality of the page that was created.

This dynamically created Page class can then be instantiated any time a request is made for the .aspx page. When it is instantiated, the resulting object is used to process the incoming requests and returns the data to the requesting client. Any web controls “ intrinsic or custom “ are in turn instantiated by this object and provide their results back to the Page object to be included in the response to the client. The executable page object is created from compiling all of the files (code-behind, user controls, and so on) associated with the page. This compilation only takes place when one of the files changes, or when the application configuration file (covered in Chapter 13) changes. This makes the process extremely efficient.

In ASP, code and presentation were either integrated into one file, or split among many files using #include files. When this file(s) was executed, the server would simply start at the top of the file and spit out any HTML text that it found back to the client. When it encountered some script, the script would be executed, and any additions to the HTML response stream would be added at that point. So in effect, all that existed was an HTML file with some code interspersed in it.

In ASP.NET, the page is actually an executable object that outputs HTML. But it is truly an object in that it has a series of processing stages “ initialization, processing, and cleanup “ just like all objects do. The difference that makes the Page class unique is that it performs these functions every time it is called “ meaning it is a stateless object and no instances of it hang around in between client requests. Also, the Page class has a unique step, known as the rendering step, when HTML is actually generated for output to the client.

When we examine code-behind programming a little later in this chapter, you will see the file that actually contains the code is really a class definition. The class defined in that file is derived from the Page class. Once the class derived from the Page class is created, that class needs to link to the .aspx file in some way. This is done in the @ PAGE directive at the top of an .aspx file. So if the code-behind file named mypage.cs has a class definition such as:

  public class MyPage: Page {..... }  

Then the corresponding .aspx file includes following directive:

  <%@ PAGE Inherits="MyPage" Code="mypage.cs" %>  

The intrinsic Page class serves as a container class for all of the components that make up a page. The interface of the Page class can be used to manipulate certain aspects of what happens on the page. The events, properties, and methods are shown in the following table:

Attribute

Description

Init event

Fired when the page is initialized .

Load event

Fired when the page is loaded, and all controls (including their viewstate) have been loaded.

Unload event

Fired when the page is done processing “ this happens after all the information has been sent to the client.

PreRender event

Fired just prior to the information being written to the client.

AbortTransaction event

Fired when the transaction that the page is participating in is aborted.

CommitTransaction event

Fired when the transaction that the page is participating in is committed.

Error event

The Error event will be fired whenever an unhandled exception occurs on the page. Handle this event to perform custom error processing (see Chapter 22 for more details).

Application property

Reference to the current Application object. For each Web application, there is exactly one instance of this object. It is shared by all of the clients accessing the Web application.

Cache property

The Cache property gets a reference to the Cache object that can be used to store data for subsequent server round-trips to the same page. The Cache object is in essence a dictionary object whose state is persisted through the use of hidden form fields or some other means, so that data can live from one page request to the next .

ClientTarget property

This property will override the browser detection that is built into ASP.NET and specify what the specific browser to render the page. Any controls that then rely on the browser being detected will use the specified configuration, rather than the capabilities of the actual requesting browser.

EnableViewState property

This Boolean value indicates whether or not the server controls on this page maintain their ViewState between page requests. This value affects all of the controls on the page, and supercedes any individual settings on the controls themselves .

ErrorPage property

If, as the page is being compiled and run, an unhandled exception is detected, then you probably want to display some kind of error message to the user. ASP.NET generates its own default error page, but to control what is being displayed, then this property can be set to the URL of the page that will be displayed instead.

IsPostBack property

This Boolean value is set to true if the page is being run as the result of a client round-trip. When it is false , this is the first time the page is being displayed, and that there is no ViewState stored for the server controls. When this is the case, the state of the controls need to be set manually “ usually during the execution of the Page_Load event.

IsValid property

This Boolean value is set to true if all of the validation controls on the page report that their validation conditions have been positively met. If any one validation test fails, then this value will be set to false . Validation controls will be covered in Chapter 5 when Server controls are shown in detail. Checking this property can help to improve page performance by avoiding performing certain expensive functions when it is known that a validation condition has not been met.

Request property

Reference to the Request object “ allowing access to information about the HTTP Request.

Response property

Reference to the Response object “ allowing access to the HTTP Response.

Server property

Reference to the current Server object.

Session property

Reference to the current Session object.

SmartNavigation property

Boolean to indicate if smart navigation is enabled (covered later in the chapter).

Trace property

This property is a reference to the Trace object for this page. If tracing is enabled on the page, then this object can be used to write explicit information out to the trace log. This will be examined in more detail in Chapter 22.

TraceEnabled property

This Boolean value sets whether tracing is enabled for the page.

User property

Gets information about the user making the page request.

Validators property

This property is a reference to a collection of all of the validation controls that are on the page. Use this collection to iterate through all of the validation controls on a page to potentially check status or set validation parameters.

ViewStateUserKey property

This value is used to uniquely identify the user of the page. It is encoded into the viewstate, and when the page is submitted, the viewstate will be valid only this property has the proper value set. This is new in Version 1.1, and is designed to help prevent one-click attacks on the server through the page.

DataBind method

Performs data binding for all controls on the page.

FindControl method

Method to find a reference to a specific control within the page.

LoadControl method

Dynamically loads a User Control from a .ascx file.

LoadTemplate method

Dynamically loads a template. This is examined in Chapter 7 where data binding and templating are covered.

MapPath method

Retrieves the physical path for a specified virtual path .

ResolveUrl method

Converts a virtual URL to an absolute URL.

Validate method

Instructs any validation controls on the page to validate their content.

These members of the intrinsic Page class are accessible from within ASP.NET pages directly, without having to go through the Page object itself. For example, the following two lines of code are equivalent:

  Page.Response.Write("Hello")   Response.Write("Hello")  

There's no performance difference so you can use whichever form you prefer. Most samples only use the Page object when referring to IsPostBack purely because it's a new feature. Using the explicit convention makes it clearer that IsPostBack is an intrinsic property, not a user defined global variable.

HttpRequest Object

The HttpRequest object in ASP.NET is enhanced compared to its counterpart in legacy ASP. These changes are covered in more detail in Chapter 23, but a few of the new features are discussed here. The HttpRequest object is mapped to the Request property of the Page object, and is therefore available in the same way as in ASP “ just by using Request .

One of the most evident changes is the promotion of a number of server variables to properties of the HttpRequest object itself. With ASP, referencing the ServerVariables collection was necessary to get information about the User Agent, the IP Address of the client making the request, or even the physical path to the ASP.NET source file. In ASP.NET, these values are now properties of the HttpRequest object, making it much easier and straightforward to access the information. For example, the following table lists some of these (the full list, including changes, is covered in detail in Chapter 23):

Property

Description

AcceptTypes

Indicates the MIME types supported by the client.

ApplicationPath

The virtual application path.

ContentLength

The length (in bytes) of the request.

ContentType

The MIME type of the request.

FilePath

The virtual path of the request.

Headers

A collection of HTTP headers.

HttpMethod

The HTTP method used for the request.

Path

The virtual path of the request.

PathInfo

Additional path information.

PhysicalApplicationPath

The physical path of the application root.

PhysicalPath

The physical path of the request.

RawUrl

The raw URL of the request.

RequestType

The HTTP method used for the request.

TotalBytes

The number of bytes in the input stream.

Url

A Uri object containing details of the request.

UrlReferrer

A Uri object detailing referrer information.

UserAgent

The browser user agent string.

UserHostAddress

The IP address of the user.

UserHostName

The DNS name of the user.

UserLanguages

An array of languages preferences.

ValidateInput

Method to validate the items in the request against an internal list of potentially dangerous string values.

Another new feature of the HttpRequest object is its Browser property. This property points to an instance of the HttpBrowserCapabilities object. This object contains information about the capabilities of the browser making the request. Previously, ASP developers had to use the Browser Capabilities component to determine the same type of information. Now, they can simply refer to the Browser property directly.

In the past, the information the Browser Capabilities component used to determine the capabilities of a browser was stored in the BROWSCAP.INI file. That information is now stored in the machine.config file in an XML format, and uses regular expression pattern matching to link a browser user agent string to the capabilities of that browser. But since the information is still contained in an updateable format, there will continue to be support for new browsers and new capabilities without requiring a completely new ASP.NET version.

The new Params collection is a collection of all of the QueryString , Form , ServerVariables , and Cookie items that are part of the request. In the past, this was the default collection of the Request object itself. To access it in ASP.NET, the Params collection needs to be explicitly referenced:

  Dim strValue As String   strValue = Request.Params("param1") ' in ASP, this could have been   ' written as Request("param1")  
Note

You can still use the individual QueryString , Form , ServerVariables , and Cookie collections to access information specifically from that item if you want. You can still use the Request("var") syntax. The default property of the HttpRequest is its Item property.

There is now a MapPath method of the HttpRequest object. This method will take a virtual path to a file as a parameter, and return the physical path to the file on the server. This method can also be used to obtain the physical path to an object in a different application on the same server.

To help prevent malicious attacks, the HttpRequest object now automatically validates all input sent with the request “ the contents of the Form , QueryString , and Cookies collections. This data is validated against an undocumented set of string values, which have been determined to be potentially dangerous. If any dangerous content is detected, then an exception is raised. This feature can be disabled in the @Page directive, and then explicitly called by using the ValidateRequest method of the HttpRequest object.

Finally, there is now a SaveAs method for the HttpRequest object. This method saves the contents of the current request to disk. This can be very useful during the debugging of a Web application, since the file contains the contents of the actual request. The HTTP headers can also be saved into the file along with the contents of the request:

  If (bErrorCondition) Then   Request.SaveAs("c:\currentRequest.txt", true)   '  true  indicates to save the headers as well   End If  

HttpResponse Object

The HttpResponse object is used to send data back to the browser as the result of a request. It also provides the page information about that response. The HttpResponse object is mapped to the Response property of the Page object, and is therefore available directly within ASP.NET pages. There are several new features that are part of the HttpResponse object with ASP.NET.

The Buffer property from ASP has been deprecated and replaced by the BufferOutput property. This Boolean property sets the way that the response data is sent back to the client. If it is set to true , which is the default, the contents of the response are held on the server until the response is finished, or until the buffer is explicitly sent back to the client. When this value is false , the information is sent back to the browser as soon it is generated by the page.

Chapter 16 looks at some of the other classes that are part of the .NET Framework. Let's look at two classes, TextWriter and Stream . These classes allow you to work with streams of text or streams of bytes. There are methods that will take a Stream object or a TextWriter object as a parameter, and send the results from that method to that object. So what does that have to do with the HttpResponse object?

There are two new properties of the HttpResponse object “ Output and OutputStream “ that expose the contents of the Response buffer as either a TextWriter object or a Stream object. One way this object can be used is in the dynamic creation of images using ASP.NET. The Save method of the Bitmap class can accept a Stream object as its destination “ so if you pass in the HttpResponse . OutputStream property to this method, the results of the save will be sent as the esponse to the client:

 <%@ Page Language="VB" ContentType="image/jpeg" %> <%@ Import Namespace="System.Drawing" %> <%@ Import Namespace="System.Drawing.Imaging" %> <%@ Import Namespace="System.Drawing.Drawing2D" %> <%    Response.Clear()    Dim height As integer = 100    Dim width As integer = 200    Dim r As New Random    Dim x As integer = r.Next(75)    Dim x1 As integer = 0    Dim a As integer = r.Next(155)    Dim x2 As integer = r.Next(100)    Dim bmp As new Bitmap(width, height, PixelFormat.Format24bppRgb)    Dim g As Graphics = Graphics.FromImage(bmp)    g.SmoothingMode = SmoothingMode.AntiAlias    g.Clear(Color.LightGray)    g.DrawRectangle(Pens.White, 1, 1, width-3, height-3)    g.DrawRectangle(Pens.Gray, 2, 2, width-3, height-3)    g.DrawRectangle(Pens.Black, 0, 0, width, height)    g.DrawString("Response.OutputStream Test", _                New Font("Arial", 10, FontStyle.Bold), _                SystemBrushes.WindowText, New PointF(10,50))    g.FillRectangle(New SolidBrush(Color.FromArgb(a, 255, 128, 255)), _                                                  x, 20, 100, 50)    g.FillRectangle(New LinearGradientBrush(New Point(x2, 0), _                                         New Point(x2+75, 50+30), _                                         Color.FromArgb(128, 0, 0, 128), _                                         Color.FromArgb(255, 255, 255, 240)),_                                                        x2 ,50, 75, 30)    bmp.Save(Response.OutputStream, ImageFormat.Jpeg)    g.Dispose()    bmp.Dispose()    Response.End() %> 

There are four key lines to look at “ the other drawing functions are probably worthy of an entire book to themselves! First, we tell the browser requesting this page to send back a set of bytes that represent an image “ not a set of text in HTML format:

  <%@ Page Language="VB" ContentType="image/jpeg" %>  

Next, just to be safe, we make sure that no header information has been sent back to the browser. To do this, we need to clear the buffer. Remember that when the output to the browser is buffered, as is the default, then the buffer can be cleared out at any time before it is sent back:

  Response.Clear()  

The next part of the page dynamically creates a Bitmap object in memory, and then draws to that object. Once the page has completed drawing the bitmap, we send it to the browser. The Save method of the Bitmap object looks like this:

  Public Sub Save( _   ByVal  stream  As Stream, _   ByVal  format  As ImageFormat _   )  

The first parameter is a Stream object. The Save method sends the bytes that make up the bitmap to this Stream . The second parameter defines the format that the image will be saved as. Even though the object is a Bitmap object, it can be used to create more than just BMP files.

  bmp.Save(Response.OutputStream, ImageFormat.Jpeg)  

To save the contents of the Bitmap object directly to the Response object, we pass the Response.OutputStream property as the Stream parameter. And since earlier in the page we defined the content type as image/jpeg , the format of the image being saved is set to JPEG . Once all of the data has been sent, we explicitly end the response by calling the End method of the Response object. There are two files “ image_cs.aspx and image_vb.aspx in the book's download code to try out. Figure 4-3 shows the output of this page.

click to expand
Figure 4-3:

In addition to the HttpResponse.Clear method that you saw in the previous example, the HttpResponse.ClearHeaders method will just clear the headers from the response. Another new property of the HttpResponse object is RedirectLocation . This property maps to the HTTP header Location. This property can be used to either set this value directly, or can be used to read the value that will be sent as part of the header.

While the HttpResponse.Write method is still available in ASP.NET, the use of server controls greatly lessens the need to manually output response information using that method. However, a new method in ASP.NET, WriteFile , greatly simplifies the output of file-based information to the response. In previous versions of ASP, the developer was responsible for opening up a file, reading its contents into a buffer, and then outputting the contents of that buffer to the response using the Write method. The WriteFile method takes a filename as a parameter, and will do all of the necessary file handling work to open that file, read it, and then output its contents to the response buffer.

For example, this allows you to stream previously created HTML directly to the browser along with the current page:

  <html>   Some html content here   <script language="VB" runat="server">   Sub Page_Load(Sender As Object, E As EventArgs)   Response.WriteFile("c:\temp\Content.html")   End Sub   </script>   </html>  

Page-Processing Steps

A web forms page isn't significantly different from a traditional Web page. It is still created as a result of an HTTP Request. The server creates the page, sends the data back to the client, closes the HTTP connection, and then forgets about the request. But there are many enhancements added with Web Forms. In order to best understand these enhancements, it is important to look at the steps that the page goes through when it is processed on the server.

Server Round-Trip

As with all dynamic Web generation systems, such as ASP, JSP, and Cold Fusion, there is a division of labor. There is work that the server does and there is work that the client does. The client is responsible for presenting information, capturing information from the user, and for optionally executing some client-side script. The server is responsible for dynamically creating the page and delivering the page to the client. The server may also be managing some degree of server-side state for the client “ so that information about the client's task can be passed from one request by a user to the next one by that same user.

In this division of labor, it is critical to recognize that work executed on the client is usually only visible to the client and work executed on the server is only visible to the server. With the Web Forms model in ASP.NET, Microsoft has introduced a new concept of server controls. These controls act like the client- side controls that have been used in the past with Visual Basic, but they execute on the server. This means that the client has no access to these controls programmatically.

In order to interact with server controls, the execution must be passed from the client back to the server. The only way to do this is via an HTTP request.

Note

There are other ways to pass information to the server to be executed without making an HTTP Request. You could use DCOM, Java RMI, or a simple socket communication. But within the pure Web paradigm of ASP.NET, the HTTP Request is the only method available.

If you look at the interaction between client and server during a web forms application, you see that the execution gets passed back and forth between client and server “ even within the context of the same .aspx page. This is known as a server round-trip, illustrated in Figure 4-4:

click to expand
Figure 4-4:

In order to trigger a round-trip, the user needs to perform some interaction with the browser. There usually isn't a case where a round-trip would be triggered without user intervention, but there is nothing that prevents that from happening. Typically, the user clicking or selecting something on a page triggers the round-trip. In either case, it takes an explicit user interaction to start a round-trip. While it is possible, you wouldn't want an event like an onmouseover to cause a round-trip. That happens too frequently and would overload the server and cause the user experience to grind to a halt.

Page ViewState

In this new round-trip model of Web Forms, there are potentially more interactions with a server than there would be in a traditional browser-server interaction. But at the core it is still stateless HTTP communication. This means that the server doesn't retain any information about the previous client request, such as values in form fields, or the state of objects instantiated to create the page. This would normally mean that the server is doing a lot of extra work in recreating the page each time during a round-trip. But the Web Forms architecture has a way of dealing with this.

The page will retain its ViewState between requests to the server. The ViewState contains the state of all user controls on the page. This information is stored as name-value pairs using the System.Web.UI.StateBag object. The ViewState is stored as a string variable that is passed back to the client in the page. Since the client probably doesn't know anything about ASP.NET and ViewState , this string is stored as a hidden form field. Take a look at the example page from earlier in this chapter, and then view its source to see the ViewState being stored:

  <html>   <head>     <form name="_ctl0" method="post" action="NewASPNet.aspx" id="_ctl0">   <input type="hidden" name="__VIEWSTATE"   value="dDw0NDY2MjMzMjt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPDU+Oz47bDx0PHQ8O3Q8aTw   zPjtAPFNwZWVkeSBFeHByZXNzO1VuaXRlZCBQYWNrYWdlO0ZlZGVyYWwgU2hpcHBpbmc7PjtAPDE7M   jszOz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxZb3VyIG9yZGVyIHdpbGwgYmUgZGVsaXZlcmVkIHZ   pYSBVbml0ZWQgUGFja2FnZTs+Pjs+Ozs+Oz4+Oz4+Oz4=" />     Please select your delivery method:  

The contents of the ViewState are quite obviously not in human-readable form. But the Web Forms processor can read this and restore the values of the server controls when this page is submitted to the server. An advantage of this method is that the state of the page is held with the page, and not within the server. Another advantage is that the page can be deployed to a web farm and you do not have to worry about forcing the request from a client to come back to the same server.

But there are some minor disadvantages as well. For this rather simple page, there is a pretty sizeable set of text making up the ViewState . In a much more complex page, the contents of the ViewState could grow to a point where they begin to affect the speed at which the page is downloaded, although this isn't as much of a performance issue as it was in the past.

ViewState is enabled by default for all server controls. This means that you don't have to do anything explicit to take advantage of it. However, there could be performance issues in maintaining ViewState for a page with a large number of controls on it. There are two ways to control whether or not the ViewState is maintained . ViewState can be disabled on a page level “ meaning that no state will be kept for any control on the page. To do this, use the @Page directive along with the EnableViewState attribute:

  <%@ Page EnableViewState="false" %>  

The second level at which to control the ViewState is on a control-by-control basis. To do this, simply add the same EnableViewState parameter to the declaration of the control:

  <asp:DropDownList id="ShipMethod" EnableViewState="false" runat="server"/>  

The EnableViewState attribute is also discussed in Chapters 6 and 7, where we look at its impact on performance.

Page-Processing Steps

The server goes through a set of distinct steps when processing a Web Forms page. At each stage, the server calls a certain set of code. This enables the developer to add code at specific points during the execution of the page. Every time a page is requested, these steps are processed. The IsPostBack property indicates whether this is the first time a page is being viewed, or it is being viewed as a result of a server round-trip. The four page-processing stages are:

  • Configuration.

  • Event Handling.

  • Rendering.

  • Cleanup.

There are other stages that the page goes through when it is loaded, but these are not generally used in everyday page processing. These stages are primarily used for server controls to be able to initialize themselves when the page is loading, then render themselves into the page, and finally clean themselves up. These stages will be examined in Chapter 18, when looking at creating custom server controls.

Configuration Stage

This is the first stage that is encountered in processing a page. If this is during a postback (not the initial load), then the page and control ViewStates are restored. After that is done, the Page_Load event is fired. This means that any code in this event handler can access the state of any control on the page. This is very important because it allows you to perform the processing necessary to get the page ready for display to the user. A typical Page_Load event is:

VB .NET
  Sub Page_Load(Sender As Object, E As EventArgs)   Dim cart As IBuyAdv.CartDB   cart = New IBuyAdv.CartDB(getDSN())   If Len(Request.Params("ProductCode")) > 0 Then   cart.AddShoppingCartItem(GetCustomerID(), Request.Params("ProductCode"))   End If   If Not Page.IsPostBack Then   PopulateShoppingCartList()   UpdateSelectedItemState()   End If   End Sub  
C#
  void Page_Load(Object sender, EventArgs e) {   IBuyAdv.CartDB cart = new IBuyAdv.CartDB(getDSN());   if (Request.Params["ProductCode"] != null) {   cart.AddShoppingCartItem(GetCustomerID(),   Request.Params["ProductCode"]);   }   if (Page.IsPostBack == false) {   PopulateShoppingCartList();   UpdateSelectedItemStatus();   }   }  

In this Page_Load event from the case study in Chapter 24, there are three different steps taking place. These steps are quite common. First, you create an instance of a database access object:

 Dim cart As IBuyAdv.CartDB cart = New IBuyAdv.CartDB(getDSN()) 

or in C#:

 IBuyAdv.CartDB cart = new IBuyAdv.CartDB(getDSN()); 

This gives you access to a database object, which you use in the methods and events of the page. The next step is to conditionally perform some processing based on a parameter passed to this page:

 If Len(Request.Params("ProductCode") > 0 Then    cart.AddShoppingCartItem(GetCustomerID(), Request.Params("ProductCode")) End If 

or in C#:

 if (Request.Params["ProductCode"] != null) {    cart.AddShoppingCartItem(GetCustomerID(), Request.Params["ProductCode"]); } 

The Request object contains information about the request being made to the server. The Params collection is a collection of all the information contained in the QueryString , Form , ServerVariables , and Cookies collections. Based on the existence of a specific value, ProductCode , let's perform some processing.

 If Not Page.IsPostBack Then    PopulateShoppingCartList()    UpdateSelectedItemState() End If 

or in C#:

 if (Page.IsPostBack == false) {    PopulateShoppingCartList();    UpdateSelectedItemStatus(); } 

The last thing we do in the Page_Load event is to load the data into the controls on the page. Now since these controls have their ViewState saved during a server round-trip, we load the data only the first time. By checking the IsPostBack property of the Page object, we can determine if this is the first time the page is being loaded.

If it is the first time that the page is loaded, we go ahead and retrieve the data from the database and add it to control. If the page is being viewed as a result of a round-trip, then we just let the ViewState restore the state of the control. There is no reason you couldn't populate the control from scratch each time, but why waste the server processing time if it isn't necessary.

Event Handling Stage

The server controls on an ASP.NET page can generate events that get processed on the server. This means that an action on the client can initiate a server round-trip through an HTTP POST. Now once that round-trip gets to the server, we need to identify the control that caused the event to happen and then take steps to process the event.

While there can only be one event that causes a round-trip to begin, there may be other events that have occurred for (or in) the server controls at the client which haven't been processed yet. These are also processed during the event handling stage. There is no particular order in which the prior events are processed, but they are always processed before the event that actually triggered the round-trip. The event that actually triggered the round-trip is processed last.

Let's look at some event handling routines, again from the Chapter 24 case study.

VB .NET
  Sub Recalculate_Click(Sender As Object, E As EventArgs)   UpdateShoppingCartDatabase()   PopulateShoppingCartList()   UpdateSelectedItemStatus()   End Sub     Sub Checkout_Click(Sender As Object, E As EventArgs)   UpdateShoppingCartDatabase()   Response.Redirect("secure/Checkout.aspx")   End Sub  
C#
  void Recalculate_Click(Object sender, EventArgs e) {   UpdateShoppingCartDatabase();   PopulateShoppingCartList();   UpdateSelectedItemStatus();   }   void Checkout_Click(Object sender, EventArgs e) {   UpdateShoppingCartDatabase();   Response.Redirect("secure/Checkout.aspx");   }  

The Recalculate_Click event is fired when the user clicks the Recalculate button on the page. Since this is a button control, the round-trip is started immediately. The next section will look at the parameters that are passed when the event is fired. The Checkout_Click event is fired when the user clicks the Checkout button on the page. In the event handler, Response.Redirect is called to send the browser off to another page. So an event handler even has the ability to affect whether or not a page is displayed.

Rendering Stage

The rendering stage is where the rubber meets the road. Or to be more explicit, the HTML meets the browser. In this stage all the static HTML in the page, the results of any Response.Write methods, and the output from all of the server controls on the page are sent to the browser. Any in-line script code is run at the same time, but no event processing occurs, since that has already happened .

Cleanup Stage

This is the final stage of the page processing. The entire HTML has been rendered and sent to the browser. The primary event that happens during this stage is the Page_Unload event. This event should be used to do things like closing any open database connections, closing any files you may have opened, and properly discarding any objects that may have been used in the page. Object references can simply fall out of scope, but it's not good practice to rely on this.

Since objects in .NET are garbage-collected (as seen in Chapter 2) the resources that an object uses will still be consumed, even after the object reference falls out of scope. It is the responsibility of the garbage collector to free up these resources of unused objects. Since it is not possible to predict when the garbage collector will run, you can't explicitly state when the resources will be freed. So to free up the resources as soon as possible, explicitly close the objects used in the page.

Web Form Events

Events in web forms are different to the events used in the traditional event-driven programming model. While events can still be raised on the client and handled on the client, and events raised on the server and handled on the server, the primary Web Form event model is for an event to be raised on the client and processed on the server. This transfer of control from the client to the server is accomplished through the use of an HTTP POST . A developer needs to be aware how this mechanism takes place, but the .NET Framework takes care of figuring out from the POST information what events need to be handled on the server.

There's a set of intrinsic events that server controls will support. To avoid continually passing control from the client to the server, this event set is rather limited. It's primarily user interactions, such as a button click or changing a selection, which will cause an event to be raised on the server. In this way, it takes an explicit user action to fire a server event “ they don't usually happen without the user taking some action at the client. The event handler function declaration is the same for all events. There are two parameters that are passed to the event handler.

In C#:

  void MyButton_OnClick(Object sender, EventArgs e)   {   ...   }  

or in Visual Basic .NET:

  Sub MyButton_OnClick(Sender As Object, e As EventArgs)   ...   End Sub  

The first parameter, sender , is a reference to the server control that raised the event. When adding a server control to the page, explicitly state the events to handle, and what function will be the event handler for that particular event.

In the following example we place a Button server control into the page with the ID of PlaceOrder . When the user clicks on this button on the client, the control will be passed to the server via an HTTP POST . On the server, the PlaceOrder_Click function will be called:

  <asp:Button id="PlaceOrder" Text="Place Order"   onClick="PlaceOrder_Click" runat="server"/>  

Remember that there is a reference to the server control passed to the event handler. In this way, one event handler can handle events for multiple controls “ the specific control is identified from the value of the sender variable.

The second parameter is an object containing a set of information about the specific event. This parameter is usually of type EventArgs , which in its base implementation contains little information about the event. But it also serves as a base class for derived classes, such as the RepeaterCommandEventArgs class. An object of this type gets passed when using a Repeater control, and you have wired up the ItemCommand event. You can see this in the example in the Event bubbling section.

Event Bubbling

There are certain server controls that serve as containers for other controls. Controls such as the Repeater , the DataList , and the DataGrid can all contain server controls as children of the parent control. These child controls will not raise their events by themselves, to be handled on the page. Rather, the event is packaged by the container and passed to the page as an ItemCommand event. This event is raised when a button is clicked within the Repeater . This example looks at how to handle the events from a set of child buttons within a Repeater control:

  <%@ Page Language="C#" %>     <html>   <head>     <script language="C#" runat="server">     public class Authors {   private string name;   private string initials;   public Authors(string name, string initials) {   this.name = name;   this.initials = initials;   }     public string Name { get { return name; } }   public string Initials { get { return initials; } }   }     void Page_Load(Object Sender, EventArgs e) {   SmartNavigation = true;   if (!IsPostBack) {   ArrayList values = new ArrayList();   values.Add(new Authors("Alex Homer", "AH"));   values.Add(new Authors("Dave Sussman", "DS"));   values.Add(new Authors("Rich Anderson", "RA"));   values.Add(new Authors("Rob Howard", "RH"));   values.Add(new Authors("Brian Francis", "BF"));   MyRepeater.DataSource = values;   MyRepeater.DataBind();   }   }   void MyRepeater_ItemCommand(Object Sender, RepeaterCommandEventArgs e) {   ClickInfo.Text = "You selected the  " + ((Button)e.CommandSource).Text +   " button <br>";   }   </script>     </head>   <body>     <form runat="server">   <asp:Repeater id="MyRepeater" OnItemCommand="MyRepeater_ItemCommand"   runat="server">     <HeaderTemplate>   <table border="0" cellspacing="5">   <tr>   <td><b>Author</b></td>   <td><b>Initials</b></td>   </tr>   </HeaderTemplate>     <ItemTemplate>   <tr>   <td> <%# DataBinder.Eval(Container.DataItem, "Name") %> </td>   <td> <ASP:Button Text='<%# DataBinder.Eval(Container.DataItem,   "Initials") %>' runat="server" /></td>   </tr>   </ItemTemplate>     <FooterTemplate>   </table>   </FooterTemplate>     </asp:Repeater>   <asp:Label id="ClickInfo" font-name="Verdana" font-size="12pt"   runat="server"/>   </form>     </body>   </html>  

When the page is run, there will be a list of authors and their initials. When one of the buttons is pressed, the ClickInfo label control will be populated to indicate what button was pressed (see Figure 4-5):

click to expand
Figure 4-5:

In this example, you have a repeater control that contains a table (simply for the sake of formatting). Each row of the table has a cell with the author name, and another cell with a button server control:

 <td> <%# DataBinder.Eval(Container.DataItem, "Name") %> </td> <td> <ASP:Button Text="<%# DataBinder.Eval(Container.DataItem,                            "Initials") %>" runat="server" /></td> 

The button control itself does not have an event handler associated with it. It does have the all-important runat="server" parameter, so the events generated by this control will be handled on the server. So where is the click event from this button handled? Look at the container control “ the Repeater control:

 <asp:Repeater id="MyRepeater" OnItemCommand="MyRepeater_ItemCommand"                runat="server"> 

When declaring the Repeater control, set up an event handler function to handle the ItemCommand event. This event is fired whenever a control contained by the Repeater control raises an event. The MyRepeater_ItemCommand function looks like this:

 void MyRepeater_ItemCommand(Object Sender, RepeaterCommandEventArgs e) {    ClickInfo.Text = "You selected the " + ((Button)e.CommandSource).Text +                                                             " button <br>";    } 

The second parameter of this event handler is of type RepeaterCommandEventArgs . This object contains enough information about the event to determine the control that raised the event. The important properties of this object are:

Property

Description

CommandSource

Reference to the child server control that actually raised the event.

Item

Reference to the specific item within the Repeater control where the event took place. This could be the header or footer template, or from an individual data row.

The child control that caused the event is a Button control, so you can cast the CommandSource object to a Button type, and then access the properties of that control. In this case, you can pull out the value of the Text property for the button, and display that in the ClickInfo label control.

Event Handling on Client and Server

In bridging the gap between client and server when it comes to handling control events, it makes sense to talk about which side handles what events. Most server controls only have one or two events that actually get processed on the server. But the HTML control that the server control is finally rendered to can generate a great number of different events for client-side use. So the question is: what gets handled where? Basically, those events that are supported by the server control will get handled on the server. So for a Button control, there is one event supported by that control “ the Click event. All of the other events that can be generated by an HTML INPUT control (what a Button server control is rendered as) will need to be handled on the client. But what about the click event that can be handled at the client- side as well?

When you have an event that can be handled on either the client or the server, then the server handling of that event will take precedence. So in the case of the Button with a server-side event handler OnServerClick and a client-side event handler OnClick , the client-side code will be ignored. You could write a client-side onmouseup event handler, which would continue to run at the client “ prior to the control being passed back to the server.

Page State

Since the core to the functionality in Web Forms is the server round-trip, the application is constantly going back to the server and asking it to create a new page and send it to the client. It is in this merger of the stateless Web world with the stateful world that the concept of page state needs to be discussed. How do we retain information while the client has control and the server has in essence forgotten about the client and its prior request?

The Page viewstate is the way that the information contained in the server controls is automatically persisted during a server round-trip. So how can the developer store information from request to request, when that information may not be contained in a server control? In the past, the ways to do this would be to store information in a hidden form field, or possibly in the Session object. While these ways are still available, the Web Forms framework provides another more flexible option: State Bags .

The state bag is a data repository that is automatically persisted from page request to page request. Let's see how to add the use of state bag to the page in the event bubbling example already discussed:

  void Page_Load(Object Sender, EventArgs e) {   int viewCount;   if (ViewState["viewCount"] != null)   viewCount = (int)ViewState["viewCount"] + 1;   else   viewCount = 1;   labelViews.Text = "Times page has been viewed: " + viewCount.ToString();   ViewState["viewCount"] = viewCount;   if (!IsPostBack) {   ArrayList values = new ArrayList();   ...   }   }  

The ViewState property is a collection of the state that is maintained by the page. You can add keys to this collection, and their value is persisted along with state from all of the server controls on your page. This example stores the number of times the page is viewed, and then displays that value (Figure 4-6):

click to expand
Figure 4-6:

Page Directives

When creating a page, you can declaratively set a number of attributes about the page. Some of the ones you have seen already are the @ Page directive and the @ Import directive. Each of these directives has a set of associated attributes that control some aspect of the page generation.

@ Page Directive

This directive is used to assign page-specific attributes that are used by the Web Forms page parser and compiler to influence how the page is created. This directive, along will all the other ones can legally be placed anywhere on the page, but by convention they are generally at the top of the file. However, there can only be one @ Page directive in a single file. The attributes of the @ Page directive are as follows :

Attribute

Values (default in bold)

Used for

AspCompat

True or False

Sets the page to run in a single-thread apartment. Allows access to legacy COM components developed in VB, which could only create STA components

AutoEventWireup

True or False

Indicates whether or not the page events are automatically wired up. If False, events such as Page_Load must be enabled by the developer

Buffer

True or False

Response buffering is enabled

ClassName

Valid class name

Class name that this page is derived from

ClientTarget

Valid User Agent name

Browser (or compatible) that the page is targeting

CodePage

Valid code page value

Sets the code page of the response, if it is different from the web server

CompilerOptions

Valid compiler options

List of compiler options to be used when the page is compiled

ContentType

Valid MIME type

Sets the content type of the response

Culture

Valid culture ID

Culture ID sets the language, calendar system, and writing system.

Debug

True or False

Compiles page with debugging enabled

Description

n/a

Description of the page “ ignored by ASP.NET

Enable SessionState

True , ReadOnly, or False

Page has access to the Session object. ReadOnly “ the page can read but not change session variables

EnableViewState

True or False

Page ViewState is maintained for server controls

Enable ViewStateMac

True or False

Page should run a machine authentication check on the ViewState . Validates that the ViewState has not been tampered with by the client

ErrorPage

Valid URL

Page to be redirected to if an unhandled error occurs

Explicit

True or False

Uses the Visual Basic Option Explicit mode

Inherits

Valid class name

Code-behind class that this page inherits

Language

Valid .NET Language name

Language used to compile all sourcecode on the page

LCID

Valid locale ID

Locale identifier for the page, if different from the locale of the web server

ResponseEncoding

Valid character encoding name

Encoding format for the text sent by the response

SmartNavigation

True or False

Enables or disables the smart navigation feature (details appear later)

Src

Valid source file name

File name of the code-behind class used by this page.

Strict

True or False

Uses the Visual Basic Option Strict mode.

Trace

True or False

Tracing the page execution is enabled.

TraceMode

SortByTime or SortByCategory

Sort order for trace messages generated when the page is created.

Transaction

NotSupported, Supported, Required, RequiresNew

Indicates the transaction settings for this page.

ValidateRequest

True or False

Validate all of the information posted to the page against known dangerous string values.

WarningLevel

0, 1, 2, or 4

Compiler warning level at which compilation should be aborted.

@ Import Directive

This directive is used to explicitly import a namespace onto the page. This will make all of the classes and interfaces contained within this namespace available to code on the page. This value can either be a .NET Framework namespace name, or a valid user-created namespace:

  <%@ Import Namespace="  value  " %>  

There can only be one namespace imported per directive entry, so to import multiple namespaces into a page, add multiple @ Import directives. The .NET Framework automatically imports a set of namespaces, so these don't need to be imported explicitly. These namespaces are:

System

System.Web.Security

System.Collections.Specialized

System.Web.UI

System.Text.RegularExpressions

System.Web.UI.WebControls

System.Collections

System.Web.Caching

System.Configuration

System.Web.SessionState

System.Text

System.Web.UI.HtmlControls

System.Web

System.IO

@ Implements Directive

The @ Implements directive is used to implement a .NET interface in the page. In implementing an interface, the page will support the defined properties, methods, and events of a specific interface. This will be important when looking at implementing custom controls in Chapter 18. For a custom control to be able to respond to events like a standard server control, the control must implement the IPostBackEventHandler interface. The directive to do this is:

  <%@ Implements Interface="System.Web.UI.IPostBackEventHandler" %>  

Interfaces are covered in Chapter 3.

@ Register Directive

When you are adding a custom server control to a page, you need to tell the compiler something about that control. If the compiler doesn't know what namespace contains the control or what assembly that namespace is in, then it will not be able to recognize the control, and will generate an error. To give the compiler the information it needs, use the @ Register directive.

There are two forms of the @ Register directive, depending on how the location of the custom control is identified:

  <%@ Register TagPrefix="  tagprefix  " TagName="  tagname  " Src="  pathname  " %>   <%@ Register TagPrefix="  tagprefix  " Namespace="  namespace  " Assembly="  assembly  " %>  

The first usage of the @ Register directive is to add support for user controls to the page. The TagPrefix attribute identifies the string that will be used to decorate all instances of the custom server control on the page. For example, if this directive is at the top of the page:

  <%@ Register TagPrefix="Ecommerce" TagName="Header"   Src="UserControls\Header.ascx" %>  

Then for every instance of the Header user control on the page, it will be prefixed with Ecommerce , as seen here:

  <Ecommerce:Header id="Header" runat="server"/>  

The TagName attribute identifies the name that will be used to refer to the control within the page. Since a user control source file, UserControls\Header.ascx , can only have one control contained within it, the tagname attribute is simply a shortcut to allow us to reference the control.

The final attribute, Src , indicates the file in which the source of the user control resides.

The second usage of the @Register directive is for adding custom server controls to the page. These custom controls are compiled and contained within assemblies. The TagPrefix attribute has the same usage seen before “ it defines the namespace of the custom server control when it is used in the page. The Namespace attribute indicates the namespace in which the custom control resides. The Assembly attribute indicates the assembly where the namespace resides:

  <%@ Register TagPrefix="Wrox" Namespace="WroxControls"   Assembly="RatingMeter" %>  

When using this custom server control within the page, it looks no different than a user control in the same place:

  <Wrox:RatingMeter runat="server" Score="3.5" Votes="1"   MaxRating="5" CellWidth="51" CellHeight="10" />  

When creating a custom control, or even a user control, you can pass attributes to the control by adding them to the tag in the page. This is discussed in detail in Chapter 18.

@ Assembly Directive

The @ Assembly directive is used to reference an assembly directly, so that the classes and interfaces that it contains become available to the code in your page. Pass in the name of a compiled assembly:

  <%@ Assembly Name="  assemblyname  " %>  

or pass in the path to a source file that will be compiled when the page is compiled:

  <%@ Assembly Src="  pathname  " %>  

This tag is usually not required, as any assembly that is in the ASP.NET application's bin directory will automatically be compiled into the page. Use this directive to explicitly include an assembly that is in the global assembly cache, or to explicitly compile in an assembly source file that is residing in another directory.

@ OutputCache Directive

This directive is used to control how the page is cached on the server. ASP.NET supports a very powerful set of caching capabilities. When output caching is enabled for a page, the first time the page is requested, it is compiled and run, and the results are sent back to the browser. But instead of the server then throwing everything away, the results of that page are retained on the server. The next time a request comes in for that page, even from a different user, the server can then just spit back the results without having to rerun the page. This can tremendously increase performance, especially if there are database generated pages, but where the underlying data that creates the page doesn't change very often.

There are a series of attributes that control how the caching is performed:

  <%@ OutputCache Duration="#ofseconds"   Location="Any  Client  Downstream  Server  None"   VaryByCustom="browser  customstring  "   VaryByHeader="headers"   VaryByParam="  parametername  "   VaryByControl="  controlname  " %>  

The Duration attribute is used to control how long an item will stay in the cache before being invalidated. When a cache item is invalidated, the next time the page is requested, ASP.NET will run the page, deliver the results to the browser, and then store the results in the cache. This attribute is mandatory “ there is no default value, and the page will not compile if you leave it out.

The Location attribute identifies where the actual data for the cache is stored. There are five possible values for this attribute:

Value

Use

Any

The cache can be located on the client, on a downstream server (like a proxy server), or on a server where the request was originally processed. This is the default.

Client

The cache is located on the client that made the request.

Downstream

The cache is located on a server downstream from the server that processed the request.

Server

The cache is located on the server where the request was processed.

None

This page does not have output caching enabled.

The VaryByCustom attribute is used to identify any custom caching requirements. If the string " browser " is passed as the value for this attribute, the cache will be varied by browser name and version. When a cache is varied, there is a different processed page stored for each condition that the cache is varied by. For example, if the cache is varied by browser, there will be one page version stored for IE 6, another one for IE 5, and yet another for Netscape 6. If someone accessed the site using Opera, then since that browser type was not cached, a new page would be generated, passed back to the browser, and then cached for the next request from an Opera browser.

The VaryByHeader attribute contains a list of HTTP headers (separated by semicolons) that are used to vary the output cache. The cache can vary on one header, or on a group of headers.

The VaryByParam attribute is used to vary the cache, based on the values of parameters passed to the server along with the request for the page. These can be QueryString parameters, or they can be the contents of form fields. This can contain a single parameter, multiple parameters separated by semicolons, or a * , which means to vary on all parameters, or none , which means that the cache will not be varied based on any parameters. You must supply a value for this attribute. If you don't want to vary by parameter, then pass a value of none . To not explicitly state which parameters to vary the cache by, then pass a * and the cache will be varied by all parameter values.

In a user control, the VaryByControl attribute can be used to vary the cache based on controls contained in the user control. The attribute contains a list of child control IDs to vary the cache by. This attribute is required in a user control that is using the @OutputCache directive. It is not allowed on an ASP.NET page.

@ Reference Directive

This directive is used to identify a page or control that the current page should dynamically compile and link with at runtime. This allows you to dynamically add a user control to a page at runtime. You should use this directive in conjunction with the LoadControl method of the Page object. By adding the custom control as a reference to the page, the compiler will be able to perform strong type checking against the control. Let's see how to use this in Chapter 18 when we look at custom controls.




Professional ASP. NET 1.1
Professional ASP.NET MVC 1.0 (Wrox Programmer to Programmer)
ISBN: 0470384611
EAN: 2147483647
Year: 2006
Pages: 243

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net