From the August 2001 issue of MSDN Magazine

MSDN Magazine

Reusability in ASP .NET: Code-behind Classes and Pagelets
Dino Esposito
Download the code for this article:Cutting0108.exe (39KB)
T

he programming interface of ASP .NET provides an extremely rich framework of controls that you can use to populate server pages. Most of these controls offer a huge number of properties, methods, and events that can be used to create even more specialized components. For example, in this column I recently covered all the features that DataGrid controls expose, including sorting, paging, selection, column templates, and in-place editing. Using these properties results in a control that's significantly more functional than a regular DataGrid—even though a DataGrid is a highly customizable control.
      To implement all of these functions, you normally have to write delegates for up to six or seven events. The complexity of these delegates depends largely on the exact tasks they accomplish. However, in most real-world scenarios they take up dozens of lines of code all wrapped by a <script runat="server"> tag.
      Having all this code concentrated within a unique tag is a good thing. However, it ends up padding your ASPX files with page-specific variables, logic, classes, and user-defined functions. An ASPX page comprises three logical elements: directives, code, and layout. While directives and layout are obviously and inextricably tied to a specific page, code may contain a certain degree of reusability and may be formed by classes, functions, and controls that are shared by multiple pages in the same application or even by different applications on the same server machine.
      For the past several months I've been writing about ASP .NET, and in this month's installment I'll dig through the so-called code-behind technique and review its impact on project management and development. Then, I'll take a look at a very special category of components called user controls. They look like user-defined controls, but their composite nature—a mix of HTML and code—make them more similar to embedded and programmable pages. Finally, I'll introduce custom server controls as the finishing touch in the ASP .NET component architecture.

Code-behind

      An ASPX page represents the source code of a Web Forms page—the new model of Web applications introduced by Microsoft® .NET. Web Forms stem from rapid application development tools like Visual Basic®.
      In Visual Basic, a form is divided into two distinct parts: the graphical component-based layout and the code that interacts with the constituent controls. However, in Visual Basic this logical and neat distinction gets lost when the form is serialized to disk. In fact, you always have a single .frm file with both code and layout information. The IDE lets you work on code and layout in a fairly independent way, but doesn't allow two people (say, a programmer and a designer) to work on the same form at the same time.
      In ASP, things are much worse; the structure of the language simply doesn't allow for a neat separation of layout and code. On the other hand, this has been a key to the success of ASP as a Web technology—the mix of script and HTML has made it easy for people with a wide range of skills to write simple, effective Web pages. ASP .NET allows better separation of layout and code elements.
      As I mentioned earlier, there are three elements in an ASPX page: directives, layout, and code. A directive just inserts messages for the compiler and browser that will process the page. It specifies the language used, enables or disables runtime services such as tracing and state maintenance, indicates the transaction support required, specifies the local code page identifier (LCID) and the page to which the user should be redirected in case of trouble.
      The layout is the template of what you want the browser to receive. Usually, this is HTML code, but the exact MIME type can be specified through the ContentType attribute of an @Page directive. For instance, this specifies that XML will be returned:

  <%@ Page ContentType="text/xml" language="VB" %>
  

 

For a complete description of ASP .NET page directives, see the documentation for @Page at https://msdn.microsoft.com/library/en-us/cpgenref/html/cpconpagedirectives.asp.
      For the layout, server controls, which are processed by the HTTP runtime, can be used to produce output for the browser. Server controls are not instantiated via a function or a method call (such as CreateObject), but are inserted declaratively through markup tags belonging to a custom or a system-provided namespace.
      In declarative programming, you configure a control by setting its attributes. While this may meet many of your needs, for a highly interactive page, you usually need to write code against your server controls. This code consists of the user interface logic that lets your users interact with the Web form.
      Code-behind is the ASP .NET feature that allows you to store directives and the page layout in one ASPX file and store the Visual Basic or C# code that's behind the page (that drives the page) in a separate file. The link to the code-behind file is kept as an attribute of the @Page directive. When users point their browsers to the ASPX page, the code-behind class file runs and dynamically produces the output for the page. When the page is invoked, the code-behind code is loaded and treated as an integral part of the page.
      Before delving into details of how this can be implemented and how it actually works, let me first emphasize the practical advantage of this approach. Developing a Web Form can involve two distinct groups of people at different times: designers and programmers. Once the structure of the page has been defined in terms of the number, names, and types of the constituent server controls, HTML designers can take the ASPX file and make it appear as friendly and compelling as possible. They decide the position of the various controls, colors, fonts, and even client-side scripting. At the same time, when working with Visual Basic and C#, programmers develop the code and delegates required for the page to work properly and fulfill user expectations.
      This is the same approach to form development you used in Visual Basic 6.0, but this approach allows you to introduce a physical separation between layout and code.

