Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp And The .NET 2.0 Platform (2005) [eng]

.pdf
Скачиваний:
111
Добавлен:
16.08.2013
Размер:
10.35 Mб
Скачать

864 CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

// Rig up the Error event.

this.Error += new EventHandler(_Default_Error);

}

void _Default_Error(object sender, EventArgs e)

{

//Gut the current response, issue an error,

//and tell the runtime the error has been processed.

Response.Clear();

Response.Write("I am sorry...I can't find a required file."); Server.ClearError();

}

protected void btnGetFile_Click(object sender, EventArgs e)

{

//Try to open a nonexistent file.

//This will fire the Error event for this page.

System.IO.File.ReadAllText(@"C:\IDontExist.txt");

}

...

}

Notice that your Error event handler begins by clearing out any content currently within the HTTP response and emits a generic error message. If you wish to gain access to the specific

System.Exception object, you may do so using the HttpServerUtility.GetLastError() method exposed by the inherited Server property:

void _Default_Error(object sender, EventArgs e)

{

Response.Clear();

Response.Write("I am sorry...I can't find a required file.<br>"); Response.Write(string.Format("The error was: <b>{0}</b>",

Server.GetLastError().Message)); Server.ClearError();

}

Finally, note that before exiting this generic error handler, you are explicitly calling the HttpServerUtility.ClearError() method via the Server property. This is required, as it informs the runtime that you have dealt with the issue at hand and require no further processing. If you forget to do so, you the end user will be presented with the runtime’s error page. Figure 23-19 shows the result of this error-trapping logic.

Figure 23-19. Page-level error handling

CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

865

At this point, you should feel quite confident with your knowledge of the architecture of an ASP.NET Page type. Now that you have such a foundation, you can turn your attention to the role of ASP.NET web controls.

Source Code The PageLifeCycle files are included under the Chapter 23 subdirectory.

Understanding the Nature of Web Controls

Perhaps the major benefit of ASP.NET is the ability to assemble the UI of your pages using the types defined in the System.Web.UI.WebControls namespace. As you have seen, these controls (which go by the names server controls, web controls, or web form controls) are extremely helpful in that they automatically generate the necessary HTML for the requesting browser and expose a set of events that may be processed on the web server. Furthermore, because each ASP.NET control has a corresponding class in the System.Web.UI.WebControls namespace, it can be manipulated in an OO manner from your *.aspx file (within a <script> block) as well as within the associated class defined in the code-behind file.

As you have seen, when you configure the properties a web control using the Visual Studio 2005 Properties window, your edits are recorded in the open declaration of a given widget in the *.aspx file as a series of name/value pairs. Thus, if you add a new TextBox to the designer of a given *.aspx file and change the BorderStyle, BorderWidth, BackColor, Text, and BorderColor properties using the IDE, the opening <asp:TextBox> tag is modified as follows:

<asp:TextBox id=myTextBox runat="server" BorderStyle="Ridge" BorderWidth="5px" BackColor="PaleGreen" BorderColor="DarkOliveGreen" Text = "Yo dude" > </asp:TextBox>

Given that the HTML declaration of a web control eventually becomes a member variable from the System.Web.UI.WebControls namespace (via the dynamic compilation cycle), you are able to interact with the members of this type within a server-side <script> block or the page’s code-behind file, for example:

public partial class _Default : System.Web.UI.Page

{

...

protected void btnChangeTextBoxColor_Click(object sender, EventArgs e)

{

// Modify the HTTP response data for this widget. this.myTextBox.BackColor = System.Drawing.Color.Red;

}

}

All ASP.NET web controls ultimately derive from a common base class named System.Web.UI. WebControls.WebControl. WebControl in turn derives from System.Web.UI.Control (which derives from System.Object). Control and WebControl each define a number of properties common to all server-side controls. Before we examine the inherited functionality, let’s formalize what it means to handle a server-side event.

Qualifying Server-Side Event Handling

Given the current state of the World Wide Web, it is impossible to avoid the fundamental nature of browser/web server interaction. Whenever these two entities communicate, there is always an underlying, stateless, HTTP request-and-response cycle. While ASP.NET server controls do a great deal to shield you from the details of the raw HTTP protocol, always remember that treating the

866CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

Web as an event-driven entity is just a magnificent smoke-and-mirrors show provided by the CLR, and it is not identical to the event-driven model of a Windows-based UI.

Thus, although the System.Windows.Forms and System.Web.UI.WebControls namespaces define types with the same simple names (Button, TextBox, GridView, Label, and so on), they do not expose an identical set of events. For example, there is no way to handle a server-side MouseMove event when the user moves the cursor over a Web Form Button type. Obviously, this is a good thing. (Who wants to post back to the server each time the mouse moves?)

The bottom line is that a given ASP.NET web control will expose a limited set of events, all of which ultimately result in a postback to the web server. Any necessary client-side event processing will require you to author blurbs of client-side JavaScript/VBScript script code to be processed by the requesting browser’s scripting engine.

The AutoPostBack Property

It is also worth pointing out that many of the ASP.NET web controls support a property named AutoPostBack (most notably, the CheckBox, RadioButton, and TextBox controls, as well as any widget that derives from the abstract ListControl type). By default, this property is set to false, which disables the automatic posting of server-side events (even if you have indeed rigged up the event in the code-behind file). In many cases, this is the exact behavior you require.

However, if you wish to cause any of these widgets to post back to a server-side event handler, simply set the value of AutoPostBack to true. This technique can be helpful if you wish to have the state of one widget automatically populate another value within another widget on the same page.

To illustrate, create a website that contains a single TextBox (named txtAutoPostback) and a single ListBox control (named lstTextBoxData). Now, handle the TextChanged event of the TextBox, and within the server-side event handler, populate the ListBox with the current value in the TextBox (got all that?):

protected void txtAutoPostback_TextChanged(object sender, EventArgs e)

{

lstTextBoxData.Items.Add(txtAutoPostback.Text);

}

If you run the application as is, you will find that as you type in the TextBox, nothing happens. Furthermore, if you type in the TextBox and tab to the next control, nothing happens. The reason is that the AutoPostBack property of the TextBox is set to false by default. However, if you set this property to true as follows:

<asp:TextBox ID="txtAutoPostback" runat="server" AutoPostBack="True" OnTextChanged="txtAutoPostback_TextChanged">

</asp:TextBox>

you will find that when you tab off the TextBox (or press the Enter key), the ListBox is automatically populated with the current value in the TextBox. To be sure, beyond the need to populate the items of one widget based on the value of another widget, you will typically not need to alter the state of a widget’s AutoPostBack property.

The System.Web.UI.Control Type

The System.Web.UI.Control base class defines various properties, methods, and events that allow the ability to interact with core (typically non-GUI) aspects of a web control. Table 23-9 documents some, not all, members of interest.

CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

867

Table 23-9. Select Members of System.Web.UI.Control

Member

Meaning in Life

Controls

This property gets a ControlCollection object that represents the child

 

controls within the current control.

DataBind()

This method binds a data source to the invoked server control and all its

 

child controls.

EnableThemeing

This property establishes if the control supports theme functionality.

HasControls()

This method determines if the server control contains any child controls.

ID

This property gets or sets the programmatic identifier assigned to the

 

server control.

Page

This property gets a reference to the Page instance that contains the server

 

control.

Parent

This property gets a reference to the server control’s parent control in the

 

page control hierarchy.

SkinID

This property gets or sets the “skin” to apply to the control. Under ASP.NET

 

2.0, it is now possible to establish a control’s overall look and feel on the fly

 

via skins.

Visible

This property gets or sets a value that indicates whether a server control is

 

rendered as UI element on the page.

 

 

Enumerating Contained Controls

The first aspect of System.Web.UI.Control we will examine is the fact that all web controls (including Page itself) inherit a custom controls collection (accessed via the Controls property). Much like in a Windows Forms application, the Controls property provides access to a strongly typed collection of WebControl-derived types. Like any .NET collection, you have the ability to add, insert, and remove items dynamically at runtime.

While it is technically possible to directly add web controls directly to a Page-derived type, it is easier (and a wee bit safer) to make use of a Panel widget. The System.Web.UI.WebControls.Panel class represents a container of widgets that may or may not be visible to the end user (based on the value of its Visible and BorderStype properties).

To illustrate, create a new website named DynamicCtrls. Using the Visual Studio 2005 page designer, add a Panel type (named myPanel) that contains a TextBox, Button, and HyperLink widget named whatever you choose (be aware that the designer requires that you drag internal items within the UI of the Panel type). Once you have done so, the <form> element of your *.aspx file has been updated as so:

<asp:Panel ID="myPanel" runat="server" Height="50px" Width="125px"> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br /> <asp:Button ID="Button1" runat="server" Text="Button" /><br /> <asp:HyperLink ID="HyperLink1" runat="server">HyperLink </asp:HyperLink>

</asp:Panel>

Next, place a Label widget outside the scope of the Panel (named lblControlInfo) to hold the rendered output. Assume in the Page_Load() event you wish to obtain a list of all the controls contained within the Panel and assign the results to the Label type:

868CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

public partial class _Default : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

ListControlsInPanel();

}

private void ListControlsInPanel()

{

string theInfo;

theInfo = String.Format("Has controls? {0}<br>", myPanel.HasControls());

foreach (Control c in myPanel.Controls)

{

if (c.GetType() != typeof(System.Web.UI.LiteralControl))

{

theInfo += "***************************<br>"; theInfo += String.Format("Control Name? {0}<br>",

c.ToString());

theInfo += String.Format("ID? {0}<br>", c.ID); theInfo += String.Format("Control Visible? {0}<br>",

c.Visible);

theInfo += String.Format("ViewState? {0}<br>", c.EnableViewState);

}

}

lblControlInfo.Text = theInfo;

}

}

Here, you iterate over each WebControl maintained on the Panel and perform a check to see if the current type is of type System.Web.UI.LiteralControl. This type is used to represent literal HTML tags and content (such as <br>, text literals, etc.). If you do not do this sanity check, you might be surprised to find a total of seven types in the scope of the Panel (given the *.aspx declaration seen previously). Assuming the type is not literal HTML content, you then print out some various statistics about the widget. Figure 23-20 shows the output.

CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

869

Figure 23-20. Enumerating contained widgets

Dynamically Adding (and Removing) Controls

Now, what if you wish to modify the contents of a Panel at runtime? The process should look very familiar to you, given your work with Windows Forms earlier in this text. Let’s update the current page to support an additional Button (named btnAddWidgets) that dynamically adds five new TextBox types to the Panel, and another Button that clears the Panel widget of all controls. The Click event handlers for each are shown here:

protected void btnAddWidgets_Click(object sender, EventArgs e)

{

for (int i = 0; i < 5; i++)

{

//Assign a name so we can get

//the text value out later

//using the HttpRequest.QueryString()

//method.

TextBox t = new TextBox();

t.ID = string.Format("newTextBox{0}", i); myPanel.Controls.Add(t); ListControlsInPanel();

}

}

870 CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

protected void btnRemovePanelItems_Click(object sender, EventArgs e)

{

myPanel.Controls.Clear();

ListControlsInPanel();

}

Notice that you assign a unique ID to each TextBox (e.g., newTextBox1, newTextBox2, and so on) to obtain its contained text programmatically using the HttpRequest.Form collection (shown momentarily).

To obtain the values within these dynamically generated TextBoxes, update your UI with one additional Button and Label type. Within the Click event handler for the Button, loop over each item contained within the HttpRequest.NameValueCollection type (accessed via HttpRequest.Form) and concatenate the textual information to a locally scoped System.String. Once you have exhausted the collection, assign this string to the Text property of the new Label widget named lblTextBoxText:

protected void btnGetTextBoxValues_Click(object sender, System.EventArgs e)

{

string textBoxValues = "";

for(int i = 0; i < Request.Form.Count; i++)

{

textBoxValues +=

string.Format("<li>{0}</li><br>", Request.Form[i]);

}

lblTextBoxText.Text = textBoxValues;

}

When you run the application, you will find that you are able to view the content of each text box, including a rather long (unreadable) string. This string contains the view state for each widget on the page and will be examined later in the next chapter. Also, you will notice that once the request has been processed, the ten new text boxes disappear. Again, the reason has to do with the stateless nature of HTTP. If you wish to maintain these dynamically created TextBoxes between postbacks, you need to persist these objects using ASP.NET state programming techniques (also examined in the next chapter).

Source Code The DynamicCtrls files are included under the Chapter 23 subdirectory.

Key Members of the

System.Web.UI.WebControls.WebControl Type

As you can tell, the Control type provides a number of non-GUI-related behaviors. On the other hand, the WebControl base class provides a graphical polymorphic interface to all web widgets, as suggested in Table 23-10.

Table 23-10. Properties of the WebControl Base Class

Properties

Meaning in Life

BackColor

Gets or sets the background color of the web control

BorderColor

Gets or sets the border color of the web control

BorderStyle

Gets or sets the border style of the web control

BorderWidth

Gets or sets the border width of the web control

CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

871

Properties

Meaning in Life

Enabled

Gets or sets a value indicating whether the web control is enabled

CssClass

Allows you to assign a class defined within a Cascading Style Sheet to a web

 

widget

Font

Gets font information for the web control

ForeColor

Gets or sets the foreground color (typically the color of the text) of the web

 

control

Height

Get or set the height and width of the web control

Width

 

TabIndex

Gets or sets the tab index of the web control

ToolTip

Gets or sets the tool tip for the web control to be displayed when the cursor is

 

over the control

 

 

I’d bet that almost all of these properties are self-explanatory, so rather than drill through the use of all these properties, let’s shift gears a bit and check out a number of ASP.NET Web Form controls in action.

Categories of ASP.NET Web Controls

The types in System.Web.UI.WebControls can be broken down into several broad categories:

Simple controls

(Feature) Rich controls

Data-centric controls

Input validation controls

Login controls

The simple controls are so named because they are ASP.NET web controls that map to standard HTML widgets (buttons, lists, hyperlinks, image holders, tables, etc.). Next, we have a small set of controls named the rich controls for which there is no direct HTML equivalent (such as the Calendar, TreeView, Wizard, etc.). The data-centric controls are widgets that are typically populated via a given data connection. The best (and most exotic) example of such a control would be the ASP.NET GridView. Other members of this category include “repeater” controls and the lightweight DataList. The validation controls are server-side widgets that automatically emit client-side JavaScript, for the purpose of form field validation. Finally, as of ASP.NET 2.0, the base class libraries ship with a number of security-centric controls. These UI elements completely encapsulate the details of logging into

a site, providing password-retrieval services and managing user roles.

Note Given that this text does not cover the details of the .NET security system, I will not comment on the new security controls here. If you require a detailed treatment of ASP.NET 2.0 security, check out Expert ASP.NET 2.0 Advanced Application Design by Dominic Selly, Andrew Troelsen, and Tom Barnaby (Apress, 2006).

A Brief Word Regarding System.Web.UI.HtmlControls

Truth be told, there are two distinct web control toolkits that ship with ASP.NET 2.0. In addition to the ASP.NET web controls (within the System.Web.UI.WebControls namespace), the base class libraries also provide the System.Web.UI.HtmlControls widgets.

872 CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

The HTML controls are a collection of types that allow you to make use of traditional HTML controls on a Web Forms page. However, unlike raw HTML tags, HTML controls are OO entities that can be configured to run on the server and thus support server-side event handling. Unlike ASP.NET web controls, HTML controls are quite simplistic in nature and offer little functionality beyond standard HTML tags (HtmlButton, HtmlInputControl, HtmlTable, etc.).

The HTML controls provide a public interface that mimics standard HTML attributes. For example, to obtain the information within an input area, you make use of the Value property, rather than the web control–centric Text property. Given that the HTML controls are not as feature-rich as the ASP.NET web controls, I won’t make further mention of them in this text. If you wish to investigate these types, consult the .NET Framework 2.0 SDK documentation for further details.

Building a Simple ASP.NET 2.0 Website

Space does not permit me to walk through the details of each and every web control that ships with ASP.NET 2.0 (that would require a sizable book in and of itself). However, to illustrate the process of working with various ASP.NET web controls, the next task of this chapter is to construct a website that will demonstrate the use of the following techniques:

Working with master pages

Working with the Menu control

Working with the GridView control

Working with the Wizard control

As you work through this example, remember that Web Form controls encapsulate the process of generating corresponding HTML tags and follow a Windows Forms model. To begin, create a new ASP.NET web application named AspNetCarsSite.

Working with Master Pages

As I am sure you are aware, many websites provide a consistent look and feel across multiple pages (a common menu navigation system, common header and footer content, company logo, etc.). Under ASP.NET 1.x, developers made extensive use of UserControls and custom web controls to define web content that was to be used across multiple pages. While UserControls and custom web controls are still a very valid option under ASP.NET 2.0, we are now provided with the concept of master pages to address the same issue.

Simply put, a master page is little more than an ASP.NET page that takes a *.master file extension. On their own, master pages are not viewable from a client-side browser (in fact, the ASP.NET runtime will not server this flavor of web content). Rather, master pages define a common UI frame shared by all pages (or a subset of pages) in your site. As well, a *.master page defines various placeholder tags that contain additional content within an *.aspx file. The end result is a single, unified UI.

Insert a new master page into your website (via the WebSite Add New Item menu selection) and observe the initial definition:

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">

CHAPTER 23 ASP. NET 2 . 0 WEB PAGES AND WEB CONTROLS

873

<title>Untitled Page</title> </head>

<body>

<form id="form1" runat="server"> <div>

<asp:contentplaceholder id="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder>

</div>

</form>

</body>

</html>

The first point of interest is the new <%@Master%> directive. For the most part, this directive supports the same attributes as <%@Page%>. For example, notice how by default a master page makes use of a code-behind file (which is technically optional). Like Page types, a master page derives from

a specific base class, which in this case is MasterPage:

public partial class MasterPage : System.Web.UI.MasterPage

{

protected void Page_Load(object sender, EventArgs e)

{

}

}

It is important to know that the attributes defined within the <%@Master%> directive do not “flow through” to the related *.aspx files. Thus, if you wish to make use of C# within your master page but author an associated *.aspx file in Visual Basic .NET, you may do so.

The other point of interest is the <asp:contentplaceholder> type. This region of a master page represents the UI widgets of the related *.aspx file, not the content of the master page itself. If you do intend to blend an *.aspx file within this region, the scope within the <asp:contentplaceholder> and </asp:contentplaceholder> tags will be empty. However, if you so choose, you are able to populate this area with various web controls that function as a default UI to use in the event that a given *.aspx file in the site does not supply specific content. For this example, assume that each *.aspx page in your site will indeed supply custom content.

Note A *.master page may define as many content place holders as necessary. As well, a single *.master page may nest additional *.master pages.

As you would hope, you are able to build a common UI of a *.master file using the same Visual Studio 2005 designers used to build *.aspx files. For your site, you will add a descriptive Label (to serve as a common welcome message), an AdRotator control (which will randomly display one of two images), and a Menu control (to allow the user to navigate to other areas of the site).

Working with the Menu Control

ASP.NET 2.0 ships with several new web controls that allow you to handle site navigation: SiteMapPath, TreeView, and Menu. As you would expect, these web widgets can be configured in multiple ways. For example, each of these controls can dynamically generate its nodes via an external XML file or data source. For your Menu type, you will simply hard-code three values.

Using the page designer, select the Menu control, activate the inline editor (located at the upper left of the widget), and select the Edit Menu Items option. Add three root items named Home, Build a Car, and View Inventory. Before dismissing the dialog box, set the NavigateUrl property for each node to the following (yet to be constructed) pages: