The Web Forms Programming Model

The Web Forms Programming Model

Calc.aspx demonstrates three important principles of the Web Forms programming model:

  • A Web form s user interface is declared using a combination of HTML and server controls. Controls can be customized by using control properties as attributes in the tags that declare the controls. Controls are also bona fide objects that are instantiated and executed each time the page that hosts them is requested.

  • Server controls fire events that can be handled by server-side scripts. In effect, ASP.NET abstracts the divide between client and server by creating the illusion that events are fired and handled on the same machine. In reality, events fire on the server when an external stimulus (such as the click of a button) causes the form to post back to the server.

  • Server-side scripts aren t scripts in the conventional sense of the word. Unlike ASP scripts, which are interpreted rather than compiled and therefore run rather slowly, server-side scripts in ASP.NET are compiled to common intermediate language (CIL) and executed by the common language runtime. Although ASP.NET pages incur more processing overhead than static HTML pages, they tend to execute much faster than ASP pages.

You probably noticed the RunAt= server attributes sprinkled throughout Calc.aspx. RunAt= server is the key that unlocks the door to the magic of Web forms; it signals ASP.NET to execute the tag rather than treat it as static HTML. RunAt= server is not optional. It must be used in every tag that ASP.NET is to process, including the <form> tag that marks the beginning of a form containing server controls.

Web Controls

TextBox, Button, and Label are server controls. They re also examples of Web controls server controls defined in the FCL s System.Web.UI.WebControls namespace. The Web controls family includes almost 30 different control types that you can use in ASP.NET Web forms. The following table lists the Web controls provided in version 1 of the .NET Framework class library:

Web Controls

Class Name

Description

AdRotator

Displays rotating banners in Web forms

Button

Generates submit buttons

Calendar

Displays calendars with selectable dates

CheckBox

Displays a check box in a Web form

CheckBoxList

Displays a group of check boxes

CompareValidator

Validates user input by comparing it to another value

CustomValidator

Validates user input using the algorithm of your choice

DataGrid

Displays data in tabular format

DataList

Displays items in single-column or multicolumn lists using HTML templates

DropDownList

Generates HTML drop-down lists

HyperLink

Generates hyperlinks

Image

Displays images in Web forms

ImageButton

Displays graphical push buttons

Label

Generates programmable text fields

LinkButton

Generates hyperlinks that post back to the server

ListBox

Generates HTML list boxes

Literal

Generates literal text in a Web form

Panel

Groups other controls

RadioButton

Displays a radio button in a Web form

RadioButtonList

Displays a group of check boxes

RangeValidator

Verifies that user input falls within a specified range

RegularExpressionValidator

Validates user input using regular expressions

Repeater

Displays items using HTML templates

RequiredFieldValidator

Verifies that an input field isn t empty

Table

Generates HTML tables

TextBox

Generates text input fields

ValidationSummary

Displays a summary of validation errors

Xml

Displays XML documents and optionally formats them using XSLT

Some Web controls are simple devices that produce equally simple HTML. Others produce more complex HTML, and some even return client-side script. Calendar controls, for example, emit a rich mixture of HTML and JavaScript. It s not easy to add a calendar to a Web page by hand (especially if you want dates in the calendar to be clickable), but calendars are no big deal in Web forms: you simply include an <asp:Calendar> tag in an ASPX file. DataGrid is another example of a sophisticated control type. One DataGrid control can replace reams of old ASP code that queries a database and returns the results in a richly formatted HTML table. You ll learn all about the DataGrid and other Web controls in the next chapter.

HTML Controls

Most Web forms are built from Web controls, but ASP.NET supports a second type of server control called HTML controls. HTML controls are instances of classes defined in the FCL s System.Web.UI.HtmlControls namespace. They re declared by adding RunAt= server (or, if you d prefer, runat= server ; capitalization doesn t matter in HTML) attributes to ordinary HTML tags. For example, the statement

<input type="text" />

declares a standard HTML text input field. However, the statement

<input type="text" runat="server" />

declares an HTML control specifically, an instance of System.Web.UI.HtmlControls.HtmlInputText. At run time, ASP.NET sees the runat= server attribute and creates an HtmlInputText object. The HtmlInputText object, in turn, emits an <input type= text > tag that s ultimately returned to the browser.

Without realizing it, you used an HTML control in Calc.aspx. The line

<form runat="server">