Enabling Code-behind

      You enable the ASP .NET code-behind feature by adding a couple of attributes to the @Page directive.

  <%@ Page Language="VB" 
  
      Inherits="StdPageClass" 
  
      Src="StdPage.vb" %>
  

 

      You can only have one @Page directive per .aspx file. Multiple attributes are defined through a space-separated list, with no space around the equals sign.
      The Inherits attribute represents the code-behind class for the page to inherit from. It can be the name of any class derived from the Page class. The source attribute (Src) represents the URL of the source file containing the code to compile for the code-behind class. An ASPX page is then stored as a compiled, time-stamped piece of code available to browsers via its .aspx URL.
      When the HTTP runtime module is pointed to this ASPX page, it analyzes the @Page directive. If the Src is present, then it loads the given code-behind file. Next, after a successful compilation, the HTTP runtime utilizes the content of the Inherits directive to get the name of the class from which the current page must be instantiated. Any ASPX page displayed through a browser, in fact, is seen on the Web server as a running instance of a class derived from the Page class of the .NET Framework. If you don't use code-behind, then the source code you insert in the various <script runat="server"> tags are considered private methods or overrides of a particular instance of a Page class. Otherwise, by choosing the code-behind approach, you can control the base class of the page and make it automatically inherit from previously coded classes.
      The Inherits attribute can also point to a custom class scoped in a particular namespace.

  <%@ Page Language="C#" 
  
      Inherits="MyDotNetLib.StdPageClass" 
  
      Src="StdPage.cs" %>
  

 

In this case, the StdPage.cs file may look like the following:

  namespace MyDotNetLib {
  
      using System;
  
      using System.Collections;
  
      using System.Data;
  
      using System.Web.UI;
  
      using System.Web.UI.WebControls;
  
  
      public class StdPageClass : Page {
  
      } 
  
  }
  

 

      The ability to work with a class lets you declare and create public data members in a much more elegant and effective way than just inserting their declarations outside of a procedure in a <script> tag. By making your globals members of a class, you can better control their visibility. In addition, you can define a code-behind class starting from an existing subclass that already inherits from Page. When you inherit from Page, or a Page-based class, you are taking advantage of the wonderful feature called visual inheritance—the backbone of Windows® Forms.
      Aside from the fact that you have to explicitly write a Page-derived class instead of a simpler list of methods and variables declaration, there's really nothing to differentiate a normal ASPX page from a code-behind one. When the page is invoked for the first time, if it is code-behind then the code generator class initializes itself from the source file read from the Src attribute. Otherwise, it builds up using the string extracted from the <script> tag. In any case, users won't detect any difference.
      When you write code for a code-behind page you must be aware of one key point. You can safely access the runat="server" controls declared in the layout only after declaring them as protected or public members of your page class. For example, if you have two textboxes like these

  <asp:textbox id="fname" runat="server" />
  
  <asp:textbox id="lname" runat="server" />
  

 

you can't just use their IDs in code-behind classes. Instead, you must first declare them as protected or public class members:

  protected TextBox fname, lname;
  

 

      Keep them as private (either through the keyword private or by using no qualifier at all) and you'll get a runtime error. The error is due to an unsuccessful attempt to dereference a null object. In fact, there would be nothing in your code to initialize such controls.
      In Figure 1, you can see the source code of a minimal code-behind page with just two textboxes whose default value is set within the OnLoad override in stdpage.cs. The actual page is shown in Figure 2.

Figure 2 Code-behind Page
Figure 2 Code-behind Page

      In Figure 1 notice the role of the base keyword that lets you invoke the same method on the parent class.

  base.OnLoad(e)
  

 

      In the sample code, the two textboxes are read-only. Unlike the Windows Forms TextBox control, the TextBox Web control doesn't have a ReadOnly property—at least as of Beta 1. So if you want to make them read-only through code, you must set the read-only attribute in much the same way as you would in HTML:

  fname.Attributes["readonly"] = "true";
  
  lname.Attributes["readonly"] = "true";
  

 

      Now, suppose that you want to create a new page allowing users to edit the content of the textboxes and submit it to the server. If you've used a code-behind class, this is easy, thanks to class inheritance. In Figure 3, you can see the ASPX file with the layout of a new page called myeditpage.aspx. It contains a button and editable textboxes. When clicked, the button runs the OnEdit procedure defined in the code-behind class.

  <% @ Page Language="C#" 
  
      Inherits="MyDotNetLib.EditPage" 
  
      Src="EditPage.cs" %>
  

 

      You can derive the page from a new class called EditPage defined in editpage.cs. This class, in turn, could inherit from StdPage, building a true hierarchy of page classes specific to your application.

  public class EditPage : MyDotNetLib.StdPage {
  
      protected Button btnEdit;
  
      protected Label statusbar;
  
  •••
  
  }
  

 

      Figure 4 shows the source of the EditPage class. It inherits from StdPage, gets free access to the fname and lname textboxes, and defines the handler for the button's click event. In order for this code to work, you must have a compiled version of StdPage available in the Web server's Bin directory or the application's Bin directory. The following command compiles stdpage.cs to a DLL assembly.

  csc /t:library /r:system.web.dll /r:system.dll stdpage.cs
  

 

Figure 5 shows the output of the demo page.

Figure 5 EditPage in Action
Figure 5 EditPage in Action

      Code-behind is an interesting technique that has been primarily designed to let you write more modular code. Its main goal is to enable you to keep code and layout physically separate. In doing so, you can exploit the native object-orientation of the .NET Framework to build a hierarchy of page classes that provide more specialized functions. All the objects you manipulate are descendants of the Page class and, as such, must be specialized and customized instances of Page. As a result, with code-behind you get modularization plus a little bit of page-level reusability. Unfortunately though, there's nothing in the layout that you can reuse. You can derive a code-behind class from one you've previously defined, but the layouts of the two pages are fairly independent entities.

Toward Embeddable Forms

      Pagelets represent another level of page reusability and, in fact, they're more reusable because of their finer granularity. With pagelets, forms—not pages—are the unit of reusability. A pagelet can be seen as an embeddable Web Form. You define one in much the same way as a Web Form, then give it a name and treat it as a component. If you are familiar with DHTML scriptlets, you can think of pagelets as their ASP .NET counterparts.
      However, unlike forms, pagelets are visual components that you import and use in your ASP .NET pages through a given namespace and tag name. In other words, you declare them as you would any other standard .NET server control. You code against them using the properties and methods they expose.
      You write pagelets as self-contained ASP .NET pages, complete with layout and code blocks. The layout represents the user interface of the component—the controls making the form. The internal code ties together the various constituent controls and implements their business logic. Here's a quick example of a pagelet, taken from the Beta 1 documentation.

  <script language="C#" runat="server">
  
    public String Color = "blue";
  
    public String Text = "Message User Control!";
  
  </script>
  
  
  <span id="Message" 
  
  style="color:<%=Color%>"><%=Text%>
  
  </span>
  

 

This code is saved in a file with an .ascx extension. The content of the <script> tag defines both the programming interface of the component and its internal code. Everything that is declared public is visible outside the control and is externally callable by clients.
      The HTML portion of the code (which looks more like ASP because of the <%...%> blocks) is the user interface of the control. This produces the output that's injected in to the host page as the result of the pagelet declaration.
      To become usable, a pagelist must first be registered within an ASPX page. This is done through the @Register directive:

  <%@ Register 
  
      TagPrefix="expo" TagName="Message" 
  
      Src="pagelet.ascx" %>
  

 

The TagPrefix and TagName attributes indicate the namespace prefix and the tag name that will identify the control within the page. In this case:

  <expo:Message>
  

 

      The Src attribute points to the source code for the pagelet. Figure 6 shows the pagelet-enabled version of the ASP .NET page you saw in action in Figure 5. The pagelet uses a syntax similar to ASP to make use of publicly callable properties and methods in its internal code. For example, if the user sets the pagelet's Color property to red, the code substitutes the property value in its style tag like this:

  ... style="color:<%=Color%>">
  

 

      When the property tag is replaced with its actual value, the tag looks like this:

  ... style="color:red">
  

 

      Despite appearances, though, this code couldn't be further from ASP. The <%...%> symbols merely identify a sort of data-bound piece of code. The binding is not between the control and a data source, but between the control and its container environment.

User Controls

      With a few modifications, virtually any Web Form can be encapsulated in a pagelet user control, thereby becoming a component that you can embed in another page. Pagelets provide an easy way to modularize and reuse simple, common user interface functionality across Web applications. Pagelets are compiled before use and stay cached on the server until expiration or modification occurs.
      When the HTTP runtime module—the system component that processes ASP .NET pages—encounters a pagelet, it promptly generates a wrapper class for it whose name is based on file name and extension. For message.ascx, the class name is ASP.message_ascx.
      So how do you take a Web form you've written and transform it into a user control? First, make sure that the pagelet does not contain any <html>, <body>, or <form> tags. Since pagelets are merged with the layout of the Web Form that hosts them, these tags can cause nesting problems. Bear in mind that only controls that want to take advantage of postback events actually need to be enclosed in a <form runat="server"> tag. Furthermore, if the page you're converting into a pagelet contains an @Page directive, change it to an @Control directive or the HTTP runtime will complain. Once you've cleaned out these unwanted HTML tags, rename the file with an .ascx extension. This is the key that will enable special treatment on the file.
      The @Control directive is almost as powerful as the @Page directive, except for a couple of unsupported attributes: Trace and OutputCache. This isn't really bad news—if you need tracing, just enable it at the page level and it will automatically work for the pagelet as well. If you need to control the caching policies for the page, you can also do that at the page level. You can't just have different caching policies for different portions of the page.
      The ability to set an @Control directive for a pagelet makes it possible to use different languages to write the pagelet and the hosting page. For example, you can use Visual Basic in the pagelet and C# in the calling page. The Message pagelet can be rewritten in Visual Basic like the following code and still work in a C# page.

  <% @ Control Language="VB" %>
  
  <script runat="server">
  
    public Color As String = ""
  
    public [Text] As String = "" 
  
  </script>
  
  <span style="color:<%=Color%>"><%=[Text]%> </span>
  

 

Notice that you must wrap the word Text with square brackets to mean that you want it to be a custom property in Visual Basic only. This is because Text is a reserved keyword in Visual Basic .NET; it's part of the Option Compare statement.

Custom Server-side Controls

      The .NET Framework is a hierarchy of classes that lets you write new classes according to the principles of object-oriented programming using inheritance. You can write a new class and make it inherit the behavior and the interface of an existing one. (Multiple class inheritance is not allowed in .NET, but one class can implement as many interfaces as it needs.)
      If you have a function you want to share across applications, nothing is easier than writing a new class. This is just one of the features of .NET and OOP. In the context of ASP .NET, though, you have another interesting possibility that's on par with code-behind classes, pagelets, and regular classes: server controls. ASP .NET pages are made of server controls—components that you declaratively place on a form and tie together through code. Although the System.Web.UI.WebControls namespace has a lot of classes to offer, sometimes you may need to write your own. What's the difference between server controls and pagelets? A user-defined server-side control is an explicit class placed in the Control class tree. In contrast, while pagelets are treated like classes, they aren't written and exposed as part of the framework.
      Furthermore, server controls have no built-in user interface. Their source is not divided into layout and code sections. A pagelet looks like a page that is embedded in another one and then treated like a control. By contrast, a server control is a .NET class specifically designed to work within an ASP .NET page. This is due to the way ASP .NET server controls are inherited—they use the Control class as a base. This means that they inherit the ability to be included on a Web Form as well.
      The code in Figure 7 illustrates a very simple control built from a TextBox control. It differs from the parent object only in that it displays default text instead of an empty buffer. This control can be used wherever a TextBox is accepted and can be used as the starting point for building more specialized controls.
      To host the control on an ASP .NET page, you use the @Register directive (see Figure 8). As you can see, the derived control automatically supports all the properties and methods of the base class, including CssClass and, of course, the runat directive.
      A pagelet doesn't need compilation prior to use. A control, however, must be explicitly compiled to a library and placed in the Bin directory of the Web server. For more on server controls, see the Visual Programmer column in the October 2000 and January 2001 issues of MSDN Magazine.

Next Month

      There's a lot more to say about ASP .NET custom server controls, and I'll look at them in more depth in my next column. In particular, I'll be building several progressively more complex controls to illustrate how powerful inheritance can be. One of the examples I will show next time illustrates how to build custom controls that have children, like a DataGrid.

Send questions and comments for Dino to cutting@microsoft.com.

Dino Esposito is a trainer and consultant based in Rome, Italy. Author of several books for Wrox Press, he now spends most of his time teaching classes on ASP .NET and ADO .NET for Wintellect (https://www.wintellect.com). Get in touch with Dino at dinoe@wintellect.com.