The following sections describe the elements used in HelloSimple.aspx, as well as other elements that you can use in a Web Form page.

Understanding Page Elements

HelloSimple.aspx (the preceding listing) shows examples of many of the elements that you can use in an ASP.NET Web Form, including server-side comments, the @ Page directive, static HTML, a server-side <script> code-declaration block containing both event handlers and methods, and several ASP.NET server controls. Table 7-1 describes these elements.

Table 7-1: ASP.NET Page Elements

Element

Description

Static HTML tags

These standard HTML elements are treated by ASP.NET as literal controls, and are rendered to the client browser as represented in the source file.

HTML comments

Syntax: <!-- -->. HTML comments allow descriptive text to be added to a page. This text is sent to the client but is not rendered by the browser.

Directives

Directives, such as the @ Page directive, provide the ASP.NET runtime with information about how to process the page. Using directives, you can control such ASP.NET features as session state, wiring up of events, and output caching, as well as importing namespaces and registering custom controls for use within a page.

Server-side code

Code can be contained in either server-side <script> code declaration blocks or <% %> render blocks. See “Writing Code in Web Forms” on page 211 for information on using code declaration and render blocks. ASP.NET supports server-side code in any language that targets the runtime.

Event handlers

Event handlers are procedures in <script> code declaration blocks that handle page or server control events, such as Page_Load or control Click events. Most ASP.NET code should be written in or called from event handlers, rather than being written in render blocks.

<script> code declaration blocks

These blocks are used to contain page-level procedures and to declare variables that are global to the page. Executable code, other than global variable declarations in code declaration blocks, must be contained within a procedure declaration. Server-side code declaration blocks must have the runat=“server” attribute, as shown in HelloSimple.aspx.

<% %> render blocks

These blocks are used to contain executable code not contained within procedures. Overuse of render blocks can result in code that is difficult to read and maintain.

Client-side <script> blocks

These blocks are used to contain script code to be executed on the client, usually in response to a client-side event. Choice of language (set by the language attribute) is dictated by the languages supported by the target browser. JavaScript is the most common choice for cross- browser compatibility in client scripts.

Server-side comments

Syntax: <%-- --%>. Server-side comments allow descriptive text to be added to a page. Unlike HTML comments, this text is not sent to the client.

User controls

These are custom controls that are defined declaratively in files with the .ascx extension. They provide a simple and straightforward mechanism for reuse of UI and UI-related code, and can contain most of the same elements as Web Forms pages.

ASP.NET server controls

This set of built-in controls provides ASP.NET developers with a programming model that mimics that of Microsoft Visual Basic. Controls are added to a page, and programmers write code to handle events raised by users’ interaction with the controls at runtime. ASP.NET provides two sets of built-in controls: the HTML controls, which provide a 1-to-1 mapping of server-side controls for most HTML elements; and the Web controls, which provide a set of controls that are very similar to the Visual Basic UI controls.

Note that some server controls, such as the TextBox and Button controls, must be placed within a server-side <form>, or an exception will be raised.

Custom server controls

Custom server controls are another mechanism for reuse in ASP.NET. They’re defined in class files (.cs or .vb files) and are precompiled into managed assemblies before use.

Understanding Page Lifetime

The goal of the ASP.NET Web Forms programming model is to provide an experience similar to that of a Visual Basic rich-client event-driven application, in which user actions, such as selecting an item from a list or clicking a button, cause server-side code to be executed. This is accomplished through postbacks.

The first time that an ASP.NET Web Forms page is executed, the code contained within the page (and any code-behind class module associated with the page) is compiled into a class that inherits from the Page base class (actually, the code behind class inherits from the Page class, and then the .aspx file inherits from the code-behind class via the Inherits attribute of the @ Page directive). The following illustration shows the relationship between the page, its code- behind class (if any), and the compiled assembly. Once compiled, the class is executed, the resulting HTML is rendered to the browser, and the class is removed from memory.

Each ASP.NET Web Forms page contains a server-side <form> tag that directs the page to post back to itself when the form is submitted by the user. Many ASP.NET server controls also render JavaScript to the client, allowing actions such as selecting an item in a drop-down list to cause a postback. The ASP.NET runtime also renders a hidden form field to the page that allows the page to maintain its state between requests.

The postback and the hidden field are key, because when the client is interacting with the page, no code is running on the server at all. The postback and the hidden field allow the page to be reconstituted on the server. Also, they allow code to be executed in response to the event raised by the user action, and based on any changes to the form fields. Once the page has been processed and the output rendered to the browser, the page and its controls are again discarded. The steps in this process are as follows:

The user requests the page from the browser.

The page and controls are loaded and initialized at the server.

If the page request is the result of a postback, the control state is loaded from the viewstate (hidden form field), and any changes submitted by the user are applied. (Note that both the original values in the viewstate and the updated values are available to server-side code.)

It’s important to note that while most server controls save their state to viewstate automatically, the same is not true for properties that you define in your pages, or in user controls or custom server controls. You’ll learn how to use viewstate for storing custom control state in Chapter 10.

Using Directives

If you’ve developed a classic ASP page, you’ve worked with directives. There were few directives in classic ASP, but they were important. Most prominent were the @ Language directive and the #Include directive. The @ Language directive, which appeared at the top of every classic ASP page, told the ASP runtime which language engine to use in interpreting script found in <% %> render blocks in the page. The #Include directive told the ASP interpreter to include a particular file inline with the current ASP page.

Directives are simply ways for developers to declaratively determine how certain aspects of a program will operate. In classic ASP, this was somewhat limited. In fact, classic ASP had only four @ directives, in addition to the @ Language directive:

@ Codepage Used in globalization to set the code page for an ASP page

@ EnableSessionState Used to disable session state for a page

@ LCID Used to set the locale identifier for a page

@ Transaction Used to specify whether and how the page participates in COM+ transactions

ASP.NET greatly expands the use of directives, adding a number of useful directives for everything from controlling page behavior and configuration to caching page output. In addition, @ directives in ASP.NET have attributes, thereby increasing their power and flexibility. The four classic ASP directives listed previously are represented in ASP.NET as attributes of the @ Page directive, which is described in the next section.

@ Page

The @ Page directive, which is allowed in .aspx files only, defines page-specific attributes used by ASP.NET language compilers and the runtime to determine how the page will behave. The default values for a few of these attributes are set in the Pages configuration section in machine.config. Some of the attributes, including AutoEventWireup, are set or overridden in pages created by Microsoft Visual Studio .NET. The attributes available for the @ Page directive are listed in Table 7-2.

Table 7-2: @ Page Attributes

Attribute

Value

Purpose

AspCompat

true/false

Provides compatibility with COM components created with Visual Basic 6.0 (or earlier) by forcing the page to be run in an STA (Single Threaded Apartment). Also provides the component with access to unmanaged instances of the ASP intrinsics (Session, Application, Request, and so on). This setting will likely degrade performance, so use it only when necessary. Default is false.

AutoEventWireup

true/false Default is set in the <pages> section of machine.config or Web.config.

Determines whether handlers for events such as Page_Load are set up automatically. See “Event Handling” on page 220 for more information. Default is true.

Buffer

true/false Default is set in the <pages> section of machine.config or Web.config.

Determines whether rendered output is buffered before being sent to the client or is sent as it is rendered. Default is true.

ClassName

Any valid class name.

Determines the name of the page generated by dynamically compiling the page. This attribute works with or without Codebehind, and with either the Src or Codebehind attributes. The default behavior if this attribute is omitted is for the page name to be in the form filename_aspx.

ClientTarget

Any valid UserAgent string. Available values are set in the <clientTarget> section of machine.config or Web.config.

Determines the target browser for which server controls should render output. The UserAgent string should be one recognized by the server controls being used.

Codebehind

Filename of code behind class.

This attribute is used in Visual Studio .NET to locate code-behind classes to be compiled during a build operation. It is not used by the ASP.NET runtime.

CodePage

Any valid code page value.

Used in globalization to set the code page for a Web Forms page.

CompilerOptions

String containing valid compiler options.

Allows developers to pass compiler options for the page to the compiler. For Visual Basic .NET and C#, this can be any valid sequence of command-line switches for the compiler.

ContentType

Any valid MIME type (such as “text/html” or “application/ vnd.ms-excel”).

Sets the MIME type for the page output. This attribute is useful when returning binary content (such as images) to the client.

Culture

Any valid culture string (such as en-US for US English).

Determines the culture setting for the page.

Debug

true/false Default is set by the debug attribute of the <compilation> section of machine.config or Web.config.

Determines whether pages are compiled with debug symbols or without. This setting affects performance, so production applications should have this set to false. The default is false.

Description

Any string.

Provides a text description of the page. This attribute is ignored by the ASP.NET runtime.

EnableSessionState

true/false/readonly Default is set in the <pages> section of machine.config or Web.config.

Determines whether a request to the page will initiate a new session, and whether the page can access or modify data stored in an existing session. Default is true.

EnableViewState

true/false Default is set in the <pages> section of machine.config or Web.config.

Determines whether viewstate is enabled for the page. ViewState allows server controls to save their current state from request to request. Default is true.

EnableViewStateMac

true/false Default is set in the <pages> section of machine.config or Web.config.

Determines whether ASP.NET runs a Machine Authentication Check (MAC) on the content of the hidden form field that is used to store viewstate, to ensure that it has not been altered on the client. Default is false.

ErrorPage

Any valid URL.

Specifies a page to which the client is redirected if there is an unhandled exception in the page.

Explicit

true/false Default is set in the <compilation> section of machine.config or Web.config.

Determines whether code written in Visual Basic is subject to the Option Explicit rule when compiled. Default is true.

Inherits

Any class derived from the Page class. Format is “namespacename. classname” or “classname”.

Specifies a code-behind class for the page. Any code contained in the page is combined with the code in the code- behind class into a single class.

Language

Any valid string for an installed .NET language (such as “vb”/ “visualbasic”, “c#”/ “cs”/ “csharp”, and so on)

Default is set in the <compilation> section of machine.config or Web.config.

Specifies the language compiler to be used to compile the page. Default is c#.

LCID

Any valid locale identifier.

Used to set the locale identifier for a page.

ResponseEncoding

Any valid encoding string. Default is set in the <globalization> section of machine.config or Web.config.

Used in globalization to set the character encoding for the HTTP response. Default is utf-8.

SmartNavigation

true/false

Determines whether SmartNavigation, which uses IFrame elements for clients running Microsoft Internet Explorer 5.0 or above to reduce the flash of navigation when repeatedly posting back to the same page, among other enhancements, is enabled or disabled. Default is false

Src

Filename of code- behind class

Specifies the name of a code-behind class file to be compiled dynamically at runtime.

Strict

true/false

Determines whether code written in Visual Basic is subject to the Option Strict rule when compiled. Default is false.

Trace

true/false Default is set in the <trace> section of machine.config or Web.config.

Determines whether the page includes trace output. Default is false.

Note: Leaving tracing enabled on production systems can cause both performance and security issues. Ensure that tracing is disabled before deploying an application.

TraceMode

SortByTime/ SortByCategory Default is set in the <trace> section of machine.config or Web.config.

Determines how the trace output is sorted when tracing is enabled. Default is SortByTime.

Transaction

One of the following: Disabled NotSupportedSupported RequiredRequiresNew

Determines whether and how the page will participate in COM+ transactions. Default is Disabled.

UICulture

Any valid UI culture value.

Specifies the UI culture setting for a Web Forms page.

ValidateRequest

true/false

Specifies whether request validation will be performed on input data such as querystring values, form fields, and so on. If true, and if any potentially dangerous content such as HTML tags or script is found, an exception of type HttpRequestValidationException will be thrown. The default is true.

Important: You should not disable this feature unless you are certain that you have adequately provided for filtering or validating that any input that the page accepts is safe for processing or display.

WarningLevel

0–4

Specifies the warning level at which the compiler should abort page compilation. Lower numbers allow compilation to continue through warnings of greater severity. Levels 2–4 apply to C# only.

@ Page Examples

In this section, we’ll take a look at a couple of examples of using the @ Page directive. The first example will show how to enable debugging of ASP.NET pages, and the second will show how to enable page-level tracing.

Enabling Debug Information

By default, ASP.NET pages created as a part of a Visual Studio .NET Web application project are compiled with debug symbols. This is good for development, since it offers much richer error information than was available in classic ASP, as shown in the following illustration.

This functionality, however, exacts a performance penalty, so you should usually turn this option off when you deploy your application. Debug symbols are enabled in two specific places in Visual Studio .NET: in Web.config (which enables debug symbols for .aspx pages) and in the Configuration Manager properties for the project/solution (which enables debug symbols for code- behind classes and other compiled classes in the project). The following procedures describe how to turn off debug symbols in both places.

Disable debug symbols in Web.config

Open the Web.config for the desired project in Visual Studio .NET.

Change the debug attribute of the <compilation> element to false:

<compilation defaultLanguage="c#" debug="false" />

Save and close the file.

Now if you encounter an unhandled error in code contained within the .aspx page, ASP.NET will provide you with an error page without the specific error information made possible by the use of debug symbols, as shown in the following illustration.

Important

Debugging carries some performance penalty, but, more important, if you do not configure the <customErrors> configuration element properly, enabling debugging can result in information being displayed to the user that could present a security risk. You should always disable debugging in production applications to minimize the amount of exploitable information your application provides when an exception occurs, as well as to improve performance.

Disable debug symbols using the Configuration Manager

Right-click the solution containing the desired project in Solution Explorer, and select Configuration Manager. The dialog box shown in the following illustration is displayed.

To disable debug symbols for a single project, select that project in the dialog box and change the value in the Configuration drop-down list from Debug to Release, as shown in the following illustration.

To disable debug symbols for all projects, change the Active Solution Configuration from Debug to Release, as shown in the illustration on the next page.

Click Close.

Enabling Tracing

By default, the trace functionality is not enabled for ASP.NET pages. As with the debug attribute, disabling tracing in production applications is good for performance, since there is overhead associated with tracing. Disabling tracing in production applications is also important for security because tracing provides a large amount of information about each request, information that could potentially assist a malicious user in attacking your site, if tracing isn’t configured properly. Tracing enables you to view information about the current request, including the collections (cookies, forms, headers, querystrings, and server variables) associated with the request.

Enable tracing

Open the desired page in Visual Studio .NET and switch to HTML view.

Add the trace attribute to the @ Page directive, with a value of true.

<%@ Page trace="true" %>

Save the file. When you request the file from a browser, you’ll be able to see the trace information appended to the page output, as shown in the illustration on the following page.

For more information on tracing and its uses in debugging ASP.NET applications, refer to Chapter 14.

Tip

In addition to manually adding (or modifying) the attributes of the @ Page directive, you can enable or disable these features on a per-page basis by using the Properties window in Visual Studio .NET to set the Debug or Trace properties of the DOCUMENT object.

@ Control

The @ Control directive, which is allowed in .ascx files only, performs the same function as the @ Page directive. However, instead of setting attributes for pages, it sets the attributes for user controls, which are reusable snippets of code named with the .ascx file extension. The attributes exposed by the @ Control directive are a subset of the attributes exposed by the @ Page directive, and their purpose and values are the same as in the table of @ Page attributes table beginning on page 195. The following attributes are available for the @ Control directive:

AutoEventWireup

ClassName

Codebehind

CompilerOptions

Debug

Description

EnableViewState

Explicit

Inherits

Language

Strict

Src

WarningLevel

@ Import

The @ Import directive is used to import either a Microsoft .NET Framework namespace or a custom namespace into a page. Importing a namespace allows you to write code against the members of that namespace without explicitly specifying the namespace each time. The @ Import directive has only one attribute, Namespace, which specifies the namespace to import. Each @ Import directive can have only one Namespace attribute, so you must use a separate @ Import directive for each namespace you want to import. For example, to use the .NET Framework SmtpMail and MailMessage classes to send e-mail from an ASP.NET page, you need to add the System.Web.Mail namespace to your page, as follows:

<%@ Import namespace="System.Web.Mail" %>

Then, to create an instance of the MailMessage class, you use the following:

MailMessage myMail = new MailMessage();

Without the @ Import directive, you need to use the following:

MailMessage myMail = new System.Web.Mail.MailMessage();

@ Implements

The @ Implements directive is used to implement a defined interface from within an ASP.NET page. An interface provides an abstract definition of a set of methods and properties. When you implement an interface, you commit to supporting the methods and properties defined by the interface, and you must create matching method and property definitions in your .aspx file, using <script> blocks. The @ Implements directive can’t be used to implement interfaces in a code-behind file. It has one attribute, interface, which specifies the interface being implemented.

@ Register

The @ Register directive is used with both user controls and custom server controls to register them for use within a page. The @ Register directive’s attributes are listed in Table 7-3.

Table 7-3: @ Register Attributes

Attribute

Value

Purpose

Assembly

Any valid assembly name. The assembly name should not contain a file extension.

Specifies the precompiled assembly for a custom server control. Used with the Namespace and TagPrefix attributes. The assembly named needs to be available to the application, either by being placed in the bin subdirectory of the application or by being installed into the global assembly cache.

Namespace

Any valid namespace name.

Specifies the namespace to be associated with the tag prefix specified by the TagPrefix attribute.

Src

Any valid path to a user control (.ascx) file Accepts either relative or absolute URLs.

Specifies the location of a user control associated with a TagName/TagPrefix pair.

TagName

Any string (must be valid for XML).

Specifies an alias for a user control to be used in implementing the user control within the page. Must be used with the TagPrefix attribute.

TagPrefix

Any string (must be valid for XML).

Specifies an alias for a tag prefix for a user control or custom server control to be used in implementing the control within the page. If used without the TagName attribute, as with a custom server control, the tag name is the same as the class name defined in the server control assembly specified by the Assembly and Namespace attributes.

@ Assembly

The @ Assembly directive is used to link an assembly into a page at compilation time. This allows developers to use all of the classes, methods, and so forth exposed by the assembly as if they were part of the page. The @ Assembly directive’s attributes are listed in Table 7-4. Only one of the Name and Src attributes of the @ Assembly directive can be used at a time.

Table 7-4: @ Assembly Attributes

Attribute

Value

Purpose

Name

Any valid assembly name.

Specifies the name of a compiled assembly to be linked to when the page is compiled.

Src

Any valid path to a class source file (.vb, .cs, and so on).

Specifies the path to a source file to be dynamically compiled and linked to the current page.

Note that it is not necessary to use the @ Assembly directive to link in assemblies residing in the bin subdirectory of your application. These assemblies are automatically linked in by default, based on the <assemblies> subsection of the <compilation> section of the machine.config configuration file, which contains the following tag:

<add assembly="*"/>

This specifies that ASP.NET should link in any assemblies in the bin subdirectory. Note also that any other assemblies specified by an <add> tag in the <assemblies> subsection do not require linking with the @ Assembly directive.

@ OutputCache

The @ OutputCache directive is used to specify that the rendered output of the page or user control in which it appears should be cached. This directive also specifies the attributes that determine the duration of caching, the location of the cached output, and the attributes that determine when a client will receive freshly rendered content rather than the cached content. Output caching in user controls can be especially useful when some of the content in a page is relatively static, but other content is frequently updated, making it a poor candidate for caching. In a case like this, you could move the static content into a user control and use the @ OutputCache directive to cache its content, while leaving the rest of the page to be dynamically generated with each request.

Specifies the location where cached output should be stored. (This attribute is not supported for user controls.)

Shared

true/false

New in ASP.NET 1.1, this attribute specifies whether a cached user control can be shared across multiple pages. If set to true, a single cached copy of the user control is shared among multiple pages within the application. If set to false, a separate copy of the user control will be cached for each page that uses the control. The default is false.

This attribute is valid for user controls only.

VaryByCustom

Any valid text string.

Specifies a custom string by which to vary the output cache. If browser is used, output caching will vary based on the browser name and major version. If you want to vary by HTTP request data other than the requesting browser, you will need to override the GetVaryByCustomString method of the HttpApplication class in Global.asax in order to implement your custom string.

VaryByHeader

List of valid HTTP headers, separated by semicolons.

Specifies one or more HTTP headers to be used to vary the output cache. When a request is received with a value for the specified HTTP header(s) that does not match that of any of the cached pages, a new version of the page will be rendered and cached. This attribute cannot be used with user controls.

VaryByParam

List of querystring keys or form field names, separated by semicolons, or one of the following:

None

*

(Required in ASP.NET pages and in user controls that don’t have a VaryByControl attribute specified.)

Specifies one or more names of either querystring keys passed with a GET request, or form field names passed with a POST request to be used to vary the output cache. When a request is received with a value for one of the specified parameters that does not match that of any of the cached pages, a new version of the page will be rendered and cached. If the value of this attribute is set to none, the output cache will not vary based on GET and POST parameters. If the value is set to *, the output cache will vary by all GET and POST parameters. (Required in a user control if you don’t specify a VaryByParam attribute.)

VaryByControl

List of properties exposed by a user control, separated by semicolons.

Specifies one or more properties exposed by a user control to be used to vary the output cache. When a request is received with a value that does not match the specified property of any of the cached pages, a new version of the page will be rendered and cached. This attribute can be used only for output caching with user controls, not with ASP.NET pages.

Enabling Output Caching

For pages or user controls whose content does not change frequently, output caching can provide a simple and powerful way to improve the performance of your application. With output caching enabled, requests that match the parameters of the cached pages will be served from the cache, which is much faster than rendering the page again.

Enable output caching on a Web Forms page

Add the @ OutputCache directive with, minimally, the required Duration and VaryByParam attributes. This directive should go at the top of the .aspx page, just below the @ Page directive.

<%@ OutputCache duration="60" location="Any" VaryByParam="*" %>

Save the file. With these values for the attributes, the output of the page will be cached for 60 seconds. Any GET/POST requests with parameters that do not match an existing cached version of the page will be served a freshly rendered version of the page, which will then be cached.

For more information on output caching and using the ASP.NET cache engine to store arbitrary data, see Chapter 12.

@ Reference

The @ Reference directive allows you to dynamically load user controls by referencing the filename of the desired control and then using the Page.LoadControl method to load the control at runtime. The @ Reference directive directs the ASP.NET runtime to compile and link the specified control to the page in which it is declared. You’ll see an example of using the @ Reference directive in the section on user controls later in this chapter.

The Page Class

The Page class provides much of the functionality of an ASP.NET page. Pages can take advantage of this functionality because every page derives from the Page class and inherits all of the methods and properties that it exposes. Some of the more notable members of the Page class, which resides in the System.Web.UI namespace, are included in the following list.

ASP Intrinsic objects These objects (Application, Session, Request, Response, Server, and Context) are implemented in ASP.NET as class instances, which are exposed as properties of the page object. For example, the Server functionality is provided by a class called HttpServerUtility. Because the instance of HttpServerUtility is exposed as the Server property of the Page class, you can call its methods (Server.Transfer, for example) just as you could in classic ASP.

Controls collection Provides access to the collection of controls defined for the page. As you’ll see later in this chapter, you can use this collection to add or modify controls on a page.

IsPostBack property Allows you to determine whether the current request is a GET request or a POST request resulting from the current page being posted back to itself. This property is very useful in deciding what to do when loading a Web Forms page, as you’ll see later in this chapter.

User property Provides access to information about the currently logged-in user.

Cache property Provides access to the ASP.NET cache engine, allowing data to be cached for later retrieval.

FindControl method Allows you to locate a control contained in the page’s Controls collection by specifying its ID property.

ViewState property Provides access to a state dictionary (based on the StateBag class) that allows you to store information in Key/Value pairs. This information is passed with each request as a hidden HTML form field.

ClearChildViewState method Allows you to delete all viewstate information for any child controls on the page. This is useful if you want these controls to maintain their state most of the time, but you want to clear the state programmatically under specific circumstances.

Many other properties and methods are exposed by the Page class. A substantial number of these are inherited from the Control class, from which the Page class is derived, or the Object class, from which the Control class (and ultimately, every other class) is derived. This is an example of how inheritance allows you to build a very rich object model.

There are two ways to indicate that an ASP.NET page is inherited from the Page class. The first is adding the @ Page directive to an .aspx file, which automatically makes all of the properties and methods of the Page class available to any code written in the page. The second, which is discussed in more detail later in this chapter, is inheriting from the Page class in a code-behind class that is associated with the page by either the Src or Inherits attribute. This not only makes all of the members of the Page class available to the code-behind class, but also allows ASP.NET to combine the code in the Web Form’s .aspx file with the code in the code-behind class file into a single compiled class at compile time. This single compiled class contains all of the methods and properties exposed by the Page class, as well as any methods and properties implemented by your code.

Any of the members of the Page class can be called within code in a page without explicitly using the page name. For example, to write text to the browser using the Write method of the Response object, you would use the following code:

< Response.Write("Hello, World!") %>

You could also use

< Page.Response.Write("Hello, World!") %>

However, it is not necessary to add the Page property because the Response property and the other Page members are exposed directly when inheriting from the Page class.

Writing Code in Web Forms

One of the strengths of classic ASP was that it gave you a lot of flexibility in writing your code. You could write your code inline with the HTML markup, as a part of <% %> render blocks. You could write your code in subroutines in render blocks. And you could write your code in <script> declaration blocks, either as arbitrary code or as subroutines.

While this flexibility made it very easy to create ASP pages, it was also a major weakness of classic ASP because it allowed for some sloppy coding. It was far too easy to write unmanageable spaghetti code, with some code inline, some code in subroutines, and so on. In addition, although code in render blocks in classic ASP was executed in a linear fashion, arbitrary code (code not contained in a subroutine) in a <script> block was not. This often resulted in confusion, because sometimes this code was executed out of sequence with what developers expected.

ASP.NET solves these problems by imposing limitations on what types of code you can write and where you can write it. While many of the examples in this book use the default technique of writing UI-specific code in a code-behind module (rather than in the Web Forms page itself), it’s worth looking at these limitations for those times when you want to work without code-behind. As with classic ASP, there are still two ways to write code within a Web Forms page: <script> blocks and <% %> render blocks.

Using <script> Code Declaration Blocks

As I mentioned, in classic ASP you could write pretty much any code you wanted (property and method definitions, arbitrary code, and so on) in a <script> block using the runat=“server” attribute. The problem was that you couldn’t be sure when any arbitrary code in the <script> block would execute, relative to the code in the rest of the page. For this reason, ASP.NET does not allow you to use arbitrary code within <script> blocks—now referred to as code declaration blocks— to emphasize their purpose of declaring properties and methods.

In ASP.NET, if you want to call Response.Write within a code declaration block, you need to wrap that call within a method declaration. The following code will work:

The language attribute of the <script> block is optional. If it is omitted, the value will default to the language specified by the @ Page directive’s language attribute. If no language attribute has been specified for the @ Page directive, the value will default to C# (this value is set in the <compiler> element of the web.config configuration file for C# projects created with Visual Studio .NET).

Using ASP <% %> Code Render Blocks

Like <script> blocks, <% %> render blocks in classic ASP were pretty much “anything goes.” In many cases, this led to sloppy programming habits and hard-to-maintain code. ASP.NET solves these problems by not allowing anything other than inline code and expressions in render blocks. This means that you can no longer place method definitions in a render block. So the following code will cause a compiler error:

< public void SayHello( Response.Write("Hello, World!") %>

whereas the following code will work fine:

< Response.Write("Hello, World!") %>

Compatibility with Classic ASP

Clearly, one of the issues that arises from these changes is that a good deal of code written in classic ASP will not be compatible with the restrictions on the use of code declaration and render blocks. For this reason, it is a good idea to examine any classic ASP code that you are considering migrating to ASP.NET for these incompatibilities, so that you can address them before making the migration (and discovering too late that the code won’t work!).

Creating and Using User Controls

User controls are a new feature of ASP.NET that provides a simple and fast method for getting reuse out of your presentation code. User controls can also dramatically increase performance when used in conjunction with the OutputCache directive described earlier. User controls are sometimes referred to as declarative controls (as opposed to server controls, which are compiled controls).

At their simplest, user controls consist of HTML markup and/or ASP.NET code persisted in a file with the .ascx file extension. As you saw earlier in this chapter, developers of user controls can also add the @ Control directive to specify aspects of how the user control will behave. Unlike Web Forms pages, user controls cannot be called directly. Instead, they must be used in an existing page.

User controls can contain most, but not all, of the same elements that you can use in Web Forms pages. For example, user controls should not contain <html>, <body>, or <form> elements because presumably these elements already exist in the page in which the user control is used. It’s also a good idea to at least add the @ Control directive, along with its ClassName attribute, as follows:

<%@ Control ClassName="MyClass" %>

Adding the ClassName attribute allows the user control to be strongly typed when added to a page programmatically (as opposed to declaratively). Creating a user control is fairly straightforward. In the following example, you’ll create a user control that calculates compounding interest. (Don’t worry about the math—it’s provided as part of the example.)

Create a user control

Create a new Web Application project in Visual Studio .NET. Type the name the the project as Chapter_07. (If you’ve installed the practice files, make sure to create the project in a location other than http://localhost/aspnetsbs/Chapter_07to avoid conflicts.)

Add a User Control to the project by right-clicking the project in Solution Explorer, selecting Add, and then selecting Add Web User Control. Type the name of the user control as Compound.ascx.

Add six Label controls, three TextBox controls, a DropDownList control, and a Button control to the user control design surface. When you’re finished, the control should look like the following illustration.

Since user controls start out in FlowLayout mode by default, you’ll need to use the Enter key to move a control to a new line. It’s important to name or number the controls in the order in which they appear on the page. (For example, the first label is Label1, the second, Label2, and so on.) This guarantees that the code in later steps will work.

Set the properties of the controls as follows:

Control

Property

Value

Label1

Text

Compound Interest Calculator

Label1

Font-Bold

True

Label1

Font-Size

Large

Label2

Text

Principal ($):

Label3

Text

Rate (%):

Label4

Text

Years:

Label5

Text

Compounding Frequency:

Button1

Text

Calculate

Label6

Text

(blank)

Click the DropDownList1 control to select it, and then select the Items property and click the ellipsis (…) button to open the Collection Editor. Add four items, set the following text and values, and then click OK:

Item

Text

Value

0

Annually

1

1

Quarterly

4

2

Monthly

12

3

Daily

365

When you have finished modifying the properties, the design view of Compound.ascx should look like the illustration on the next page.

Double-click the Calculate button. This will switch to the code- behind for the user control and add the Button1_Click event handler. Add the following code, which calls the function to calculate the compounded interest, to the event handler.

Save the control and code-behind module, and then build the Chapter_07 project.

This control takes a dollar amount representing the principal to be invested (or borrowed), an interest rate, a period in years, and a compounding frequency, and uses these values to calculate the ending balance. This control encapsulates all of the controls and methods necessary for doing the calculation, so the only way to access them is through the user interface defined by the control. It is also possible, if desired, to expose the controls and/or their values publicly, so that they can be manipulated at runtime from the containing page.

To use this control, you need to add it to a Web Forms page. You can do this two ways: declaratively and programmatically. The declarative technique is simpler, but the programmatic technique gives you better runtime control.

Adding a User Control to a Page Declaratively

To use a user control in a Web Forms page, you need to make the control available to the page, and then implement the control on the page. As described in “Using Directives” on page 194, user controls can be made available to a page using either the @ Register directive or the @ Reference directive, depending on whether you are adding the control declaratively or programmatically. If you want to add the control declaratively, you need to use the @ Register directive to set up the tag syntax for the control. Fortunately, the Visual Studio .NET environment makes this a simple matter of drag and drop.

Add a control to a Web Form declaratively

Open the Chapter_07 project created in the previous example if it is not already open.

Add a new Web Forms page to the project and type its name as CompoundContainer.aspx.

Drag the Compound.ascx user control from the Solution Explorer onto the design surface of CompoundContainer.aspx. The result should look like the illustration on the next page.

Save CompoundContainer.aspx, and then browse it using Internet Explorer by right-clicking the file in Solution Explorer and selecting Browse With. Enter amounts for the principal, rate, and years, then choose a compounding frequency, and then click Calculate. The result should look similar to the following illustration. Note that if you enter any non-integer value in the textboxes, an exception will occur when you click Calculate. You could prevent this by adding validator controls to ensure that only integer values are entered.

If you take a look at the HTML view for CompoundContainer.aspx, you’ll see that Visual Studio .NET takes care of adding both the @ Register directive that makes the user control available to the page, and the tag that represents the user control. This greatly simplifies the process of adding a user control to the page, and should probably be your preferred method for adding user controls in Visual Studio .NET. You can, however, also add a user control to the page programmatically, as you’ll see in the next section.

Adding a User Control to a Page Programmatically

The steps for adding a control to a page programmatically are somewhat similar to—but a bit more involved than—adding a control declaratively. You still need to make the control available to the page. For controls that are to be added programmatically, you can either use the @ Register directive, as in the previous example, or the @ Reference directive. Since the @ Register directive is more commonly used for declarative controls, use the @ Reference directive.

Add an @ Reference directive

Add a new Web Form to the Chapter_07 project. Type the name of the file as CompoundProgrammatic.aspx.

Switch to HTML view, and add the following code to the page, just below the @ Page directive:

<%@ Reference Control="Compound.ascx" %>

Save the file, but don’t close it.

The @ Reference directive tells ASP.NET to compile and link the user control Compound.ascx with the page when it is compiled. This makes the control available for you to add to the page. Since you’re not going to be adding a tag within the HTML markup for the control’s output, take advantage of a special server control called the Placeholder control. As the name suggests, this allows you to put a placeholder in the HTML markup to which you can add controls later. In this way, you can decide precisely where you want the output from the control to appear.

Add the Placeholder control

Add a PlaceHolder control to CompoundProgrammatic.aspx by editing the <form> element to look like the following code:

Finally, we need to add the control to the page, and to the Controls collection of the Placeholder control, which will place the output of the control where we want it. Then the control will take care of the rest, just as in the previous example.

Add the user control to the page

Switch to Design view and double-click an empty area of the page. This will open the code-behind module for the page and place the cursor in the Page_Load event handler. Add the following code to the event handler:

Save the page and code-behind module, and then build the Chapter_07 project.

Browse CompoundProgrammatic.aspx, using the Browse With dialog box. The output of the page should be the same as in the previous illustration.

Comparing User Controls to Include Files

User controls perform a similar function to server-side includes in classic ASP, but they’re considerably more powerful because of the level of integration with the page model. A user control can have its own controls, and it can save the viewstate of its controls or its own viewstate. This allows the user control to maintain its state across multiple calls without any effort on the part of the page containing the user control. User controls can expose both properties and methods, making them easy to understand for developers who are used to components.

Include files are still available in ASP.NET, mainly for backward compatibility with classic ASP. Given the advantages of user controls, it makes sense to use them for new applications.