caused an instance of System.Web.UI.HtmlControls.HtmlForm to be created on the server. HtmlForm returned the <form> tag that you saw when you viewed the page s HTML source code with the View/Source command:

<form name="_ctl0" method="post" action="calc.aspx" >

HtmlInputText and HtmlForm are but two of many controls defined in the System.Web.UI.HtmlControls namespace. The following table lists all the HTML controls that the FCL supports and the tags that produce them.

HTML Controls

Tag

Corresponding HTML Control

<a runat= server >

HtmlAnchor

<button runat= server >

HtmlButton

<form runat= server >

HtmlForm

<img runat= server >

HtmlImage

<input type= button runat= server >

HtmlInputButton

<input type= reset runat= server >

HtmlInputButton

<input type= submit runat= server >

HtmlInputButton

<input type= checkbox runat= server >

HtmlInputCheckBox

<input type= file runat= server >

HtmlInputFile

<input type= hidden runat= server >

HtmlInputHidden

<input type= image runat= server >

HtmlInputImage

<input type= radio runat= server >

HtmlInputRadioButton

<input type= password runat= server >

HtmlInputText

<input type= text runat= server >

HtmlInputText

<select runat= server >

HtmlSelect

<table runat= server >

HtmlTable

<td runat= server >

HtmlTableCell

<th runat= server >

HtmlTableCell

<tr runat= server >

HtmlTableRow

<textarea runat= server >

HtmlTextArea

Any other tag with runat= server

HtmlGenericControl

It s important to know which HtmlControls class corresponds to a given HTML tag because only by knowing the class name can you consult the documentation to determine which properties you can use with that tag and which events the resulting control fires. For example, here s the HTML controls version of Calc.aspx:

<html> <body> <form runat="server"> <input type="text"  runat="server" /> + <input type="text"  runat="server" /> <input type="submit" value="  =  " OnServerClick="OnAdd" runat="server" /> <span  runat="server" /> </form> </body> </html> <script language="C#" runat="server"> void OnAdd (Object sender, EventArgs e) { int a = Convert.ToInt32 (op1.Value); int b = Convert.ToInt32 (op2.Value); Sum.InnerText = (a + b).ToString (); } </script>

Besides the different way in which the form s controls are declared, the HTML controls version of this Web form differs from the Web controls version in three important respects:

  • The attribute that wires the button control to the event handler is named OnServerClick rather than OnClick. Why? Because an <input type= button runat= server /> tag translates into an instance of HtmlInputButton, and HtmlInputButton controls, unlike Button controls, don t fire Click events. They fire ServerClick events.

  • OnAdd reads input from the text boxes using the property name Value rather than Text. HtmlInputText controls don t have Text properties as Labels and TextBoxes do; instead, they expose their contents using Value properties.

  • OnAdd writes its output by initializing Sum s InnerText property instead of its Text property. The <span runat= server > tag creates an instance of HtmlGenericControl. HtmlGenericControl doesn t have a Text property, but it does have an InnerText property.

Once you know which class ASP.NET instantiates as a result of applying a runat= server tag to an otherwise ordinary HTML tag, you can figure out from the documentation what the tag s programmatic interface looks like.

Why does ASP.NET support HTML controls when Web controls do everything HTML controls do and then some? HTML controls simplify the task of turning existing HTML forms into Web forms. It takes a while to convert a couple of hundred <input> tags and <select> tags and other HTML tags into Web controls. It doesn t take long to add runat= server to each of them.

Page-Level Events

Server controls that render HTML and fire events are a cornerstone of the Web Forms programming model, but controls aren t the only entities that fire events. Pages do, too. To understand page-level events, it helps to understand what goes on behind the scenes when ASP.NET processes the first HTTP request for an ASPX file:

  1. It creates a temporary file containing a class derived from System.Web.UI.Page. The Page class is one of the most important classes in ASP.NET; it represents ASP.NET Web pages.

  2. ASP.NET copies the code in the ASPX file, as well as some code of its own, to the Page-derived class. A method named OnAdd in a <script> block in an ASPX file becomes a member method of the derived class.

  3. ASP.NET compiles the derived class and places the resulting DLL in a system folder. The DLL is cached so that steps 1 and 2 won t have to be repeated unless the contents of the ASPX file change.

  4. ASP.NET instantiates the derived class and executes it by calling a series of methods on it. It is during this execution phase that the Page object instantiates any controls declared inside it and solicits their output.

As a Page object executes, it fires a series of events that can be processed by server-side scripts. The most important are Init, which is fired when the page is first instantiated, and Load, which is fired after the page s controls are initialized but before the page renders any output. The Load event is particularly important to ASP.NET developers because a Load handler is the perfect place to initialize any controls that require dynamic (that is, run-time) initialization. The next section offers an example.

If you want to see the DLLs that ASP.NET generates from your ASPX files, you ll find them in subdirectories under the Windows (or Winnt) directory s Microsoft.NET\Framework\vn.n.nnnn\Temporary ASP.NET Files subdirectory, where n.n.nnnn is the version number of the .NET Framework installed on your PC. Drill down in the directory tree under Temporary ASP.NET Files\root, for example, and you ll find a DLL containing the class that ASP.NET derived from Page to serve Calc.aspx (assuming you ran Calc.aspx from \Inetpub\wwwroot). If the subdirectory contains several DLLs, open them with ILDASM, and you ll find one containing a Page-derived class named Calc_aspx. (See Figure 5-8.) That s the class ASP.NET instantiates each time a request arrives for Calc.aspx. If Calc.aspx changes, ASP.NET recompiles the DLL on the next request. Otherwise, the DLL remains on your hard disk so that ASP.NET can reuse it as needed.

Figure 5-8

DLL generated from Calc.aspx.

The Page.Load Event and the Page.IsPostBack Property

Suppose you want to build a Web form that displays today s date and the four days following it in a drop-down list. If today is January 1, 2002, one solution is to statically initialize a DropDownList control:

<asp:DropDownList  RunAt="server"> <asp:ListItem Text="January 1, 2002" RunAt="server" /> <asp:ListItem Text="January 2, 2002" RunAt="server" /> <asp:ListItem Text="January 3, 2002" RunAt="server" /> <asp:ListItem Text="January 4, 2002" RunAt="server" /> <asp:ListItem Text="January 5, 2002" RunAt="server" /> </asp:DropDownList>

The problem with this approach is obvious: every day you ll have to modify the form to update the dates. A smarter approach is to write a handler for the page s Load event that initializes the DropDownList at run time:

<asp:DropDownList  RunAt="server" /> . . . <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { for (int i=0; i<5; i++) { DateTime date = DateTime.Today + new TimeSpan (i, 0, 0, 0); MyList.Items.Add (date.ToString ("MMMM dd, yyyy")); } } } </script>

A Page_Load method prototyped this way is automatically called by ASP.NET when the page fires a Load event. You don t have to manually wire the event to the handler as you do for controls. The same is true for all page-level events. You can respond to any event fired by Page by writing a method named Page_EventName, where EventName is the name of the event you want to handle.

The Page_Load handler in the previous example adds items to the DropDownList by calling Add on the control s Items collection. Items represents the items in the DropDownList. Significantly, this implementation of Page_Load initializes the control only if a value named IsPostBack is false. IsPostBack is one of several properties defined in the Page class. Because all code in an ASPX file executes in the context of a class derived from Page, your code enjoys intrinsic access to Page properties and methods. IsPostBack is a particularly important property because it reveals whether your code is executing because the page was requested from the Web server with an HTTP GET (IsPostBack==false) or because the page was posted back to the server (IsPostBack==true). In general, you don t want to initialize a Web control during a postback because ASP.NET maintains the control s state for you. If you call Add on the control s Items collection the first time the page is fetched and then call it again when the page is posted back, the control will have twice as many items in it following the first postback.

The Page.Init Event

Page_Load methods are handy for performing run-time control initializations. You can also write Page_Init methods that fire in response to Init events. One use for Init events is to create controls and add them to the page at run time. Another is to programmatically wire events to event handlers. For example, instead of connecting Click events to an event handler with an OnClick attribute, like this:

<asp:Button Text="  =  " OnClick="OnAdd" RunAt="server" /> . . . <script language="C#" runat="server"> void OnAdd (Object sender, EventArgs e) { int a = Convert.ToInt32 (op1.Text); int b = Convert.ToInt32 (op2.Text); Sum.Text = (a + b).ToString (); } </script>

you could connect them programmatically in this manner:

<asp:Button Text="  =  "  RunAt="server" /> . . . <script language="C#" runat="server"> void Page_Init (Object sender, EventArgs e) { EqualsButton.Click += new EventHandler (OnAdd); } void OnAdd (Object sender, EventArgs e) { int a = Convert.ToInt32 (op1.Text); int b = Convert.ToInt32 (op2.Text); Sum.Text = (a + b).ToString (); } </script>

This is the technique that Visual Studio .NET uses to wire events to event handlers. You ll see an example at the end of this chapter when you build a Web Forms application with Visual Studio .NET.

Page-Level Directives

ASP.NET supports a number of commands called page-level directives that you can put in ASPX files. They re sometimes called @ directives because all directive names begin with an @ sign: @ Page, @ Import, and so on. Page-level directives appear between <% and %> symbols and must be positioned at the top of an ASPX file. In practice, @ directives appear in all but the simplest of ASPX files. The following table lists the directives that ASP.NET supports. Succeeding sections document the most commonly used directives. Other directives are discussed as circumstances warrant elsewhere in the book.

ASP.NET @ Directives

Directive

Description

@ Page

Defines general attributes and compilation settings for ASPX files

@ Control

Defines general attributes and compilation settings for ASCX files

@ Import

Imports a namespace

@ Assembly

Enables linkage to assemblies not linked to by default

@ Register

Registers user controls and custom controls for use in a Web form

@ OutputCache

Exerts declarative control over page caching and fragment caching

@ Reference

Adds a reference to an external ASPX or ASCX file

@ Implements

Identifies an interface implemented by a Web page

The @ Page Directive

Of the various page-level directives that ASP.NET supports, @ Page is the one used most often. The following @ Page directive changes the default language for all scripts that don t specify otherwise from Visual Basic .NET to C#. It s especially useful when you inline code in an ASPX file by placing it between <% and %> tags:

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

And here s an ASPX file that uses it:

<%@ Page Language="C#" %> <html> <body> <% Response.Write ("Hello, world"); %> </body> </html>

As this example demonstrates, ASP.NET pages can use Response and other intrinsic objects in the same way ASP pages can. Because you can t include Language= C# attributes in <% %> blocks, you either need an @ Page directive telling ASP.NET which compiler to pass your code to or a Web.config file that changes the default language on a directory-wide basis. (If you re not familiar with Web.config files just yet, don t worry about it for now. You ll learn all about them in Chapter 9.)

Another common use for @ Page directives is to enable debugging support. By default, ASP.NET builds release-build DLLs from your ASPX files. If you encounter a run-time error and need to debug it, you need DLLs with debugging symbols. The statement

<%@ Page Debug="true" %>

commands ASP.NET to create debug DLLs rather than release DLLs and enriches the information available to you when you debug a malfunctioning Web form.

As you can see, @ Page is overloaded to support a variety of uses. In all, it supports some 28 different attributes such as Language and Debug. A page can have only one @ Page directive, but that directive can contain any number of attributes. For example, the statement

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

enables debugging and sets the page s default language to C#.

The @ Import Directive

Next to @ Page, the directive that ASP.NET programmers use the most is @ Import. The @ Import directive is ASP.NET s equivalent of C# s using directive. Its purpose is to import a namespace so that the types in that namespace are known to the compiler. You need @ Import any time you use an FCL data type that s defined in a namespace that ASP.NET doesn t import by default. For example, the statement

<%@ Import Namespace="System.Data" %>

makes all the data types defined in System.Data available to a Web form.

What namespaces does ASP.NET import by default? Here s a complete list:

  • System

  • System.Collections

  • System.Collections.Specialized

  • System.Configuration

  • System.IO

  • System.Text

  • System.Text.RegularExpressions

  • System.Web

  • System.Web.Caching

  • System.Web.Security

  • System.Web.SessionState

  • System.Web.UI

  • System.Web.UI.HtmlControls

  • System.Web.UI.WebControls

Because System.Data isn t imported automatically, you must import it yourself if you want to use System.Data types (for example, DataSet) in a Web form. Otherwise, you ll receive an error message the first time ASP.NET attempts to compile the page. System.Web.Mail is another example of a commonly used namespace that isn t imported automatically. Look back at Chapter 3 s SendMail program (Figure 3-7), and you ll see an @ Import statement importing System.Web.Mail on the very first line of the ASPX file.

Unlike @ Page, @ Import can appear multiple times in a Web page. The following statements import three namespaces and are often used together in ASPX files that access SQL Server databases:

<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.Data.SqlTypes" %>

The @ Assembly Directive

The @ Import directive identifies namespaces containing an application s data types; @ Assembly identifies assemblies. The .NET Framework class library is implemented in a series of single-file assemblies: Mscorlib.dll, System.dll, and others. If ASP.NET is to compile your page, it must know which assemblies the page references so that it can provide that information to the compiler. The following assembly names are provided to the compiler by default and therefore require no @ Assembly directive:

  • Mscorlib.dll

  • System.dll

  • System.Data.dll

  • System.Drawing.dll

  • System.EnterpriseServices.dll

  • System.Web.dll

  • System.Web.Services.dll

  • System.Xml.dll

These assemblies include the data types that Web forms are most likely to use. But suppose you want to use the FCL s System.DirectoryServices.DirectorySearcher class in a <script> block to perform a query against Active Directory. Because DirectorySearcher lives in an assembly (System.DirectoryServices.dll) that ASP.NET doesn t reference by default, its use requires an @ Assembly directive. In the following example, @ Import is required also because DirectorySearcher is defined in a nondefault namespace:

<%@ Import Namespace="System.DirectoryServices" %> <%@ Assembly Name="System.DirectoryServices" %>

It s coincidental that the namespace name and assembly name are one and the same; that s not always the case. Note that an assembly name passed to @ Assembly must not include the filename extension (.dll). In addition, the list of default assemblies can be changed by editing a machine-wide configuration file named Machine.config or augmented by dropping a Web.config file containing an <assemblies> section into an application root. Like @ Import, @ Assembly can appear multiple times in a Web page.

The @ OutputCache Directive

One of the best ways to optimize the performance of ASP.NET applications is to cache Web pages generated from ASPX files so that they can be delivered straight from the cache if they re requested again. ASP.NET supports two forms of caching: page caching, which caches entire pages, and fragment (or subpage) caching, which caches portions of pages. The @ OutputCache directive enables an application to exert declarative control over page and fragment caching.

Because examples have a way of lending clarity to a subject (funny how that works, isn t it?), here s a simple one that demonstrates @ OutputCache:

<%@ Page Language="C#" %> <%@ OutputCache Duration="60" VaryByParam="None" %> <html> <body> Today is <%= DateTime.Now.ToLongDateString () %> </body> </html>

This ASPX file displays today s date in a Web page. (The <%= ... %> syntax is an alternative to using Response.Write. It s an easy way to inject text into the page s output.) Since today s date changes only every 24 hours, it s wasteful to reexecute this page every time it s requested. Therefore, the page includes an @ OutputCache directive that caches the output for 60 seconds at a time. Subsequent requests for the page come straight from the cache. When the cache expires and ASP.NET receives another request for the page, ASP.NET reexecutes (and recaches) the page. The Duration attribute controls the length of time that the cached page output is valid.

In real life, Web pages are rarely this simple. The output from an ASPX file often varies based on input provided by users. The designers of ASP.NET anticipated this and gave the page cache the ability to hold multiple versions of a page, qualified by the user input that produced each version. Imagine, for example, that you wrote a Web form that takes a city name and state name as input and returns a satellite image of that city from a database. (Sound far-fetched? It s not. Chapter 11 includes an application that does just that.) If the city and state names accompanying each request are transmitted in variables called city and state, the following directive caches a different version of the page for each city and state requested for up to 1 hour:

<%@ OutputCache Duration="3600" VaryByParam="city;state" %>

It s that simple. You can even use a shortened form of the VaryByParam attribute to cache a separate version of the page for every different input:

<%@ OutputCache Duration="3600" VaryByParam="*" %>

Now if two users request a satellite image of Knoxville, Tennessee, 30 minutes apart, the second of the two requests will be fulfilled very quickly.

A Web Forms Currency Converter

Figure 5-9 shows a Web form that performs currency conversions using exchange rates stored in an XML file. To see it in action, copy Converter.aspx and Rates.xml, which are listed in Figures 5-10 and 5-11, to \Inetpub\wwwroot and type http://localhost/converter.aspx in your browser s address bar. Then pick a currency, enter an amount in U.S. dollars, and click the Convert button to convert dollars to the currency of your choice.

