Code-Behind





Code-Behind

One of the more frustrating aspects of building traditional ASP pages was the convoluted nature of mixing server-side script with static layout elements. As ASP pages grow in size, they often become harder and harder to understand as the interplay between server-side script and static HTML becomes more complex. There are ways of dealing with this complexity, including imposing standards mandating scripting techniques and the use of server-side include directives to remove some of the code from a page. ASP.NET adds another, even more appealing option for separating programmatic logic from static page layout with a technique called code-behind.

In our earlier examples, we saw how each page in ASP.NET is compiled into a Page-derived class. Code-behind is the technique of creating an intermediate base class that sits between the Page base class and the machine-generated class from the .aspx file. This intermediate base class derives directly from Page, and the class generated from the .aspx file derives from the intermediate base class instead of directly from Page. With this technique, you can add fields, methods, and event handlers in your code-behind class and have these features inherited by the class created from the .aspx file, removing potentially significant amounts of code from the .aspx file. This technique relies on the ability to specify an alternative base class for the autogenerated class, which is done using the Inherits attribute of the Page directive. Listings 1-5 and 1-6 show our earlier sample page rewritten to use this code-behind technique.

-5 Sample .aspx File Using Code-Behind
<!-- Codebehind.aspx -->
<%@ Page Language="VB"
    Inherits="EssentialAspDotNet.Architecture.SamplePage"%>
<html><body>

<h2>aspx = class!</h2>
<ul>
<% WriteArray() %>
</ul>
</body> </html>
-6 Sample Code-Behind File
' SampleCodeBehind.vb

Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Collections

Namespace EssentialAspDotNet.Architecture

Public Class SamplePage
  Inherits Page

  Private _values As ArrayList = New ArrayList()

  Public Sub New()
      _values.Add("v1")
      _values.Add("v2")
      _values.Add("v3")
      _values.Add("v4")
  End Sub

  Protected Sub WriteArray()
    Dim i As Integer
    For i = 0 To _values.Count-1
      Response.Output.Write("<li>{0}</li>", _values(i))
    Next i
  End Sub
End Class

End Namespace

Note that we were able to move the initialization into the constructor of our class. This code-behind class must be compiled into an assembly and deployed in the /bin directory of this application for this to work (as we will see, all assemblies placed in the /bin directory of an ASP.NET application are implicitly added as references to the page compilation command). The class created from our .aspx file is now derived from SamplePage instead of Page because we are inserting a class into the hierarchy, as shown in Figure.

Class Hierarchy Created Using Code-Behind

graphics/01fig04.gif

As an alternative to precompiling the code-behind file, you can use the src attribute, as shown in Listing 1-7. Any file referenced with the src attribute of the Page directive is compiled into a separate assembly and added to the list of referenced assemblies when the page is compiled. The advantage of using the src attribute for your code-behind files is that you can update a code-behind file just by replacing the file, and the next request that comes in causes ASP.NET to recompile the referenced file. This saves the step of compiling the code into an assembly yourself and updating the physical assembly in the /bin directory. It also ensures that the file will be compiled with the correct version of the .NET libraries, if for some reason you have different versions installed on different machines. On the other hand, if you have a compilation error in your source file, it will not be detected until you deploy the file and the page is accessed again. Precompiling the assembly beforehand guarantees that you will catch all compilation errors before deployment.

-7 Using the src Attribute to Reference a Code-Behind File
<!-- Codebehind.aspx -->
<%@ Page Language="VB" src="SampleCodeBehind.vb"
    Inherits="EssentialAspDotNet.Architecture.SamplePage"%>
<!-- ... -->

1 Event Handling

The code-behind example shown in the previous section extracted code from the .aspx page by defining methods and fields in the code-behind class, resulting in a "cleaner" page with less code clutter. In addition to methods and fields, code-behind classes can define handlers for events issued by the Page base class, which can be a useful way to manipulate the rendering of a page without adding code to the .aspx file.

The Page base class defines four events (actually, they are inherited from the Control base class) that are called in sequence during its lifetime: Init, Load, PreRender, and Unload, as shown in Listing 1-8. In addition, it defines four virtual function handlers for these events that are invoked when the events are fired, also shown in Listing 1-8. Thus, you can register a handler for any of these events in two ways: by subscribing a delegate to the event manually or by providing an overridden version of the virtual function defined in the base class.

-8 Events Defined in System.Web.UI.Page
Public Class Page
  Inherits TemplateControl
  Implements IHttpHandler

  ' Events
  Public Event Init      As EventHandler;
  Public Event Load      As EventHandler;
  Public Event PreRender As EventHandler;
  Public Event Unload    As EventHandler;

  ' Predefined event handlers
  Overridable Protected Sub OnInit(e As EventArgs)
  Overridable Protected Sub OnLoad(e As EventArgs)
  Overridable Protected Sub OnPreRender(e As EventArgs)
  Overridable Protected Sub OnUnload(e As EventArgs)

End Class

The Init event occurs before any server-side controls have had their state restored. The Load event occurs after all server-side controls have had their state restored but before any server-side events have been fired. The PreRender event fires after all server-side events have fired but before anything has been rendered—that is, before any HTML has been returned. The Unload event takes place after page rendering has completed.

These events give you fairly complete control over the generation of the page; however, Load is typically the most useful of all of them because it gives you a chance to modify the state of controls before rendering but after their state has been restored. The server-side control model is discussed in detail in the next chapter.

For an example of adding handlers for these events, consider the code-behind class shown in Listing 1-9. This demonstrates a technique used by Visual Studio .NET when working with code-behind files, which is to provide an overridden version of the virtual OnInit method to manually subscribe a delegate to the Load event of the Page class. In this case, the OnInit virtual function is called first, in which the delegate for the Load event is subscribed. When the Load event fires, the MyLoadHandler is invoked and prints a line of text at the top of the page since it is invoked before the page's rendering process. Note that there is no real advantage or disadvantage to either technique, except that the manual delegate subscription requires an extra step to actually hook it up.

-9 Trapping Page Events in Code-Behind
' File: EventsPage.vb

Public Class EventsPage
  Inherits Page

  ' Override OnInit virtual function to manually
  ' subscribe a delegate to the Load event
  Protected Overrides Sub OnInit(e As EventArgs)
    AddHandler Me.Load, New EventHandler(AddressOf MyLoadHandler)
  End Sub

  ' Load event handler
  Protected Sub MyLoadHandler(src As Object, e As EventArgs)
    Response.Write("<tiny>rendered at top of page</tiny>")
  End Sub

End Class

There is one additional mechanism for subscribing to events issued by the Page class called AutoEventWireup. This technique works by simply adding a method to your Page-derived class, named Page_Init, Page_Load, Page_PreRender, or Page_Unload, with the signature required by the EventHandler delegate. When the Page-derived class is created, one of the initialization steps it goes through uses reflection to look for any functions with these exact names. If it finds any, the initialization routine creates a new delegate initialized with that function and subscribes it to the associated event. Listing 1-10 shows a sample .aspx file that has defined a Page_Load method that is wired up using this technique. Note that this function was not a virtual function override nor was it explicitly wired up as an event handler in our code. This technique works similarly in code-behind classes as well.

-10 Using AutoEventWireup to Add an Event Handler
<!-- AutoEventWireup.aspx -->
<%@ Page Language='VB' %>

<script runat=server>
  Protected Sub Page_Load(src As Object, e As EventArgs)
    Response.Write("<h4>Load event fired!</h4>")
  End Sub
</script>

<html>
<body>
<h1>AutoEventWireup Page</h1>
</body>
</html>

Unlike the other two mechanisms for subscribing to events, this mechanism has the disadvantage of relying on runtime type information to look up the method name and perform the event subscription, which is less efficient. If you know you are not going to take advantage of this event subscription technique, you can disable the runtime type lookup by setting the AutoEventWireup attribute of the Page directive to false, as shown in Listing 1-11. If you are building pages with Visual Studio .NET, this flag is set to false in any pages you create with the designer.

-11 Disabling AutoEventWireup
<%@ Page Language='VB' AutoEventWireup='false' %>
<!-- ... -->

     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows