Working with the Page

Overview

Whatever you do will be insignificant, but it is very important that you do it.

-Mahatma Gandhi

Although formless pages are still accepted and correctly handled, the typical ASP.NET page is a single form page. The HtmlForm class defines the behavior of the server-side form when the runat attribute is set to server. The HtmlForm class acts as a container of server controls and wraps them in an HTML

element when the page is rendered. The obtained HTML form always posts to the same page and for this reason is said to be reentrant. The default method used to submit form data is POST but GET can be used as well.

In most cases, the server form is the outermost tag and is contained directly in

. In general, though, the server tag can be the child of any other server container control such as ,

,

, and any other HTML generic control. (We covered HTML controls and Web controls in Chapter 3.) If any noncontainer controls (for example, a TextBox) are placed outside the form tag, an exception is thrown. Note, though, that no check is made at compile time. The exception is raised by the control itself when the host page asks to render. Noncontainer Web controls, in fact, check whether they are being rendered within the boundaries of a server form and throw an HttpException if they are not. A call to the Page's VerifyRenderingInServerForm method does the job.

In this chapter, we'll examine some aspects of form-based programming in ASP.NET, including how to use multiple forms in the same page. We consider the use of pop-up windows and show how to inject script code in the body of the client page. We'll also consider the styling of the page and how a made-to-measure API, such as the ASP.NET globalization and localization classes, can in some situations help to easily and effectively change the look and feel of the page. When designing a Web site or any application to be consumed over the Web, you can't realistically pretend to keep it error-free! For this reason, debugging, tracing, and effective and user-friendly error handling are key aspects for top-notch ASP.NET developers.

Programming with Forms

One of the most common snags ASP developers face when they first approach the ASP.NET lifestyle is the fact that managed Web applications support the single-form interface (SFI) model.

  Note

If you've never heard anyone use the SFI acronym, there's no reason for you to panic. It's an acronym I've created to mimic other more popular acronyms that, although used in different contexts, describe similar programming models—the single-document interface (SDI) and its opposite, the multiple-document interface (MDI).

In the SFI model, each page always posts to itself and does so through the POST or GET HTTP method. The HTTP method and the target frame of the post can be programmatically adjusted using ad hoc HtmlForm properties—Method and Target. The final destination of the postback can't be set in any way. What in HTML and ASP programming was the Action property of the form is simply not defined on the ASP.NET HtmlForm class. As a result, the SFI model is a built-in feature so much integrated with the ASP.NET platform that you can only take it or code the old ASP way without server forms.

Technically speaking, a kind of multiform interface (MFI) is still possible. Hold on, though. By MFI here, I simply mean the possibility of having multiple forms on the final HTML page. In no way does this mean you can have multiple server-side forms in an ASP.NET page. An ASP.NET page can host exactly one server form and as many HTML forms as needed. If you place two or more server forms in a page, you won't get any error at compile time. When processing the request, the Page-derived object examines markup blocks one at a time. The first block is processed and rendered. After that, the page object sets an internal flag to remember that one form object has been processed for the page. Guess what happens next? When the second block is found, the page verifies that no other similar block has been processed earlier. If the flag is set, an HttpException is thrown. Later in this chapter, we'll discuss an example in which multiple forms—one server and one client—work together.

Multiple forms, though, are sometimes needed—although probably not frequently. You would need them, for example, for functionality hosted by the application pages, such as search or login capabilities.

The HtmlForm Class

The HtmlForm class inherits from HtmlContainerControl and implements the IAttributeAccessor interface. The base class provides HtmlForm with the capability of containing child controls. This capability is shared with other HTML control classes, such as HtmlTable, characterized by child elements and a closing tag. The IAttributeAccessor interface defines two methods—GetAttribute and SetAttribute—used to read attribute values out of the opening tag.

Properties of the HtmlForm Class

The HtmlForm class provides programmatic access to the HTML element on the server through the set of properties shown in Table 4-1.

As you can see, the second and third

tags are not marked runat, meaning that ASP.NET will treat them as plain text to output verbatim. When you click on the submit1 button, the browser navigates to another.aspx—a different ASP.NET page that knows nothing about the main page's controls. You can still retrieve values from the referrer page by using the old ASP programming style.

<% @Page Language="C#" %>

Note also that when you use the ASP.NET form to post back, the content of the other text boxes is cleared. The same thing happens when you submit form data using the third button—the submit button of an HTML form that posts to the same page. Why doesn't ASP.NET work as usual? It's simple: there's no view-state information for the controls in the third form. That's the importance of working the ASP.NET way.

  Important

You can have multiple server-side forms in your ASP.NET page as long as only one at a time is visible. For example, a page with, say, three tags marked to run at the server is allowed, but only one of them can be actually rendered. Given the dynamics of page rendering, an exception is thrown if more than one HtmlForm control attempts to render. By playing with the Visible property of the form controls, you can change the active server form during the page lifetime. This approach mimics that of ASP.NET mobile controls. (See Chapter 11.) This trick doesn't really solve the problem of having multiple active forms, but it can be helpful sometimes.

Passing Variables Across Pages

All in all, the HTML form mechanism is a way to invoke an external page to accomplish some specific tasks. When the form is posted, the browser packs the contents of the form fields in the body of the request. On the server, the target page retrieves those values and processes the request. In other words, you use the HTML element when you need to execute some code on the server passing parameters.

Accomplishing this task with ASP.NET is straightforward if you use postback events and the SFI programming model. In this case, you implicitly assume that all the server-side code resides within the same reentrant page. Let's consider, though, the more general case in which you just want to spawn any external page and make sure it can get some parameters from the caller. Figure 4-3 explains a possible ASP.NET architecture to transfer the control from the caller page to external pages passing some context information. This multiple-form model is implemented within the default SFI model.

Figure 4-3: Passing values between Web Forms pages.

The base page contains multiple logical forms—that is, not real tags but groups of input fields with each one having a submit button. Each group posts back to the same page, but then in the server-side event handler the page transfers the execution to another page—the actual action page. Should the action page access the context of the referrer by using the Session or Application objects?

Referencing Referrer Pages

In this model, the action page is activated through the Server.Transfer method. As will be discussed in Chapter 13, a page spawned through Transfer is processed in the same AppDomain and by the same HttpApplication object that was processing the referrer. The spawned page also inherits the original context of the request—the HttpContext object created upon startup.

In Chapter 13, we will learn that the HttpContext object has a property named Handler that returns the IHttpHandler object that is managing the request. When Transfer executes, the context of the original request is preserved and made available to the newly running page. Within the context, the Handler property in particular is not replaced and continues pointing to the handler that first received the HTTP request from the client. The spawned page can access the page object representing its referrer by using the Handler property as follows:

Page referrerPage = (Page) Context.Handler;

Because Handler returns a valid instance of the page object, the spawned page can access all its properties and methods. It cannot directly access the controls because of the protection level. In fact, if you look back at the source code for compiled pages in Chapter 2, you'll see that all page controls are mapped as protected members on the ASP.Xxx_aspx class. However, you could define ad hoc public properties on the caller page and expose to spawned pages all the data you want. For example, the following code exposes the content of the RegUserName text box through the RegistrationUserName public property:

<% @Page ClassName="MultiFormPage" %>

Notice the ClassName attribute in the @Page directive. It lets you assign a user-defined alias to the page class. In practice, any spawned pages can access the referrer page as an instance of the class MultiFormPage rather than as an instance of the ASP.Xxx_aspx class.

Retrieving Values

As mentioned, the spawned page can access the instance of the parent page using the Handler property. However, this fact alone is not sufficient to enable access to additional properties. The value returned by Handler must be cast to the actual page type—ASP.Xxx_aspx or whatever the ClassName attribute specifies. But how can the page add a reference to the type? That's just what the @Reference directive is all about. Figure 4-4 shows a page with a couple of logical forms—each of which transfers execution to a different page.

Figure 4-4: An ASP.NET page that contains multiple groups of logically related controls acting as independent forms.

When the user clicks the Register button, the page posts back and transfers to register.aspx. This page, in turn, retrieves the handler of the original request and casts it to an instance of the MultiFormPage class. After that, the register.aspx page can use any of the ad hoc properties defined by the caller page to expose its public data.

<% @Page ClassName="MultiFormPage" %>

Register

 

 

 


Login

 

 

 


The structure of register.aspx and login.aspx is nearly identical, although the two pages will do quite different things. Both pages access input data from the referrer using the @Reference directive and the Context.Handler property.

<% @Page Language="C#" %> <% @Reference Page="MultiForms.aspx" %>

User <%= refPage.RegistrationUserName %> has been registered. Password is <%= refPage.RegistrationPassword%>.

The model we discussed here has a couple of drawbacks that should be mentioned. First, it makes use of Server.Transfer, which is a server-to-server mechanism to transfer control. Although highly efficient in terms of performance, it does not notify the browser of the URL change. Second, the Server.Transfer method does not work across domains. In other words, you can't post to a page resident on another server. An alternative is using Response.Redirect, but in this case you must take care yourself of posting any relevant data to the target page. For more information and further references, see the "Resources" section at the end of the chapter.

  Note

From this example, it should be clear that having multiple server forms is not really impossible. The

tag will remain unique, but one could create custom, form-like container controls and use them within the single server form. The new control will have an Action property and manage to handle the submit postback event on any of its buttons. The onclick handler would simply transfer the control to the page specified in Action. To establish uniformity in the way in which spawned pages retrieve input parameters, you could force the host page to implement a particular interface or take greater advantage of hidden fields. We'll return to this in Chapter 18 when discussing custom Web controls.
  Caution

Note that the approach previously described to pass values to other pages requires the callee to know about the type and the programming interface of the caller. When assuming this is not safe, you might want to consider an alternative scheme for passing values. You can copy relevant information to the Context.Items collection and use Server.Transfer to pass the control. Because the context for the request is the same, caller and callee share the same HTTP context. In this way, the callee reads input data from a common cargo collection rather than referencing a particular page class. In terms of programming, using the Context.Items collection is nearly identical to using Request.Form or Request.QueryString.

Pop Up Forms

The HTML 4.0 object model allows you to use the window.showModalDialog method to show a modal window with a Web page inside. In some situations, this is a very viable approach for retrieving information from the user. ASP.NET pages can be used with pop-up windows. If the page has read-only contents, no particular care should be taken—just open the window with some client-side script code and let it go. On the other hand, if the page shown through a modal window is interactive and posts back, some measures should be taken to ensure that all the output goes through the modal dialog until the user closes it.

To show logically modal forms without using client-side modal dialogs, you could resort to the trick we discussed earlier and build multiple forms with 100 percent ASP.NET code. (See Figure 4-2.) Let's drill down a bit into creating modal windows.

Client Modal Dialogs

To perform a modal operation—that is, a blocking operation that prevents interaction with the main page until terminated—a Web page can resort to the following JavaScript code:

 

Pro ASP.NET (Ch04)

The showModalDialog method on the window object opens the specified page in a Windows dialog box with the given title and styles. (For more information about the styling options of the dialog, see the MSDN documentation.) The preceding code pops up a 200-by-300-pixel modal window filled with the HTML content generated by dialog.aspx. So far so good.

Suppose now that the dialog.aspx page contains some links and buttons you can click. Again, think of a modal page you want to pop up for users' registration and logging. The user enters some data and then clicks; the page posts back, but the new output is displayed in a new browser window. Any value you can assign to the form's target value seems to be ineffective. The behavior is by design, but is there a workaround? Yes, for the browsers that support it, you can use a wrapper inline frame. (The tag is part of the HTML 4.0 specification.)

Instead of containing the code to display the window contents, dialog.aspx contains a properly sized tag.

 

At this point, the target attribute set on the multiforms.aspx page is detected and properly handled, as shown in Figure 4-5.

Figure 4-5: Navigable modal dialogs based on ASP.NET pages.

 

Using Embedded Forms

As mentioned earlier, by toggling on and off the visibility of blocks of related controls you can implement sort of pop-up forms as well. In the page, you define a logical form—that is, a block of controls acting as a real form—and hide and show it as needed. Basically, such a logical form would work with the rest of the page in a mutually exclusive way. The following code snippet shows a form with two logical subforms: one with a button and one with the typical login fields. Both are wrapped by a server-side

panel.

 


Login

 

 

 


When the button is clicked, the RunDlg block is hidden and Login is displayed. Note that when the Visible attribute is set to false, no code will be generated for any control.

 

Using this approach, the form expected to work as a modal dialog is the only piece of the page's UI being displayed at a time. Note that for a better implementation of the solution, you can take advantage of user controls to wrap the contents of a server-side

block. We'll cover user controls in Chapter 10.

Working with Script Code

When you have input forms that need to be displayed only in certain situations (for example, when the user is not registered), a commonly used approach entails that you keep hidden the optional form and show the user a link to click. (See Figure 4-6.)

Figure 4-6: The optional dialog shows up only if the user explicitly requests it.

The overall user interface of the page is cleaner, and only information that's strictly needed is displayed. The downside of this approach is that you need an extra roundtrip to display the optional input form. In some cases, typically when an up-level browser is used, resorting to some client-side script code can be as much as twice effective, giving you a clean user interface and avoiding the extra roundtrip.

Supporting Multiple Browsers

Browser information is packed in the HttpBrowserCapabilities object returned by the Request.Browser property. Table 4-3 lists all the available properties.

Table 4-3: Browser Capabilities

Property

Description

ActiveXControls

Gets whether the browser supports ActiveX controls.

AOL

Gets whether the client is an America Online browser.

BackgroundSounds

Gets whether the browser supports background sounds.

Beta

Gets whether the browser is a beta release.

Browser

Gets the user agent string transmitted in the HTTP User-Agent header.

CDF

Gets whether the browser supports Channel Definition Format (CDF) for webcasting.

ClrVersion

Gets the Version object containing version information for the .NET common language runtime installed on the client.

Cookies

Gets whether the browser supports cookies.

Crawler

Gets whether the browser is a Web crawler search engine.

EcmaScriptVersion

Gets the version number of ECMA script supported by the browser.

Frames

Gets whether the browser supports HTML frames.

Item

Indexer property, gets the value of the specified browser capability.

JavaApplets

Gets whether the client browser supports Java applets.

JavaScript

Gets whether the browser supports JavaScript.

MajorVersion

Gets the integer number denoting the major version number of the browser.

MinorVersion

Gets the decimal number denoting the minor version number of the browser.

MSDomVersion

Gets the version of the Microsoft HTML (MSHTML) document object model that the browser supports.

Platform

Gets the name of the platform that the client uses.

Tables

Gets whether the client browser supports HTML tables.

Type

Gets a string made of the name and the major version number of the browser (for example, Internet Explorer 6 or Netscape 7).

VBScript

Gets whether the browser supports VBScript.

Version

Gets a string representing the full (integer and decimal) version number of the browser.

W3CDomVersion

Gets the version of the W3C XML Document Object Model (DOM) supported by the browser.

Win16

Gets whether the client is a Win16-based computer.

Win32

Gets whether the client is a Win32-based computer.

As a margin note, consider that if you're writing a mobile application, you must cast the Request.Browser return value to the MobileCapabilities object and work with that. The mobile capabilities object is supported only by version 1.1 of the .NET Framework. However, a separate download is available to enable mobile controls support for version 1.0 of the .NET Framework.

The Browser property returns the full string contained in the User-Agent header, and using it is probably the most flexible way to make sure the browser has just the characteristics you need to check. However, Type is another useful property for verifying the identity of the client browser. The following code shows how to make sure that the browser is Internet Explorer version 4.0 or newer:

bool upLevelBrowser = false; HttpBrowserCapabilities caps = Request.Browser; if (caps.Browser.ToUpper().IndexOf("IE") > -1) { // This is IE. Is version >3? upLevelBrowser = (caps.MajorVersion >3); }

By putting this code in the Page_Load event, you enable yourself to modify the structure of the page according to the capabilities of the browser.

  Note

The ClrVersion property does not include build information, which means you can't track which service pack is installed on the client, if any. For the local machine, you can get full version information about the installed CLR using the following code:

Version v = Environment.Version; Console.WriteLine("{0},{1},{2},{3}", v.Major, v.Minor, v.Build, v.Revision);

If the Revision property is 0, no service pack is installed. If the revision number is 209, service pack 1 is installed. The revision number for service pack 2 is 288.

Now that we know a way to distinguish between browsers, let's use this technique to make up-level browsers show optional input forms via script. In the context of this example, an up-level browser is Internet Explorer 4.0 or a newer version.

Adding Client Side Scripts

The idea is to obtain the effect shown in Figure 4-6 using client-side DHTML script code on up-level browsers and the classic postback event elsewhere. When the user clicks the hyperlink, what happens depends on the type of the browser. Although clear in the overall design, the solution is trickier to implement than one might think at first. The hidden difficulty revolves around the type of object the user really clicks on. Whatever the browser, the user always clicks a hyperlink with some client script code attached. Because we're talking about ASP.NET pages, any script code is associated with the hyperlink on the server. And this is exactly the issue.

If the browser is up-level, you need to render the clickable hyperlink by using a client-side HTML element—that is, an element without the runat="server" attribute.

Click <a href="javascript:YourFunc()">here</a>

If the browser is down-level, you need to use the ASP.NET LinkButton control as shown in the following code:

Click

How should you design the page layout to meet this requirement? Let's review a few possibilities, starting with ASP-style code blocks:

If not registered, click <a href="javascript:ShowRegisterDlg">here</a>. .


Although effective, the solution shown generates messy and rather unreadable code when used on a large scale. It also requires you to declare the upLevelBrowser variable as global. Finally, ASP-style code-blocks are deprecated, although ASP.NET fully supports them.

A second possibility entails the use of ASP.NET custom controls smart enough to detect the browser and produce appropriate HTML output. The key advantage of this approach is that you use a single tag and bury all necessary logic in the folds of its implementation. However, because we'll not cover custom controls until Chapter 18, we'll go for a third alternative that falls somewhere in the middle of the previous two.

You use a placeholder control to mark the place in which the hyperlink should appear in one form or the other. Next, in the Page_Load event handler, you populate the Controls collection of the PlaceHolder control with either plain text or a dynamically created instance of the LinkButton control. You use plain text—more exactly, a LiteralControl instance—if the client is up-level and client script code can be used. You use the LinkButton otherwise. The following code snippet shows the page layout:

Login


If not registered, click .


In the Page_Load event handler, you first learn about the browser's capabilities and then configure the placeholder to host an HTML hyperlink or a server-side link button.

void Page_Load() { // Check browser capabilities bool upLevelBrowser = false; HttpBrowserCapabilities caps = Request.Browser; if (caps.Browser.ToUpper().IndexOf("IE") > -1) { // This is IE. Is version >3? upLevelBrowser = (caps.MajorVersion >3); } // if downlevel (considering only IE4+ uplevel) if (upLevelBrowser) { AddDhtmlScriptCode(theLink); RegPanel.Visible = true; RegPanel.Style["display"] = "none"; } else AddPostBackCode(theLink); }

If the browser is up-level, the registration panel should be included as HTML but not displayed to the user. For this reason, you must set the Visible property to true—ensuring that the HTML code for the panel will be generated—and, at the same time, you need to hide the controls from view by resorting to CSS-style properties.

If the browser proves to be down-level—whatever this means to your application—you simply create and configure the link button control to receive the user's clicking and post back.

void AddDhtmlScriptCode(PlaceHolder ctl) { // Name of the Javascript function string scriptFuncName = "ShowRegisterDlg"; // Token used to register the Javascript procedure // with the instance of the Page object string scriptName = "__ShowRegisterDlg"; // Create the hyperlink HTML code and add it to the placeholder string html = "<a href="javascript:{0}()">{1}</a>"; html = String.Format(html, scriptFuncName, "here"); LiteralControl lit = new LiteralControl(html); ctl.Controls.Add(lit); // Create the Javascript function (must include

Table 4-1: Form Properties

Property

Description

Attributes

Gets a name/value collection with all the attributes declared on the tag.

ClientID

Gets the value of UniqueID.

Controls

Gets a collection object that represents the child controls of the form.

Disabled

Gets or sets a value indicating whether the form is disabled. Matches the disabled HTML attribute.

EnableViewState

Rarely used at this level, gets or sets a value indicating whether the state of child controls should be persisted.

Enctype

Gets or sets the encoding type. Matches the enctype HTML attribute.

ID

Gets or sets the programmatic identifier of the form.

InnerHtml

Gets or sets the markup content found between the opening and closing tags of the form.

InnerText

Gets or sets the text between the opening and closing tags of the form.

Method

Gets or sets a value that indicates how a browser posts form data to the server. The default value is POST. Can be set to GET if needed.

Name

Gets the value of UniqueID.

NamingContainer

Gets a reference to the form's naming container; typically the host page.

Page

Gets a reference to the host page.

Parent

Gets a reference to the parent object. The parent object is typically, but not necessarily, the page. Note that if the form is contained in a

orelement not marked "runat=server", the parent object is still the page.

Site

Returns null.

Style

Gets a collection of all cascading style sheet (CSS) properties applied to the form.

TagName

Returns "form".

Target

Gets or sets the name of the frame or window to render the HTML generated for the page.

TemplateSource-Directory

Gets the virtual directory of the page that contains the form.

UniqueID

Gets the unique, fully qualified name of the form.

Visible

Gets or sets a value that indicates whether the form is rendered. If false, the form is not rendered to HTML.

The form must have a unique name. If the programmer doesn't assign the name, ASP.NET generates one using a built-in algorithm. The default name follows the pattern _ctlX, in which X is a unique integer—typically the index of the control in the page. The programmer can set the form's identifier by using either the ID or Name property. If both are set, the ID attribute takes precedence. Based on the value of ID, ASP.NET determines the values of UniqueID and ClientID. UniqueID is a fully qualified string based on the naming container of the form. ClientID, on the other hand, is used to identify a control for client-side operations, such as JavaScript functions.

The parent object of the form is the outer container control with the runat attribute. If such a control exists, the page object is set as the parent. Typical containers for the server form are

andif they are marked as server-side objects.

By default, the Method property is set to POST. The value of the property can be modified programmatically. If the form is posted through the GET method, all form data is passed on the URL's query string. However, if you choose the GET method, make sure the limited size of a GET request does not affect the integrity of your application.

Methods of the HtmlForm Class

Table 4-2 lists the methods available on the HtmlForm class. All the methods listed in the table are inherited from the base System.Web.UI.Control class.

Table 4-2: Form Methods

Method

Description

DataBind

Calls the DataBind method on all child controls.

Dispose

Performs the final clean up before the object is freed.

FindControl

Retrieves and returns the control that matches the specified ID.

HasControls

Indicates whether the form contains any child controls.

RenderControl

Outputs the HTML code for the form. If tracing is enabled, caches tracing information to be rendered later, at the end of the page.

ResolveUrl

Ensures that the specified URL is absolute. Reads the base URL from the TemplateSourceDirectory property.

The FindControl method searches only in the form's naming container. Controls belonging to an inner or outer naming container are not found.

Multiple Forms

As mentioned, the SFI model is the default in ASP.NET and plays a key role in the automatic state management mechanism we described in Chapter 2. Some pages, though, would have a more consistent and natural design if they could define multiple logical forms. In this context, a logical form is a logically related group of input controls. For example, think of a login page for restricted areas of a Web site. Registered users just type in their user name and password and connect. Unregistered users need to fill out a form before they're given the parameters to connect. How do you code this? Let's review a few options.

In ASP, you can use two forms, each posting to a different page. The schema is outlined in Figure 4-1.

Figure 4-1: Each form posts to a different page.

This solution is impracticable in ASP.NET unless you resort to a kind of pseudo-trick that basically consists of not using ASP.NET server controls. (More on this in a moment.) More apt to the ASP.NET programming style is the solution depicted in Figure 4-2.

Figure 4-2: One physical tag contains multiple logical forms. All logical forms post to the same page and hide any unneeded user interface element.

A unique page is involved and contains a single tag. Within the opening and closing tags of the form, two or more groups of logically related controls find their place. Each group can post back using either a classic submit button or a hyperlink bound to some script code—the entity that ASP.NET renders as a link button. Although the various groups are not forms in the technical sense of the word, they do behave like forms. All groups post to the same page, but the ASP.NET infrastructure guarantees both state maintenance and the execution of a piece of code specific to the group that caused the post. Each link button is associated with a server-side method that runs when the page posts back as the result of a click. Functionally speaking, the mechanism is in no way different than the ASP implementation. Furthermore, the programming model enjoys a higher abstraction layer and handy features such as view state and server-side control programming.

Toggling the Control's Visibility

How can you update the user interface of the HTML being generated? You should know the answer immediately if you're somewhat familiar with Dynamic HTML (DHTML) and client-side programming. Otherwise, just read on.

ASP.NET server controls can programmatically be hidden from and restored to view. Controls with the Visible property set to false are ignored by the ASP.NET parser, and no HTML code will ever be generated for them. It turns out that this feature is key to dynamically creating the user interface of a page, piece by piece. So with reference to Figure 4-2, you design your login page to contain three blocks of HTML code: the login form, registration form, and restricted area. The first time you show the page—that is, when IsPostBack is set to false—the former two blocks are displayed while the restricted area remains invisible. Next, the user clicks to either log in or register. The page posts back to execute ad hoc code. Once you have registered the new user or checked the credentials of a known user, you simply toggle off the visibility of the forms and toggle on that of the restricted area. Et voil!

  Note

Playing with the visibility of controls is a key technique in Dynamic HTML and is the underpinning to many animation effects you see on Web sites when you navigate with Internet Explorer. Typically, cascading menus and collapsible views are obtained by putting a hidden

tag below the clickable text. When you click on the text, some script code simply toggles the display attribute of thetag and the browser does the rest, refreshing the page.

Multiple Tags on a Page

For completeness, let's see how to create an ASP.NET page that really contains multiple tags. Of course, only one of these tags can have the runat attribute set to server, and only for the controls contained therein can ASP.NET provide view state management and postback events. Let's consider the following ASP.NET page:

 

Категории