ASP.NET Application Development

In conventional ASP programming, developers typically access the Request object to get the parameters needed to render the page and render the content of the page through either the Response object or code rendering blocks. We also use other ASP objects such as the Application, Session, and Server objects to manage application variables, session variables, server settings, and so on.

As mentioned earlier, ASP.NET is intended to change all this spaghetti madness by introducing a much cleaner approach to server-side scripting framework: Web Forms, or programmable pages, and server controls.

In the following sections, we cover the components of a Web Form, its life cycles, the server controls that the Web Form contains, event handing for these server controls, as well as how to create your own server controls.

Web Form Components

Similar to VB Forms, a Web Form consists of two components: the form with its controls and the code behind it that handles events associated with the form’s controls. A Web Form has the file extension .aspx and contains HTML elements, as well as server controls. The code behind the form is usually located in a separate class file. Note that while it is possible to have both the form and the code in one file, it is better to have separate files. This separation of user interface and application code helps improve the spaghetti-code symptom that most ASP-based applications are plagued with.

ASP.NET provides the Page class in the System.Web.UI namespace. This class encapsulates all common properties and methods associated with web pages. The code behind the class derives from this Page class to provide extensions specific to the page we’re implementing. The aspx file provides the form layout and control declarations. Figure 7-4 illustrates the relationship between the Page base class, the Web Form code behind the class, and the Web Form user interface (UI).

Web Form components

Figure 7-4. Web Form components

As a Web Form developer, you will have to provide the latter two. The Web Form UI is where you declare server controls with appropriate IDs. The code behind the class is where you programmatically access server controls declared in the Web Form UI, as well as handle events from these controls. The following simple example shows the aspx page, the code behind source file, and how they work together. The aspx file (TestEvent.aspx) contains only HTML tags and a directive that links to the code behind:

<%@ Page language="c#" codebehind="TestEvents.cs" inherits="CTestEvents" %>
<html>
  <head><title>Testing Page Events with codebehind</title></head>
  <body>
    <form runat=server>
      Init Time: <asp:Label id=labelInit runat=server/><br/>
      Load Time: <asp:Label id=labelLoad runat=server/><br/>
      <input type=submit />
    </form>
  </body>
</html>

The code-behind, TestEvent.cs, contains the class CTestEvents to which the aspx page is referring:

using System;

public class CTestEvents : System.Web.UI.Page {
  protected System.Web.UI.WebControls.Label labelInit;
  protected System.Web.UI.WebControls.Label labelLoad;

  public CTestEvents(  ) {
    labelInit = new System.Web.UI.WebControls.Label(  );
    labelLoad = new System.Web.UI.WebControls.Label(  );
  }

  public void Page_Init(Object oSender, EventArgs oEvent) {
    labelInit.Text = DateTime.Now.ToString(  );
  }

  public void Page_Load(Object oSender, EventArgs oEvent) {
    labelLoad.Text = DateTime.Now.ToString(  );
    if(IsPostBack) {
      labelLoad.Text += "(PostBack)";
    }
  }
}

You must compile TestEvent.cs and place the DLL in the /bin directory under your web application’s virtual directory before trying to access the aspx page.[44] The command to compile this C# file is:

csc /t:library TestEvents.cs

ASP.NET parses the Web Form files to generate a tree of scriptable objects, where the root is the Page-derived object representing the current Web Form. This is similar to how the IE browser parses the HTML file and generates a tree of scriptable objects to be used in DHTML; however, the tree of objects for the Web Form files resides on the server side.

As you are already aware from our survey of the System.Web.UI namespace, the Page class actually derives from the Control class. In a sense, a Web Form is a hierarchy of Control-derived objects. These objects establish the parent-child relationship through the Parent and Controls properties.

Besides the Controls and Parent properties, the Page class also provides other useful properties, which are familiar to ASP developers—such as the Request, Response, Application, Session, and Server properties.

Because the Web Form is nothing but a programmable page object, using this object-oriented model is much more intuitive and cleaner than the conventional ASP development. As opposed to the linear execution of server-side scripts on an ASP page, ASP.NET enables an event-based object-oriented programming model.

Let’s take an example of a web page that contains a form with numerous fields. One or more of these fields display list information from a database. Naturally, we have code in the ASP page to populate these fields so that when a user requests this ASP page, the generated page would have the content ready. As soon as the last line of data is written to the browser, the ASP page is done. This means that if there were errors when the user submits the form, we will have to repopulate all the database-driven form fields, as well as programmatically reselect values that the user chose prior to submitting the form. In ASP.NET, we don’t have to repopulate the database-driven fields if we know that the page has already been populated. Furthermore, selected values stay selected with no manual handlings. The next couple of sections describe the concept in more detail.

Web Form events

The Page class exposes events such as Init, Load, PreRender, and Unload. Your job as a developer is to handle these events and perform the appropriate task for each of these stages. This is much better than the linear execution model in ASP programming, because you don’t have to worry about the location of your initialization scripts.

The first event that happens in the life of a Web Form is the Init event. This is raised so that we can have initialization code for the page. The controls on the page are not yet created at this point. This event is raised once for each user of the page.

The Load event follows the Init event. Subsequently, it is raised each time the page is requested. When this event is raised, all child controls of the Web Form are loaded and accessible. You should be able to retrieve data and populate the controls so that they can render themselves on the page when sent back to the client.

The following example shows the how the Init and Load events can be handled in ASP.NET. In this example, we show both the HTML and its code together in one file to make it simpler:

<html>
  <head><title>Testing Page Events</title></head>
  <body>

    <script language="C#" runat=server>
      void Page_Init(Object oSender, EventArgs oEvent) {
        labelInit.Text = DateTime.Now.ToString(  );
      }

      void Page_Load(Object oSender, EventArgs oEvent) {
        labelLoad.Text = DateTime.Now.ToString(  );
        if(IsPostBack) {
          labelLoad.Text += "(PostBack)";
        }
      }
    </script>

    <form runat=server>
      Init Time: <asp:Label id=labelInit runat=server/><br/>
      Load Time: <asp:Label id=labelLoad runat=server/><br/>
      <input type=submit />
    </form>
  </body>
</html>

The first time you access this page, the Init event happens, followed by the Load event. Because these events happen rather quickly, both the Init Time and Load Time would probably show the same time. When you click on the submit button to cause the page to reload, you can see that the Init Time stays what it was, but the Load Time changes each time the page is reloaded.

The PreRender event happens just before the page is rendered and sent back to the client. We don’t often handle this event; however, it depends on the situation.

The last event in the life of a Web Form is the Unload event. This happens when the page is unloaded from memory. Final cleanup should be done here.

Beside these page-level events, controls on the page can also raise events such as ServerClick and ServerChange for HtmlControls, as well as Click, Command, CheckedChanged, SelectedIndexChanged, TextChanged events for WebControls. It is the handling of these events that makes ASP.NET truly dynamic and interactive.

Lifecycle of a Web Form

In ASP, the web page starts its life when a client requests a particular page. IIS parses and runs the scripts on the ASP page to render HTML content. As soon as the page rendering is complete, the page’s life ceases. If you have forms that pass data back to the ASP page to be processed, the ASP page runs as a new request, not knowing anything about its previous states. Passing data back to the original page for processing is also referred to as postback.

In ASP.NET, things are a little different. The page still starts at the client’s request; however, it stays around for as long as the client is still interacting with the page. For simplicity’s sake, we say that the page stays around, but in fact, only the view states of the page persist between requests to the page. These view states allow the controls on the server to appear as if they are still present to handle server events. We can detect this postback state of the page via the IsPostBack property of the Page object and forego certain costly reinitialization. The handling of events during these postbacks is what makes ASP.NET so much different than conventional ASP development.

In the following example, we extend the previous example to handle the postback. When the Load event is handled for the first time, we populate the drop-down list box with data. Subsequently, we indicate only the time the event is raised without reloading the data. This example also demonstrates the server event handler handleButtonClick that was bound to the ServerClick event of the button:

<html>
  <head><title>Testing Page Events</title></head>
  <body>

    <script language="C#" runat=server>
      void Page_Init(Object oSender, EventArgs oEvent) {
        labelInit.Text = DateTime.Now.ToString(  );
      }

      void Page_Load(Object oSender, EventArgs oEvent) {
        labelLoad.Text = DateTime.Now.ToString(  );
        if(!IsPostBack) {
          selectCtrl.Items.Add("Acura");
          selectCtrl.Items.Add("BMW");
          selectCtrl.Items.Add("Cadillac");
          selectCtrl.Items.Add("Mercedes");
          selectCtrl.Items.Add("Porche");
        } else {
          labelLoad.Text += " (Postback)"; 
        }
      }

      void handleButtonClick(Object oSender, EventArgs oEvent) {
        labelOutput.Text = "You've selected: " + selectCtrl.Value;
        labelEvent.Text = DateTime.Now.ToString(  );
      }
    </script>

    <form runat=server>
      Init Time: <asp:Label id=labelInit runat=server/><br/>
      Load Time: <asp:Label id=labelLoad runat=server/><br/>
      Event Time: <asp:Label id=labelEvent runat=server/><br/>
      Choice: <select id=selectCtrl runat=server></select><br/>
      <asp:Label id=labelOutput runat=server/><br/>
      <input type=button value=update 
             OnServerClick="handleButtonClick" runat=server />
    </form>

  </body>
</html>

The life cycle of a Web Form consists of three main stages: Configuration, Event Handling, and Termination. As mentioned earlier, these stages span across many requests to the same page, as opposed to the serving-one-page-at-a-time policy found in ASP.

Configuration

In the Configuration stage, the page’s Load event is raised. It is your job to handle this event to set up your page. Because the Load event is raised when all the controls are already up and ready, your job is now to read and update control properties as part of setting up the page. In the previous code example, we handled the Load event to populate the drop-down list with some data. We also updated the labelLoad control’s Text to display the time the Load event happens. In your application, you will probably load the data from a database and initialize form fields with default values.

The page’s IsPostBack property indicates whether this is the first time the page is loaded or if it is a postback. For example, if you have a control that contains a list of information, you will only want to load this control the first time the page is loaded by checking the IsPostBack property of the page. When IsPostBack is true, you know that the list control object is already loaded with information. There is no need to repopulate the list. In the previous code example, we skipped over the population of the drop-down and just displayed a string "(postback)".

You might need to perform data binding and re-evaluate data-binding expressions on the first and subsequent round trips to this page.

Events Handling

In this middle stage, the page’s server event-handling functions are being called as the result of some events being triggered from the client side. These events are from the controls you’ve placed on the Web Form. Figure 7-5 depicts the life cycle of an event.

The Web Form event life cycle

Figure 7-5. The Web Form event life cycle

Termination

At this stage, the page has finished rendering and is ready to be discarded. You are responsible for cleaning up file handles, releasing database connections, and freeing objects. Even though you can rely on the CLR to perform garbage collection for you, we strongly advise you to clean up after yourself because garbage collection only happens periodically. On heavily loaded systems, if the garbage-collection cycle is not optimal, the unfreed resources can exhaust memory and bring your system to a halt.

We can perform the clean up for the previous example with the Unload event handler as shown here. Because there is nothing to clean up in this simple example, we just show you the function as a template:

 void Page_Unload(Object oSender, EventArgs oEvent) {
   // cleaning up code here
 }

Server Controls

As we saw from the System.Web.UI.HtmlControls and System.Web.UI. WebControls namespaces, server controls are programmable controls that run on the server before the page is rendered by ASP.NET. They manage their own states between requests to the same page on the server by inserting a hidden field storing the view state of the form. This eliminates the need to repopulate the value of form fields with the posted value before sending the page back the client.

Server controls are also browser independent. Because they are run on the server side, they can rely on the Request.Browser property to get the client’s capability and render appropriate HTML.

Since the server controls are just instantiations of .NET classes, programming the server controls yields easy-to-maintain code. Especially when you have custom server controls that encapsulate other controls, web application programming becomes nothing more than gluing these blocks together.

All HTML controls and web controls mentioned in System.Web.UI.HtmlControls and System.Web.UI.WebControls are server controls shipped with ASP.NET.

Custom Server Controls

As you become more familiar with the ASP.NET framework and the use of server controls on your Web Form, you will eventually need to know how to develop these server controls yourself. In ASP.NET, there are two ways of creating custom server controls: the pagelet approach, which is easy to do but rather limited in functionality, and the Control base class (or UserControl) derivative approach, which is more complicated but also more powerful and flexible.

Pagelets

Until recently, code reuse in ASP development has been in the form of server-side includes. If you have common UI blocks or scripts, you can factor them into an include file. Use the syntax <!-- #include file=" url " --> to include the common file into the main page to return to the browser. This approach is fine, but it has serious limitations. The main thing is to make sure the HTML tag IDs and script variable names are unique. This is because IIS does nothing more than merge the include file when it parses server-side includes. The include file ends up being in the same scope with the container file. You cannot include the same file twice because there will be tag ID and script conflicts.

With ASP.NET, you can factor out common HTML and scripts into what is currently called a pagelet and reuse it without worrying about the ID conflicts. A pagelet is a Web Form, without a body or a form tag, that is accompanied by scripts. The HTML portion of the pagelet is responsible for the layout and the user interface, while the scripts provide the pagelet with programmability by exposing properties and methods. Because the pagelet is considered a user control, it provides an independent scope. You can insert more than one instance of the user control without any problem.

The container Web Form must register the pagelet as a user control using the @Register directive and then include it on the page with the < prefix : tagname > syntax. If more than one copy of the pagelet is used in a container page, each of them should be given different IDs for the container page’s script to work correctly. The script on the container Web Form can access and manipulate the pagelet the same way it does any other server controls.

The following example shows how an address form is reused as a pagelet. You might display this address form to allow the web user to register with your application or to display the shipping and billing addresses when the web user checks out:

<table>
  <tr>
    <td><asp:Label id=labelName runat="server">Name</asp:Label></td>
    <td><asp:TextBox id=txtUserName runat="server" 
         Width="332" Height="24"></asp:TextBox></td>
    </tr>
  <tr>
    <td><asp:Label id=labelAddr1 runat="server">Address</asp:Label></td>
    <td><asp:TextBox id=txtAddr1 runat="server" 
         Width="332" Height="24"></asp:TextBox></td>
    </tr>
  <tr>
    <td><asp:Label id=labelAddr2 runat="server"></asp:Label></td>
    <td><asp:TextBox id=txtAddr2 runat="server" 
         Width="332" Height="24"></asp:TextBox></td>
    </tr>
  <tr>
    <td><asp:Label id=labelCity runat="server">City</asp:Label></td>
    <td>
    <asp:TextBox id=txtCity runat="server"></asp:TextBox>
    <asp:Label id=labelState runat="server">State</asp:Label>
    <asp:TextBox id=txtState runat="server" Width="34" Height="24">
      </asp:TextBox>
    <asp:Label id=labelZIP runat="server">ZIP</asp:Label>
    <asp:TextBox id=txtZIP runat="server" Width="60" Height="24">
      </asp:TextBox>
    </td>
    </tr>
  <tr>
    <td><asp:Label id=labelEmail runat="server">Email</asp:Label></td>
    <td><asp:TextBox id=txtEmail runat="server" 
         Width="332" Height="24"></asp:TextBox></td>
    </tr>
</table>

<script language="C#" runat="server" ID=Script1>
  public String UserName {
    get { return txtUserName.Text; }
    set { txtUserName.Text = value; }
  }
  public String Address1 {
    get { return txtAddr1.Text; }
    set { txtAddr1.Text = value; }
  }
  public String Address2 {
    get { return txtAddr2.Text; }
    set { txtAddr2.Text = value; }
  }
  public String City {
    get { return txtCity.Text; }
    set { txtCity.Text = value; }
  }
  public String State {
    get { return txtState.Text; }
    set { txtState.Text = value; }
  }
  public String ZIP {
    get { return txtZIP.Text; }
    set { txtZIP.Text = value; }
  }
</script>

To use your pagelet, register it as a server control via the @Register directive, as shown in the next block of code. After registering, include the tag for the pagelet as if it was a normal server control. Specify the prefix, the tag name, the server control’s ID, and set the runat property to server:

<%@ Register TagPrefix="Acme" TagName="Address" Src="Address.ascx" %>
<%@ Page language="c#"%>
<html>
<head>
  <script language="C#" runat=server>
    void Page_Load(Object oSender, EventArgs evt) {
      addr.UserName = "Jack Daniel";
    }
  </script>
</head>
<body>
    Welcome to the E-Shop.  
    Registering with E-Shop will allow for monthly updates of bargains...
  <form method="post" runat="server">
    <p><Acme:Address id=addr runat="server"></Acme:Address></p>
    <p><asp:Button id=cmdClear runat="server" Text="Clear"></asp:Button>
       <asp:Button id=cmdSubmit runat="server" Text="Submit">
       </asp:Button></p>
  </form>
</body>
</html>

You should be able to programmatically access the properties of the pagelet through the server control’s ID, which is addr in this case. In the previous example, we accessed the UserName property of the Address pagelet via its ID:

addr.UserName = "Jack Daniel";

For an e-commerce checkout page, you could have two instances of <Acme:Address> on the same page: one for the billing and the other for the shipping address. Your script should access these instances of the pagelet via the ID you assign to each address control.

You can also programmatically instantiate instances of the pagelet through the use of the Page’s LoadControl method. The first thing is to declare a variable of type Control in your script to host your pagelet. This is because the Control is the root of all objects, including your pagelet. Then instantiate the variable with a call to the LoadControl, passing in the filename of the control page. To make the control visible on the page, add the control to the Page’s collection of controls. Because you currently have an instance of the Control object, you won’t be able to call the pagelet’s properties and methods until you cast the variable from Control type to your pagelet type. This is similar to having an Object variable in Visual Basic to hold a COM component. To access the COM-component methods and properties, you would cast the Object variable to the component type. As currently implemented, pagelets when loaded are automatically typed as pagename_extension. For example, if your pagelet were named myControl.ascx, the type generated for it would be myControl_ascx. The boldface line in the following example shows you how to cast addr1 from Control to type Address_ascx in order for you to access the UserName property of the pagelet:

<%@ Register TagPrefix="Acme" TagName="Address" Src="Address.ascx" %>
<%@ Page language="C#" %>
<html>
<head>
  <script language="C#" runat=server>
    void Page_Load(Object oSender, EventArgs evt) {
      addr.UserName = "Jack Daniel";
      Control addr1;
      addr1 = LoadControl("Address.ascx");      ((Address_ascx)addr1).UserName = addr.UserName;
      this.frm.Controls.AddAt(3, addr1);
    }
  </script>
</head>
<body>
  <form id=frm method="post" runat="server">
    Billing Address:<br/>
    <Acme:Address id=addr runat="server"></Acme:Address>
    Shipping Address:<br/>
    <p><asp:Button id=cmdClear runat="server" Text="Clear"></asp:Button>
       <asp:Button id=cmdSubmit runat="server" Text="Submit"></asp:Button></p>
  </form>
</body>
</html>

This example, the checkout page, shows you how to declare a pagelet statically in your page with the <Acme:Address> tag, as well as how to dynamically create an instance of the custom control Address with the Page’s LoadControl( ) method. Once you’ve created the control dynamically, you must cast the object to the control type before manipulating it.

The AddAt( ) method is used to insert the Address pagelet at a particular location in the checkout page. Instead of declaring the dynamic pagelet as a Control, you can also declare it as its type, which is Address_ascx. This way, you just have to cast it once when loading the dynamic control:

Address_ascx addr2 = (Address_ascx)LoadControl("Address.ascx");
addr2.UserName = "ABC";

Control derivatives

While it is easy to create custom controls using the pagelet approach, this technique is not flexible enough to create more powerful custom controls, such as ones that expose events or hierarchy of controls. With ASP.NET, you can also create custom controls by inheriting from the Control base class and overriding a couple of methods.

The following example shows you how to create the simplest custom control as a Control derivative.

namespace MyWebControls
{
  using System;
  using System.Web.UI;
  using System.Web.UI.WebControls;
  using System.ComponentModel;

  public class MyWebControl : System.Web.UI.WebControls.WebControl
  {
    //protected override void Render(HtmlTextWriter output)
    //{
    //    output.Write("custom control testing via Render(  )");
    //}

    protected override void CreateChildControls(  ) 
    {
      Table tbl = new Table(  );
      TableRow row = new TableRow(  );
      TableCell cell = new TableCell(  );
      HyperLink a = new HyperLink(  );
      a.NavigateUrl = "http://msdn.microsoft.com";
      a.ImageUrl = "image url";
      cell.Controls.Add (a);
      row.Cells.Add(cell);
      tbl.Rows.Add(row);

      row = new TableRow(  );
      cell = new TableCell(  );
      cell.Controls.Add (new LiteralControl("custom control testing"));
      row.Cells.Add(cell);
      tbl.Rows.Add(row);

      tbl.BorderWidth = 1;
      tbl.BorderStyle = BorderStyle.Ridge;

      Controls.Add(tbl);
    }
  }
}

As you can see, the MyWebControl object derives from the WebControl class. We have seen that WebControl ultimately derives from the base Control class. All we really do here is override either the Render or the CreateChildControls methods to construct the custom web control. If you choose to override the Render method, you will have to generate the HTML for your custom control through the HtmlTextWriter object, output . You can use methods such as Write, WriteBeginTag, WriteAttribute, and WriteEndTag.

In our example, we override the CreateChildControls method. Instead of worrying about the actual HTML tag and attribute names, we create ASP.NET objects directly by their class names, such as Table, TableRow, TableCell, HyperLink, and LiteralControl, to construct a hierarchy of objects under a table . We can also manipulate attributes for the objects via their properties. At the end of the method, we add the table to the custom control’s collection of controls.

You will have to compile the previous control code to generate a DLL assembly. To use the control, just deploy the assembly by copying it to the /bin directory of your web application. Then you should be able to register the control with the @Register directive and use the control as if it was a server control provided by ASP.NET. If you are using Visual Studio.NET, you can add a reference to the control assembly file or the control project for the test web project that uses the control.

Your custom-control test page should now look like the following:

<%@ Page language="c#"%>
<%@ Register TagPrefix="WC" Namespace="MyWebControls" 
             Assembly="MyWebControls%>
<html>
<head>
  <script language="C#" runat=server>
    void Page_Load(object sender, EventArgs e) {
      MyWebControls.MyWebControl myCtrl;
      myCtrl = new MyWebControls.MyWebControl(  );
      this.Controls.Add(myCtrl);
    }
  </script>
</head>
<body>
  <form method="post" runat="server">
    This is the main page
    <WC:MyWebControl id=myControl1 runat="server" />
  </form>
</body>
</html>

As you can see, we register the custom control with the @Register directive and alias the namespace MyWebControls with the WC prefix. In the body of the Web Form, we can add the custom-control tag as <WC:MyWebControl>.

In addition to inserting the custom control onto the page declaratively as shown earlier, we can also programmatically create the custom control at runtime. The Page_Load code demonstrates this point:

 MyWebControls.MyWebControl myCtrl;
 myCtrl = new MyWebControls.MyWebControl(  );
 this.Controls.Add(myCtrl);

The output page is shown in Figure 7-6.

Custom control test output, statically and dynamically

Figure 7-6. Custom control test output, statically and dynamically

Event-Driven Programming

There are two ways to associate event handlers—functions that handle the event—to the UI controls.

Refer to the earlier section on Section 7.4 particularly where we describe the syntax for server controls. All we do to bind an event from a control to an event handler is to use the eventname = eventhandlername attribute/value pair for the control. For example, if we want to handle the onclick event for the HTML control input, all we do is the following. Note that for the HTML controls, the server-side click event is named onserverclick, as opposed to the client-side click event onclick, which can still be used in DHTML scripting:

<input id="cmd1" runat="server"onserverclick="OnClickHandler"
  type="button" value="click me">

For an ASP.NET web control, the syntax is the same:

<asp:Button id="cmd2" runat="server"onclick="OnclickHandler2"
  Text="click me too"></asp:Button>

After binding the event to the event-handling function name, we have to provide the actual event handler:

void OnClickHandler(object sender, EventArgs e)
{
  // code to retrieve and process the posted data
}

The second way of binding events is delegation. You don’t have to have any notion of code in the aspx file, not even the event-handling function name. All you have to do is to register the event handler with the control’s event-handler property. For web controls, the event handler property for button click is Click. For HTML controls, it’s ServerClick:

ControlID.Click += new System.EventHandler (this.EventHandlerName);

ControlID.ServerClick += new System.EventHandler (this.EventHandlerName);


[44] The Web Application directory is the root virtual directory where your web application resides. To set up the virtual directory, use the IIS Administration Tool.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.162.252