Here are some points of interest regarding the source code:

  • Because it uses the DataSet class defined in the System.Data namespace, Converter.aspx begins with an @ Import directive importing System.Data.

  • Rather than show a hard-coded list of currency types in the list box, Converter.aspx reads them from Rates.xml. Page_Load reads the XML file and initializes the list box. To add new currency types to the application, simply add new Rate elements to Rates.xml. They ll automatically appear in the list box the next time the page is fetched.

  • For good measure, Converter.aspx wires the Convert button to the Click handler named OnConvert programmatically rather than declaratively. The wiring is done in Page_Init.

Notice how easily Converter.aspx reads XML from Rates.xml. It doesn t parse any XML; it simply calls ReadXml on a DataSet and provides an XML file name. ReadXml parses the file and initializes the DataSet with the file s contents. Each Rate element in the XML file becomes a row in the DataSet, and each row, in turn, contains fields named Currency and Exchange . Enumerating all the currency types is a simple matter of enumerating the DataSet s rows and reading each row s Currency field. Retrieving the exchange rate for a given currency is almost as easy. OnConvert uses DataTable.Select to query the DataSet for all rows matching the currency type. Then it reads the Exchange field from the row returned and converts it to a decimal value with Convert.ToDecimal.

One reason I decided to use a DataSet to read the XML file is that a simple change would enable the Web form to read currencies and exchange rates from a database. Were Converter.aspx to open the XML file and parse it using the FCL s XML classes, more substantial changes would be required to incorporate database input.

A word of caution regarding this Web form: Don t use it to perform real currency conversions! The exchange rates in Rates.xml were accurate when I wrote them, but they ll be outdated by the time you read this. Unless you devise an external mechanism for updating Rates.xml in real time, consider the output from Converter.aspx to be for educational purposes only.

Figure 5-9

Web form currency converter.

Converter.aspx

<%@ Import Namespace=System.Data %> <html> <body> <h1>Currency Converter</h1> <hr> <form runat="server"> Target Currency<br> <asp:ListBox  Width="256" RunAt="server" /><br> <br> Amount in U.S. Dollars<br> <asp:TextBox  Width="256" RunAt="server" /><br> <br>

 <asp:Button Text="Convert"  Width="256" RunAt="server" /><br> <br> <asp:Label  RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Init (Object sender, EventArgs e) { // Wire the Convert button to OnConvert ConvertButton.Click += new EventHandler (OnConvert); } void Page_Load (Object sender, EventArgs e) { // If this isn't a postback, initialize the ListBox if (!IsPostBack) { DataSet ds = new DataSet (); ds.ReadXml (Server.MapPath ("Rates.xml")); foreach (DataRow row in ds.Tables[0].Rows) Currencies.Items.Add (row["Currency"].ToString ()); Currencies.SelectedIndex = 0; } } void OnConvert (Object sender, EventArgs e) { // Perform the conversion and display the results try { decimal dollars = Convert.ToDecimal (USD.Text); DataSet ds = new DataSet (); ds.ReadXml (Server.MapPath ("Rates.xml")); DataRow[] rows = ds.Tables[0].Select ("Currency = '" + Currencies.SelectedItem.Text + "'"); decimal rate = Convert.ToDecimal (rows[0]["Exchange"]); decimal amount = dollars * rate; Output.Text = amount.ToString ("f2"); } catch (FormatException) { Output.Text = "Error"; } } </script>

Figure 5-10

Currency converter source code.

Rates.xml

<?xml version="1.0"?> <Rates> <Rate> <Currency>British Pound</Currency> <Exchange>0.698544</Exchange> </Rate>

 <Rate> <Currency>Canadian Dollar</Currency> <Exchange>1.57315</Exchange> </Rate> <Rate> <Currency>French Franc</Currency> <Exchange>7.32593</Exchange> </Rate> <Rate> <Currency>German Mark</Currency> <Exchange>2.18433</Exchange> </Rate> <Rate> <Currency>Italian Lira</Currency> <Exchange>2162.67</Exchange> </Rate> <Rate> <Currency>Japanese Yen</Currency> <Exchange>122.742</Exchange> </Rate> <Rate> <Currency>Mexican Peso</Currency> <Exchange>9.22841</Exchange> </Rate> <Rate> <Currency>Swiss Franc</Currency> <Exchange>1.64716</Exchange> </Rate> </Rates>

Figure 5-11

XML file used by Converter.aspx.



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net