Working with User Controls in Web Forms Applications





Working with User Controls in Web Forms Applications

When you're interested in reusing a piece of user-interface functionality in an ASP.NET application, but you don't want to expose yourself to the full brunt of a precompiled server control, you can create a user control. User controls are simple to build in ASP.NET script and don't require the precompilation that server controls do.

To create a user control, you typically start with a basic HTML representation of the user interface you want. This is a convenient feature of user controls. It means you can use whatever HTML editing control you're most comfortable with to do the lion's share of control development.

For example, suppose you're building an information portal application in which search functionality is a key feature. You want to have a Search dialog box on every page of your application. To implement this, you can create a Search dialog box user control. The HTML representation of the control is shown in Listing 9.1.

Listing 9.1 Initial HTML Representation of the Search Dialog Box User Control
<table width="250" border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td bgcolor="#000066"><font color="#FFFFFF">Search</font></td>
  </tr>
  <tr>
  <td align="center" bgcolor="#CCCCCC" height='75px'>
    <table width="98%" border="0">
        <tr>
          <td>Text:</td>
          <td>
            <input type="text" name="textfield">
          </td>
          <td>
            <input type="submit" name="Submit" value="Go">
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>

You should notice a few important things about the HTML representation of this user control. First, notice that the HTML header tags (things such as <HTML>, <HEAD>, and <BODY>) are missing. This is the case because this control is intended to be the component of an existing page, not a page that stands alone. You should not include these tags when building a user control.

Next, notice that although this HTML contains form elements such as a text box and button, it does not contain an actual <FORM> tag. This is because the containing page is where the form tag should reside; form tags don't belong in user controls themselves.

Finally, user controls are always saved using the .ascx file extension. Giving your controls an .ascx extension easily identifies the code as a user control and prevents the Web server from serving up the file. You can verify this by attempting to navigate to a file with an .ascx extension—the server will refuse to send it directly in the browser. Rather than navigating to them directly, user controls will be rendered only in the context of a hosting page, as we'll demonstrate later in this chapter.

A Search dialog box such as this one could be useful in many Web applications. If you were to use this bit of HTML again and again in different places, you would want to have some high-level programmatic control over its behavior. For example, you would definitely want to be able to specify the text displayed in the title bar and the Search prompt, as well as specify other cosmetic aspects such as the background color of the title bar and body of the control.

You can provide programmers who use your control the capability to change these values programmatically by exposing them as user-control properties. We'll describe how that works in the next section.

In addition to saving the control as a file with an .ascx extension, your user control can contain an additional element—a Control directive. This directive is similar to the Page directive that appears at the start of ASP.NET pages, but it contains special settings that are relevant to user controls.

The Control directive supports a subset of the attributes supported by the Page directive (described in Chapter 2, "Page Framework"). The attributes supported by the Control directive are the following:

  • AutoEventWireup

  • ClassName

  • CompilerOptions

  • Debug

  • Description

  • EnableViewState

  • Explicit

  • Inherits

  • Language

  • Src

  • Strict

  • WarningLevel

You use these settings nearly identically to the way you use them for pages; see Chapter 2 for more information on what they do and how they work.

The Inherits and Src attributes refer to code-behind functionality for the control itself. This enables you to separate code functionality from a user control the same way you do for a page. Code behind is also discussed in Chapter 2.

You'll notice that the Trace attribute is missing from the list of attributes found on the list of Control directives. The Trace attribute is not a part of the Control directive because trace mode functionality is controlled by the entire page and cannot be limited to a specific user control. For more information on tracing, see Chapter 3, "Debugging ASP.NET Applications."

Adding Properties to a User Control

You can give developers the capability to programmatically change elements of your user control by exposing certain elements of the control as public properties. By doing this, you enable customization of the control at a high level, without forcing developers to manually hack the HTML that composes your control.

To do this, you replace hard-coded HTML elements in your control with references to public variables or (preferably) property procedures. Listing 9.2 shows an example of the Search dialog box created in the previous section, this time with a property procedure for a TitleBarText property.

Listing 9.2 Search Dialog Box with a TitleBarText Property
<SCRIPT runat='server'>
  private String strTitleBarText;

  public String TitleBarText
  {
    get {
      return strTitleBarText;
    }

    set {
    strTitleBarText = value;
    }
  }

</SCRIPT>
<table width="250" border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td bgcolor="#000066"><font color="#FFFFFF"><% =TitleBarText %></font></td>
  </tr>
  <tr>
    <td align="center" bgcolor="#CCCCCC" height='75px'>
      <table width="98%" border="0">
        <tr>
          <td>Text:</td>
          <td>
            <input type="text" name="textfield">
          </td>
          <td>
            <input type="submit" name="Submit" value="Go">
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>

The three things that changed in this version of the control are the inclusion of a private variable, strTitleBarText, to store the value of the title bar text, a property accessor function called TitleBarText, and a render block (shaded) in the middle of the HTML representation of the control.

When you include this code in the control, the HTML representation of the control will be different, depending on how the programmer of the containing page chooses to set these properties. For an example of the finished product, see the next section, "Programming a User Control in a Page."

It's common for controls such as this Search control to incorporate other properties to alter the appearance and behavior of the control. Your controls will probably contain properties for foreground and background color, height and width, and so on. These properties are very easy to implement in user controls with the techniques demonstrated here.

Programming a User Control in a Page

To use a user control in an ASP.NET Web forms page, you must first include a Register directive at the top of the page and then create a tag that represents an instance of that control, as you do with the other intrinsic ASP.NET Web forms controls.

Listing 9.3 gives an example of a typical Register directive for the Search user control we created in the previous section.

Listing 9.3 Example of a Minimal Register Directive Referencing a User Control in a Page
<%@ Register TagPrefix="MyControl"
             TagName="Search"
             Src="search.ascx" %>

Three attributes are required in the Register directive. The TagPrefix attribute gives your control a unique namespace so that its name doesn't collide with other controls that may have similar names. This prefix comes in handy when you need to use two types of controls, written by different authors but both coincidentally named Search, on the same page. However, rather than using the generic tag prefix MyControl, you should use a tag prefix that identifies yourself or your company. That way, a namespace collision between your control and someone else's will be less likely.

The TagName attribute provides a name that identifies your control class. Note that this is different from the name of the instance of the control you use to manipulate the control programmatically.

Finally, the Src attribute indicates where the source file (.ascx) of your user control is stored. This file does not have to be in the same directory as the page; it can be in a subdirectory.

After you've registered your control for use in a page, you can create an instance of the control using the same tag-based syntax you use for any other kind of Web form control in ASP.NET. For example, if the Register directive for your Search control looks like the previous example (in Listing 9.3), a tag that would create an instance of the control on an ASP.NET Web form looks like this:

<MyControl:Search id='Search1' runat='server' />

As with all Web form controls, you should ensure that your control tags are placed inside an ASP.NET form (that is, a FORM tag that contains the runat="server" attribute). If you don't do this, your page won't work. Also, as with other types of Web forms controls, you can assign default properties to the control two ways: in the tag itself or in the Page_Load event procedure (or both).

For example, suppose you're using the Search user control in an application that enables users to search an online personnel database. In this case, you might want to set certain properties of the user control in code to reflect the purpose of the search. Listing 9.4 provides an example.

Listing 9.4 Example of a Custom User Control Utilized in an ASP.NET Web Form
<%@ PAGE language='C#' debug='true' trace='false' %>
<%@ REGISTER TagPrefix='MyControl' TagName='Search' Src='search.ascx' %>
<html>
  <head>
    <title>ASP.NET Page </title>
  </head>
  <script runat='server'>
    void Page_Load(Object Sender, EventArgs e)
    {
      Search1.TitleBarText = "Personnel Search";
    }
  </script>
  <body>
    <form runat='server'>
      <MyControl:Search id='Search1' runat='server' />
    </form>
  </body>
</html>

In this example, the TitleBarText property is set in the page's Page_Load event procedure. But it could just as well have been set in the declaration tag for the control itself, like so:

<MyControl:Search id='Search1'
                  TitleBarText='PersonnelSearch'
                  runat='server' />

There's really no difference between the two techniques. Which one you use depends on your preference and whether the property setting is known when you author the page. If the property setting isn't known (that is, the property is derived from a calculation), you'll need to assign it in code.

Adding Methods to Your User Control

You can add methods to your user control the same way you add properties to controls. To add a method, you simply create a public function or subroutine in the SCRIPT tag contained in your user control.

For example, suppose you want the page programmer to be able to show an Advanced Search dialog box. To enable this, you might provide a ShowAdvanced method in your Search control. Listing 9.5 shows an example of a new version of the Search control that provides this functionality.

Listing 9.5 Search Control with ShowAdvanced Method to Provide Extended Features
<SCRIPT runat='server'>
  private String strTitleBarText;

  public String TitleBarText
  {
    get
    {
      return strTitleBarText;
    }

    set
    {
      strTitleBarText = value;
    }
  }

  public void ShowAdvanced()
  {
    // Public (control method)
    Advanced.Visible = true;
  }

</SCRIPT>
<table width="250" border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td bgcolor="#000066"><font color="#FFFFFF">
        <% =TitleBarText %>
      </font>
    </td>
  </tr>
  <tr>
    <td align="center" bgcolor="#CCCCCC" height='75px'>
      <table width="98%" border="0">
        <tr>
          <td>Text:</td>
          <td>
            <input type="text" name="textfield">
          </td>
          <td align="center">
            <input type="submit" name="Submit" value="Go">
          </td>
        </tr>
      </table>
    </td>
  </tr>
  <tr id='Advanced' visible='False' runat='server'>
    <td align="center" bgcolor="#CCCCCC" height='40'>
      <asp:checkbox id="ShowContractors" value="1" runat="server" />
      Show contractors</td>
  </tr>
</table>

The Advanced Search functionality is provided through an HTML server control, a table row control called Advanced. The Visible property of this control is set to false by default, but calling the ShowAdvanced method of the control sets it to true. In addition to displaying the row, all the controls contained in the row are automatically displayed as well. You could include code in ShowAdvanced to do other interesting work, such as setting defaults for the Advanced Search controls and so forth.

To call this method, the page programmer calls the Show method in the hosting page:

<script runat='server'>
  void Page_Load(Object Sender, EventArgs e)
  {
     Search1.TitleBarText = "Personnel Search";
    Search1.ShowAdvanced();
  }
</script>

The simplicity of this code highlights an important benefit of user controls (and componentized code in general)—adding new functionality to a component does not typically require that programmers who use that component make significant changes in their code to adapt to the new functionality. If you don't want to use the Search control's new functionality, simply leave it out of your method call.

Handling Events from a User Control

Your user control can handle events that are generated by controls contained by the user control. For example, you may choose to encapsulate Search functionality within the Search control itself. To do this, you write a handler for the event that kicks off the search, inserting any code you want in the event handler.

This version of the Search control demonstrates how to handle internal system events. For this example, when the user clicks the OK button, the button's Click event is handled by the Search_Click event procedure, which then kicks off the actual search. We haven't talked about doing database queries yet (we get to that in Chapter 11, "Creating Database Applications with ADO.NET"), so we'll stub out the "real" search results with a label control. Listing 9.6 provides an example of the Search control.

Listing 9.6 Version of the Search Control That Handles an Internal Event
<SCRIPT runat='server'>
  private String strTitleBarText;

  public String TitleBarText
  {
    get{
      return strTitleBarText;
      }
    set{
      strTitleBarText = value;
      }
  }

  void Search_Click(Object Sender, EventArgs e)
  {
    SearchResults.Text = "Search results for '" +
            SearchText.Text   +   "' go here.";
  }

</SCRIPT>
<table width="250" border="0" cellpadding="3" cellspacing="0">
  <tr>
    <td bgcolor="#000066"><font color="#FFFFFF">
        <% =TitleBarText %>
      </font>
    </td>
  </tr>
  <tr>
    <td align="center" bgcolor="#CCCCCC" height='75px'>
      <table width="98%" border="0">
        <tr>
          <td>Text:</td>
          <td>
            <asp:textbox id='SearchText' runat='server' />
          </td>
          <td align="center">
            <asp:button id="GoSearch" onClick='Search_Click' text="Go"
runat='server' />
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>
<br>
<asp:label id='SearchResults' runat='server' />

Note that you don't have to handle the Click event of the button if you don't want to. Because the user control exists on a form in an ASP.NET page, the control will generate a round trip to the server when its Go button is pressed. You can then handle the Search functionality in the hosting page if you didn't already handle it inside the control. There's no right way to accomplish this division of labor; encapsulating the Search functionality in the control makes the control simpler to use, but less flexible, because the programmer of the hosting page would have to change the control if she wanted to change the Search functionality.


 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows