O'Reilly Book Excerpts: ASP.NET in a Nutshell
User Controls and Custom Server Controls, Part 1
In part one from this series of book excerpts from ASP.NET in a Nutshell, get an overview on ASP.NET controls, and learn about ASP.NET user controls.
Reuse is a technique that is important to most developers. Reuse allows you to avoid constantly reinventing the wheel by using functionality that has already been built and tested. It increases productivity, by reducing the total amount of code you need to write, and reliability, since by using tested code, you (presumably) already know the code works reliably.
ASP.NET provides a range of options for reuse. The first is the wide variety of built-in server controls that ship with ASP.NET. These server controls alone can eliminate hundreds, or even thousands, of lines of code that needed to be written to achieve the same effect in classic ASP. In addition, the .NET Framework Class Library (FCL) provides hundreds of classes to perform actions (such as sending SMTP email or making network calls) that in classic ASP would have required purchasing a third-party component or making calls into the Win32 API.
Going hand-in-hand with reuse is the concept of extensibility, the ability to take the existing functionality provided by the .NET Framework and ASP.NET and extend it to perform actions that are more tailored to your particular applications and problem domains. ASP.NET provides a significant number of avenues for extensibility:
- Custom server controls
- Allow you to create entirely new controls for use with ASP.NET or to derive from existing controls and extend or modify their functionality.
- As in classic ASP, components are the primary means for extending an ASP.NET application by encapsulating the application's business logic into an easily reusable form. With the .NET Framework, it's easier than ever to build components, and components are more interoperable across languages than in the COM world. .NET components can also communicate with COM components through an interoperability layer.
- HttpHandlers and HttpModules
- HttpHandlers are components that are called to perform the processing of specific types of requests made to IIS. HttpModules are components that participate in the processing pipeline of all requests for a given ASP.NET application. These extensibility techniques are beyond the scope of this book, but you can get answers to questions on these topics at http://www.aspfriends.com/aspfriends/aspnghttphandlers.asp.
The rest of this chapter discusses employing ASP.NET user controls and custom server controls for reuse and employing custom server controls for extensibility. The chapter also explains how custom server controls can easily be shared across multiple applications, making reuse simpler than ever.
The simplest form of reuse in classic ASP is the include file. By adding the following directive:
<!-- #include file = "filename.inc" -->
ASP developers could place the contents of the specified file inline with the page in which the directive appeared. Unfortunately, this reuse technique is a bit crude and sometimes makes applications harder to debug.
While ASP.NET still supports include files, a better way to provide the same kinds of reuse is through a new feature called user controls. User controls consist of HTML, a server-side script, and controls, in a file with the .ascx file extension. When added to a Web Forms page, ASP.NET treats user controls as objects; these user controls can expose properties and methods like any other object. The rendered output of user controls can also be cached to improve application performance.
Example 6-1 shows a simple user control that provides navigational links to other examples in this chapter. The user control appears in each example page to demonstrate how the use of a user control can provide a single point for modifying such frequently used elements as headers, footers, and navigation bars.
Example 6-1: Nav.ascx
<%@ Control Language="vb" %> <table cellpadding="0" cellspacing="0"> <tr> <td valign="top"> <strong>Navigation Bar</strong><br/> <hr width='80%'> <a href="NavBarClient.aspx" onmouseover="img1.src='node_rev.jpg';" onmouseout="img1.src='node.jpg';"> <img border='0' align='absMiddle' alt='NavBar Client' src='node.jpg' id='img1' name='img1'></a> <a href="NavBarClient.aspx" onmouseover="img1.src='node_rev.jpg';" onmouseout="img1.src='node.jpg';">NavBar Client</a> <hr width='80%'> <a href="UCClient.aspx" onmouseover="img2.src='alt_node_rev.jpg';" onmouseout="img2.src='alt_node.jpg';"> <img border='0' align='absMiddle' alt='User Control Client' src='alt_node.jpg' id='img2' name='img2'></a> <a href="UCClient.aspx" onmouseover="img2.src='alt_node_rev.jpg';" onmouseout="img2.src='alt_node.jpg';">User Control Client</a> <hr width='80%'> <a href="BlogClient.aspx" onmouseover="img3.src='node_rev.jpg';" onmouseout="img3.src='node.jpg';"> <img border='0' align='absMiddle' alt='Blog Client' src='node.jpg' id='img3' name='img3'></a> <a href="BlogClient.aspx" onmouseover="img3.src='node_rev.jpg';" onmouseout="img3.src='node.jpg';">Blog Client</a> <hr width='80%'> <a href="BlogAdd.aspx" onmouseover="img3.src='alt_node_rev.jpg';" onmouseout="img3.src='alt_node.jpg';"> <img border='0' align='absMiddle' alt='Add New Blog' src='alt_node.jpg' id='img3' name='img3'></a> <a href="BlogAdd.aspx" onmouseover="img3.src='node_rev.jpg';" onmouseout="img3.src='node.jpg';">Add New Blog</a> <hr width='80%'> </td> </tr> </table>
With the exception of the
@ Control directive, which is not strictly required, the code in Example 6-1 consists exclusively of HTML and client-side script (for performing a simple mouseover graphics switch). However, the user control could just as easily contain server controls and/or server-side script to perform more complicated tasks.
@ Control directive performs essentially the same task as the
@ Page directive, only for user controls. Chapter 3 lists the attributes of the
@ Page and
@ Control directives and the purpose of each.
The advantage of using a user control for this type of functionality is that it places all of our navigation logic in a single location. This placement makes it considerably easier to maintain the navigation links for a site. If you used ASP.NET's built-in server controls instead of raw HTML in your navigation user control, you could manipulate those server controls programmatically from the page on which the control is used. For example, you could hide the link to the page that's currently displayed or highlight it in some fashion.
The disadvantage of a user control is that it is not reusable across multiple sites. It's also not usually a good idea to tightly couple user interface elements and data, as this control does, because doing so tends to reduce the reusability of a control. Later in this chapter, you'll see how to improve this user control by turning it into a custom server control.
User controls are made available to a page through the use of either the
@ Register directive, which prepares a user control on a page declaratively (i.e., using a tag-based syntax like server controls), or to be included programmatically using the LoadControl method of the
TemplateControl class (from which both the
Page class and the
UserControl class derive).
Example 6-2 shows a page that uses the
@ Register directive and a declarative tag to create the user control shown in Example 6-1. The
@ Register directive in Example 6-2 tells ASP.NET to look for any
<aspnetian:nav> tags with the
runat="server" attribute, and when it finds one, create an instance of the user control and place its output where the tag is located. This allows us to place our control very precisely.
Example 6-2: UCClient.aspx
<%@ Page Language="vb" %> <%@ Register TagPrefix="aspnetian" TagName="nav" Src="Nav.ascx" %> <html> <head> </head> <body> <table border="1" width="100%" cellpadding="20" cellspacing="0"> <tr> <td align="center" width="150"> <img src="aspnetian.jpg"/> </td> <td align="center"> <h1>User Control Client Page<h1> </td> </tr> <tr> <td width="150"> <aspnetian:nav runat="server"/> </td> <td> This is where page content might be placed <br/><br/><br/><br/><br/><br/><br/><br/><br/> </td> </tr> </table> </body> </html>
You can instead create the control dynamically using the LoadControl method and add the control to either the Controls collection of the page, or, better yet, to the Controls collection of a PlaceHolder control. The latter allows you to control the location of the user control based on the location of the placeholder. This technique is shown in Example 6-3.
Example 6-3: UCClient_Prog.aspx
<%@ Page Language="vb" %> <html> <head> <script runat="server"> Sub Page_Load( ) PH.Controls.Add(LoadControl("Nav.ascx")) End Sub </script> </head> <body> <table border="1" width="100%" cellpadding="20" cellspacing="0"> <tr> <td align="center" width="150"> <img src="aspnetian.jpg"/> </td> <td align="center"> <h1>User Control Client Page<h1> </td> </tr> <tr> <td width="150"> <asp:placeholder id="PH" runat="server"/> </td> <td> This is where page content might be placed <br/><br/><br/><br/><br/><br/><br/><br/><br/> </td> </tr> </table> </body> </html>
TIP: If you want to work with the control after loading it using LoadControl, you need to cast the control to the correct type using the CType function in Visual Basic .NET or by preceding the control with (typename) in C#. Note that this requires that the user control be defined in a class that inherits from UserControl, so this technique would not work with the user control in Example 6-1.
In the next installment, learn how to build ASP.NET custom server controls
Return to the .NET DevCenter