Similar to Active Server Pages, Web Forms are text files consisting of HTML tags and other controlling tags such as directives and script blocks. The default extension for web forms is aspx; however, you can use the IIS administration tool to map other file extensions explicitly with aspnet_isapi.dll to have them parsed and compiled when accessed, as if they were ASP.NET resources.
There are 10 different syntax elements in ASP.NET; because most of them are carried over from ASP, we list here the familiar ones and discuss only those that are important in ASP.NET:
Previously, all ASP directives were formatted as <%@ [attribute=value]+ %> because there was only one type of directive.[3]
[3] As noted in the Preface, the plus sign indicates one or more instances of the preceding term in brackets.
ASP.NET adds a number of directives to ASP.NET files. With the new list of directivesPage, Control, Import, Implements, Register, Assembly, OutputCache, and Referencethe syntax for directive is now <%@ directive [attribute=value]+ %>. All of the old ASP directives are attributes under the Page directive. If you use the old syntax by omitting the directive name, the attribute/value pairs will be applied to the default directive, which is Page.
In addition to containing all previous ASP directives (CodePage, EnableSessionState, Language, LCID, and Transaction), the ASP.NET Page directive also supports the important attributes ErrorPage, Inherits, Src, and EnableViewState, which we will make use of in this chapter. The complete list of all attributes for the Page directive can be found in the .NET Framework Developers' Guide:
<@ Page Language="VB" ErrorPage="URL" EnableViewState="true">
Similar to the way the Page directive is used for an ASP.NET page (an .aspx file), the Control directive is used for an ASP.NET control (an .ascx file). (We get into developing ASP.NET controls in Section 7.5.4 later in this chapter.)
We can use the Import directive to add namespace references to the current page. Your code can access all classes and interfaces of imported namespaces. For example, if you want to use ADO.NET, you would include the following code:
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.OleDb" %>
A number of namespaces are automatically imported into all ASP.NET pages to simplify the developers' task:
Because an aspx file is basically a Page class derivative, it too can also implement an interface. The @ Implements directive is used to declare that the aspx implements the specified interface. For example, the following line declares that the page implements the IPostBackEventHandler interface:
<%@ Implements Interface="System.Web.UI.IPostBackEventHandler" %>
This directive registers custom server controls for use in the current page by specifying the aliases to be used as prefixes for class names. It is used in conjunction with the custom server-control elements to provide a concise way of specifying server-control names. The following line of code registers a custom control to be used in this page:
<%@ Register Tagprefix="Ch07" TagName="MyCustomControl Src="MyCustomControl.ascx" %>
The name of the control is MyCustomControl; the prefix used when declaring the control is Ch07; the source for the control is in MyCustomControl.ascx. (We demonstrate this when we develop our ASP.NET controls later in this chapter.)
If the server custom control was not done through .ascx but instead, through inheriting UserControl, the syntax for the Register directive is the following:
<%@ Register Tagprefix="MyCustomCtrl" Namespace="MyNamespace" Assembly="MyDll.dll" %>
The Assembly directive specifies the assembly to which the current page belongs. This effectively makes all the classes and interfaces belonging to the assembly accessible to the current page. For example, the following line of code specifies that the current page belong to the Ch07 assembly:
<%@ Assembly Name="Ch07" %>
This means that code in this page can access anything in the Ch07 assembly as long as the Ch07 assembly is compiled and placed in the bin subdirectory of this web application.
You can use the OutputCache directive to control the output-caching duration for the current page. This is similar to setting up the expiration for the response object in ASP programming. The Duration attribute of the OutputCache directive defines the time in seconds until the page expires.
The @ Reference directive is used to add a reference to a page or a control to this aspx page.
As in ASP, code declaration blocks define the code to be parsed and run for the page. In these blocks, the runat attribute specifies whether the code block is client-side or server-side code. For server-side programming, set this attribute to server. If you ignore the runat attribute, IIS will interpret the code block as client-side code, which is used for Dynamic HTML (DHTML).
<script runat="server" [language="codelanguage"]> Code </script>
For both client- and server-side code declaration blocks, you can also use the src attribute to point to an external source file containing the code. This helps separate the code from the HTML content of the page. The value for src can be a relative path or a URL to a source file. The URL can be on the same or a different web server:
<script runat="server" [language="codelanguage"] [src="externalfilename"] />
There are no changes to this syntax versus that in ASP. Inline code or inline expressions specified in these code-rendering blocks are executed when the page is rendered. All these blocks are enclosed between the tags <% and %>. The language used in these tags is specified in the language attribute of the Page directive.
HTML controls are very similar to standard HTML elements, with the exception of the id and the runat attributes. If you've developed web applications with DHTML, you should be familiar with the id attribute of an HTML element and how to programmatically reference the client-side control representing the HTML element. The difference in this case is that the control is not on the client side but on the server side. For example, the following code represents an HTML server button control:
<input id="cmd1" runat="server" type="button" value="Click Me" />
All HTML server controls must be inside a <form runat="server"> control because Web Forms use the POST method to maintain the controls' states.
When encountering an HTML element tagged with id and the runat attribute set to server, ASP.NET creates the appropriate scriptable server HtmlControl object. For example, the previous HTML snippet generates a server HtmlControl of type HtmlInputButton that has an id of cmd1.
You can bind an event handler to this control's event to handle notification from this control, such as the onclick event. There are two ways to bind an event handler to a control's event, the declarative way and the programmatic way. The declarative is done inside the HTML element tag as an attribute/value pair. The attribute is the name of the event, and the value is the name of the event-handling function. For example, to handle the onclick event, add this to the previous HTML tag:
onserverclick="handleServerClick"
The programmatic way to bind an event to the handler involves a line of code that assigns a delegate to the event property of the control. In C#, the code to bind the ServerClick event of the button to the event handler handleServerClick is:
cmd1.ServerClick += new System.EventHandler(handleServerClick);
If you've used client-side DHTML in your web applications, event binding should be nothing new to you, except for some subtle differences. The first difference is obvious: the event handler runs on the server before the page is sent back to the browser, instead of running on the client side. The other difference is that all event-handler functions for server-side must have two parameters: Sender and Event. The Sender parameter is of type object, indicating the source element that caused the event to happen; the Event parameter is of type EventArgs, which is the actual event fired. In DHTML scripting, we would inspect the window.event object to find out which element was the source of the event and other event information.
Similar to HTML Server Controls, custom controls also have id and runat attributes; however, custom controls are not standard HTML elements. To insert a custom control into a page, use the following syntax:
<tagprefix:tagname id="controlID" runat="server" eventname= "eventHandler" />
Notice that all custom controls' tags have a tag prefix, which is an alias to the namespace in which the control is defined. See the Register directive earlier in this chapter for information on registering namespaces' aliases. Binding events to their handlers for custom controls is the same as for HTML controls. Even though we show the two ways of binding events, it is preferable to bind events using the second method because it cleanly separates the HTML tags from the code behind the screen.
All web controls mentioned in the WebControls namespace can be inserted in the same manner (these controls have the prefix asp). For example, you can have the following tags in your aspx page:
<asp:TextBox id=txt1 runat=server></asp:TextBox> <asp:Button id=cmd1 runat=server Text="Web Button"></asp:Button> <asp:Label id=label1 runat=server></asp:Label>
These tags result in three objects generated from the three classes: TextBox, Button, and Label, from the System.Web.UI.WebControls namespace. In your server script, you can access and manipulate these objects to render your page appropriately.
Data-binding expressions bind the server controls with some data sources. The syntax to bind data is:
<%# data-binding-expression %>
Examine the following block of code to see the simplest data binding:
<asp:Label text='<%# TestData %>' runat=server/>
The data-binding expression here indicates that the label's text content is bound to a publicly defined property, TestData, of the Web Form. This means that when data binding occurs for the form, <%# TestData %> will be replaced by the content of the TestData property. Let's define this property for the Web Form:
public string TestData = "Hello World";
The Web Forms page framework does not perform data binding automatically. The developers must explicitly call the DataBind( ) method to activate the evaluation of the data-binding expression and perform the substitution. We can call the page's DataBind method upon the page-load event or whenever we change the TestData property and want it reflected on the page. This example calls DataBind( ) upon page load to bind the Label's text to the TestData variable:
<html> <head><title>Data Binding Sample</title></head> <body> <script language="C#" runat=server> /* Declare the variable we want to bind to. */ public string TestData; void Page_Load(Object oSender, EventArgs oEvent) { TestData = "Hello World!\n"; Page.DataBind( ); } </script> <asp:Label text='<%# TestData %>' runat=server/> </body> </html>
Let's try something a little more complicated. In the next block of tags, we have three labels bound to three different properties of an object called currStudent:
Name: <asp:Label text='<%# currStudent.FirstName %>' runat=server/> <asp:Label text='<%# currStudent.LastName %>' runat=server/> <br/> SSN: <asp:Label text='<%# currStudent.SSN %>' runat=server/>
The currStudent object is a publicly accessible property of the current page:
<script language="C#" runat=server> public class CStudent { /* Declare the variable we want to bind to. */ public string FirstName; public string LastName; public string SSN; } public CStudent currStudent; void Page_Load(Object oSender, EventArgs oEvent) { currStudent = new CStudent( ); currStudent.FirstName = "Jack"; currStudent.LastName = "Daniel"; currStudent.SSN = "123-45-6789"; Page.DataBind( ); } </script>
You can have this currStudent object filled with data coming from any source then perform a DataBind call to update the page with the current student's information. The assumption here, of course, is that the Student class provides the previously mentioned properties.
Server-side object tags statically declare and instantiate COM and .NET objects. The syntax to declare server-side objects in global.asax is:
<object id="id" runat="server" scope="scope" class=".NET class name"> <object id="id" runat="server" scope="scope" progid="COM ProgID"> <object id="id" runat="server" scope="scope" classid="COM classID">
Scope can be pipeline, application, or session, which means the object is available on the page, as an application variable, or as a session variable, respectively. To dynamically add a server-side object to the page, you would use the Page.LoadControl( ) method or just instantiate the control directly.
Server-side includes server-side comments and literal text which are exactly the same as in ASP. Therefore, we will not go over them here.