Wrox Home  
Search
ASP.NET AJAX Programmer's Reference
by Shahram Khosravi
September 2007, Paperback


Excerpt from ASP.NET AJAX Programmer's Reference

Developing Custom ASP.NET AJAX Client Controls

by Dr. Shahram Khosravi

An ASP.NET AJAX client control is an ASP.NET AJAX client component that directly or indirectly derives from the Control base class. You can think of an ASP.NET AJAX client control as an ASP.NET AJAX representation of a specific DOM element on a page.

The ASP.NET AJAX client controls essentially emulate their corresponding ASP.NET server controls. Most basic ASP.NET server controls, such as Label and Image, are ASP.NET representations of DOM elements. These representations enable you to program against the underlying DOM elements using the ASP.NET/.NET Framework. In other words, these representations enable you to treat DOM elements as .NET objects.

The ASP.NET AJAX client controls play a similar role in the client-side programming. These controls are the ASP.NET AJAX representations of DOM elements, allowing you to program against these elements using the ASP.NET AJAX Framework. In other words, these representations enable you to treat DOM elements as ASP.NET AJAX objects.

Every ASP.NET AJAX client control emulates its corresponding ASP.NET server control as much as possible. As such, they expose similar methods and properties as their server counterparts.

The ASP.NET AJAX client-side framework includes with a Sys.Preview namespace defined as follows:

Type.registerNamespace('Sys.Preview');

The Sys.Preview namespace contains a UI namespace defined as follows:

Type.registerNamespace('Sys.Preview.UI');

The Sys.Preview.UI namespace contains several client controls that directly or indirectly derive from the ASP.NET AJAX Control base class. The following sections walk you through the code for these client controls to help you gain the skills you need to develop your own custom client controls. You'll also take a look at the code for Web pages that use these client controls.

Label Client Control

The ASP.NET AJAX Label client control is the ASP.NET AJAX representation of the <span> HTML element. The Label client control derives from the Control base class and extends its functionality to add support for two new properties named htmlEncode and text. The following sections discuss the members of the Label client control.

Constructor

Listing 1 presents the implementation of the constructor of the Label client control. Note that this constructor takes a single argument, which references the DOM span element that the Label control represents.

Listing 1: The Constructor of the Label Client Control

Sys.Preview.UI.Label = function Sys$Preview$UI$Label(associatedElement)
{
  Sys.Preview.UI.Label.initializeBase(this, [associatedElement]);
}


Sys.Preview.UI.Label.registerClass('Sys.Preview.UI.Label',Sys.UI.Control);

This constructor calls the initializeBase method to invoke the constructor of its base class—Control —passing in the reference to the DOM element that the Label control represents.

htmlEncode

The Label client control exposes a getter method named get_htmlEncode and a setter method named set_htmlEncode that respectively get and set the value of the htmlEncode Boolean property of the control, as shown in Listing 2.

Listing 2: The Getter and Setter Methods of the html Encode Property

function Sys$Preview$UI$Label$get_htmlEncode()
{
  return this._htmlEncode;
}


function Sys$Preview$UI$Label$set_htmlEncode(value)
{
  this._htmlEncode = value;
}

text

Listing 3 presents the implementation of the Label control's get_text getter method, which returns the value of the text property of the control.

Listing 3: The get_text Getter Method of the Label Control

function Sys$Preview$UI$Label$get_text()
{
  var element = this.get_element();


  if (this._htmlEncode)
    return element.innerText;
  else
    return element.innerHTML;
}

This method first calls the get_element method to return a reference to the DOM element that the Label control represents:

var element = this.get_element();

The Label control inherits the get_element method from its base class— Control.

Next, the get_text method checks whether the value of the htmlEncode property is set to true. If so, it returns the value of the innerText property of the DOM element that the Label control represents:

if (this._htmlEncode)
    return element.innerText;

If not, it returns the value of the innerHTML property of the DOM element that the Label control represents:

else
  return element.innerHTML;

Listing 4 presents the implementation of the set_text method of the Label control.

Listing 4: The set_text Method of the Label Control

function Sys$Preview$UI$Label$set_text(value)
{
  if (!value)
    value="";


  var element = this.get_element();
  if (this._htmlEncode)
  {
    if (element.innerText !== value)
    {
      element.innerText = value;
      this.raisePropertyChanged('text');
    }
  }


  else
  {
    if (element.innerHTML !== value)
    {
      element.innerHTML = value;
      this.raisePropertyChanged('text');
    }
  }
}

This method first calls the get_element method of its base class to return a reference to the DOM element that the Label control represents:

var element = this.get_element();

Next, it checks whether the value of the Label control's htmlEncode property has been set to true. If so, it assigns the new value to the innerText property of the DOM element and calls the raisePropertyChanged method to raise the propertyChanged event:

element.innerText = value;
this.raisePropertyChanged('text');

The Label control inherits the raisePropertyChanged method from the Component base class. If the htmlEncode property has been set to false, get_text assigns the new value to the innerHTML property of the DOM element and calls the raisePropertyChanged method to raise the propertyChanged event.

The get_text and set_text methods of the Label control constitute convenient wrappers around the innerText and innerHTML properties of the DOM element that the control represents.

If you're wondering how the get_text and set_text methods work in a browser such as Firefox that does not support the innerText property, the answer lies in the Mozilla compatibility layer of the ASP.NET AJAX client-side framework, which includes the logic that adds the support for this property. Refer to the PreviewScripts.js JavaScript file for more information on the Mozilla compatibility layer.

prototype

As Listing 5 shows the get_htmlEncode, set_htmlEncode, get_text, and set_text methods of the Label client control are directly defined on the prototype property of the control. This means that these methods are instance methods and must be invoked on the instances of the Label control class, not the class itself.

Listing 5: The prototype Property of the Label Control

Sys.Preview.UI.Label.prototype =
{
  _htmlEncode: false,
  get_htmlEncode: Sys$Preview$UI$Label$get_htmlEncode,
  set_htmlEncode: Sys$Preview$UI$Label$set_htmlEncode,
  get_text: Sys$Preview$UI$Label$get_text,
  set_text: Sys$Preview$UI$Label$set_text
}

descriptor

Every component, including the Label control, must expose a property named descriptor that references an object literal describing the members of the component. The ASP.NET AJAX client-side framework includes a class named TypeDescriptor that uses the descriptor property of a component to discover its members. In other words, the descriptor property of a component contains metadata about the type of the component and its members. As such, the descriptor property of a component must always be defined directly on the component class itself.

The descriptor property of a component references an object literal that contains one or more name/value pairs, where each name/value pair describes a specific group of members. The name part of the name/value pair that describes the properties of a component contains the word properties, and the value part is an array of object literals where each object literal describes a particular property. In the case of the Label control, this array contains two object literals, where the first object literal describes the htmlEncode property and the second object literal describes the text property (see Listing 6). Each object literal contains two name/value pairs. The name part of the first name/value pair is the word name, and the value part is the string that contains the name of the property being described. The name part of the second name/value pair is the word type, and the value part references the constructor of the type of the property being described.

Listing 6: The descriptor Property of the Label Control

Sys.Preview.UI.Label.descriptor =
{
  properties: [ { name: 'htmlEncode', type: Boolean },
                { name: 'text', type: String } ]
}

Using Label Client Control

Listing 7 presents a page that uses the Label client control.

Listing 7: A Page that Uses the Label Client Control

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Untitled Page</title>
  <script type="text/javascript" language="javascript">
    var label;


    function clickcb(domEvent)
    {
      var chkbx = $get("chkbx");
      label.set_htmlEncode($get("chkbx").checked);
      var txtbx = $get("txtbx");
      label.set_text(txtbx.value);
    }


    function pageLoad()
    {
      var btn = $get("btn");
      $addHandler(btn, "click", clickcb);
      label = $create(Sys.Preview.UI.Label, null, null, null, $get("myspan"));
    }
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
      <Scripts>
        <asp:ScriptReference Assembly="Microsoft.Web.Preview"
        Name="PreviewScript.js" />
      </Scripts>
    </asp:ScriptManager>
    <input type="checkbox" id="chkbx"/>
    <label for="chkbx">Enable HTML encoding</label>
    <br /><br />
    Enter text: <input type="text" id="txtbx" />
    <button id="btn" type="button">Submit</button><br /><br />
    <span id="myspan"></span>
    <div>
    </div>
  </form>
</body>
</html>

Figure 1 shows what you'll see in your browser when you access this page:

  • A check box that allows you to toggle HTML encoding on or off
  • A text box where you can enter text
  • A Submit button
  • A <span> HTML element
Figure 1
Figure 1

When you enter a text into the text box and click the Submit button, the callback for the button retrieves the text and displays it inside the <span> HTML element. Figure 2 presents a different scenario from what's shown in Figure 1. The text "<b>ASP.NET AJAX </b>" is entered in both cases, containing the opening and closing tags of the <b> HTML element. In Figure 1, however, the HTML encoding is off. In this case, the opening and closing tags of the <b> HTML element are not HTML-encoded and consequently the <span> HTML element shows the text in bold. In Figure 2, on the other hand, the HTML encoding is on. In this case, the opening and closing tags of the <b> element are HTML encoded and consequently the <span> element displays these tags as if they were normal non-HTML characters.

Figure 2
Figure 2

Note that the page shown in Listing 7 contains the following reference:

<asp:ScriptReference Assembly="Microsoft.Web.Preview"
Path="PrevewScript.js" />

This script references a JavaScript file named PreviewScripts.js that contains the definition of the Label client control. This JavaScript file is embedded in the Microsoft.Web.Preview.dll assembly. You need to add this assembly to the bin directory of your application. When you install the Microsoft ASP.NET Futures, it automatically adds the necessary template to the Visual Studio. Therefore, if you use this template when you're creating a new Web site, the Microsoft.Web.Preview.dll assembly will be automatically added to the bin directory of your Web site.

As Listing 7 shows, pageLoad calls the addHandler static method on the DomEvent class to register the clickcb JavaScript function as the event handler for the click event of the Submit button:

var btn = $get("btn");
$addHandler(btn, "click", clickcb);

Then pageLoad instantiates an instance of the Label client control to represent the <span> HTML element:

label = $create(Sys.Preview.UI.Label, null, null, null, $get("myspan"));

Now let's walk through the code for the clickcb JavaScript function. This function first uses the $get global JavaScript function to return a reference to the check box element:

var chkbx = $get("chkbx");

It then passes the check box status into the set_htmlEncode method of the Label client control:

label.set_htmlEncode($get("chkbx").checked);

Finally, it calls the set_text method on the Label client control that represents the <span> element to display the value entered into the text box:

var txtbx = $get("txtbx");
label.set_text(txtbx.value);

Image Client Control

The ASP.NET Image server control is the ASP.NET representation of an image DOM element. As such, it exposes the width, height, src, and alt properties of this DOM element as the Width, Height, ImageURL, and AlternateText properties on the Image server control itself. This allows you to treat these DOM properties as properties on the Image server-control.NET object. The ASP.NET AJAX Image client control plays the same role in the ASP.NET AJAX client-side framework.

It is the ASP.NET AJAX representation of an image DOM element. As such, it exposes the DOM width, height, src, and alt properties of this DOM element as the width, height, imageURL, and alterateText properties on the Image client control itself. This allows you to treat these DOM properties as properties on an ASP.NET AJAX Image client control object. The following sections cover the implementation of the Image client control members.

Constructor

As Listing 8 shows, the constructor of the Image client control takes a single argument, which references the <img> HTML element the Image client control will represent. This constructor simply calls the initializeBase method to invoke the constructor of its Control base class, passing in the reference to the <img> element. The registerClass method is then called to register the Image class as the subclass of the Control base class.

Listing 8: The Constructor of the Image Client Control

Sys.Preview.UI.Image =
function Sys$Preview$UI$Image(associatedElement)
{
  Sys.Preview.UI.Image.initializeBase(this, [associatedElement]);
}
Sys.Preview.UI.Image.registerClass('Sys.Preview.UI.Image', Sys.UI.Control);

prototype

Listing 9 presents the implementation of the prototype property of the Image client control. In this implementation, an object literal describing all the instance methods of the control has been assigned to the prototype property. As discussed in the previous chapter, an instance method of a class is a method that is directly defined on the prototype property of the class, as opposed to the class itself. An instance method must always be invoked on an instance of a class, not the class itself.

Listing 9: The prototype Property of the Image Client Control

Sys.Preview.UI.Image.prototype =
{
  get_alternateText: Sys$Preview$UI$Image$get_alternateText,
  set_alternateText: Sys$Preview$UI$Image$set_alternateText,
  get_height: Sys$Preview$UI$Image$get_height,
  set_height: Sys$Preview$UI$Image$set_height,
  get_imageURL: Sys$Preview$UI$Image$get_imageURL,
  set_imageURL: Sys$Preview$UI$Image$set_imageURL,
  get_width: Sys$Preview$UI$Image$get_width,
  set_width: Sys$Preview$UI$Image$set_width
}

As you can see in this listing, the Image client control exposes four pairs of instance methods. Each pair allows you to set and get the value of a particular property of the Image class. For example, the set_height and get_height instance methods allow you to set and get the value of the height property of the Image client control.

The four properties that the Image client control exposes — width, height, imageURL, and alternateText — are given the same names as the corresponding properties of its Image server control counterpart to make client-side programming feel more like server-side ASP.NET programming.

image URL

The Image client control exposes two methods named get_imageURL and set_imageURL that allow you to get and set the value of the src property of the underling DOM element, as shown in Listing 10. As you can see from this code listing, both methods first call the get_element method to return a reference to the <img> element that the Image client control represents. The Image client control inherits this method from its Control base class.

Listing 10: The set_imageURL and get_imageURL Methods of the Image Client Control

function Sys$Preview$UI$Image$get_imageURL()
{
  return this.get_element().src;
}


function Sys$Preview$UI$Image$set_imageURL(value)
{
  this.get_element().src = value;
}

width

The Image client control exposes two methods named get_width and set_width that allow you to get and set the value of the width property of the image DOM element that the control represents. As you can see in Listing 11, these methods are just wrappers around the width property of the DOM element, which means you can treat this the same way as a property on an ASP.NET AJAX object.

Listing 11: The set_width and get_width Methods of the Image Client Control

function Sys$Preview$UI$Image$get_width()
{
  return this.get_element().width;
}


function Sys$Preview$UI$Image$set_width(value)
{
  this.get_element().width = value;
}

height

As you can see in Listing 12, the set_height and get_height methods act as wrappers around the height property of the underlying image DOM element. This enables you to treat this as a property on an ASP.NET AJAX object, which is the Image client control in this case.

Listing 12: The set_height and get_height Methods of the Image Client Control

function Sys$Preview$UI$Image$get_height()
{
  return this.get_element().height;
}


function Sys$Preview$UI$Image$set_height(value)
{
  this.get_element().height = value;
}

alternate Text

The get_alternateText and set_alternateText methods allow you to get and set the value of the alt property of the image DOM element using the ASP.NET AJAX client-side framework in the same way as you would to get and set the value of this property using the ASP.NET Framework (see Listing 13).

Listing 13: The set_alternateText and get_alternateText Methods of the Image Client Control

function Sys$Preview$UI$Image$get_alternateText()
{
  return this.get_element().alt;
}


function Sys$Preview$UI$Image$set_alternateText(value)
{
  this.get_element().alt = value;
}

Using the Image Client Control

Listing 14 presents a page that uses the Image client control. Previously, we implemented a similar page that showed how to use the Label client control where the page used the following script reference to reference the PreviewScript.js JavaScript file embedded in the Microsoft.Web.PreviewScript.dll assembly:

<asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="PreviewScript.js" />

As you can see, Listing 14 uses the same script reference because the same JavaScript file also contains the definition of the Image client control.

Listing 14: A Page that uses the Image client control

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Untitled Page</title>
  <script type="text/javascript" language="javascript">


    function pageLoad()
    {
      var type = Sys.Preview.UI.Image;
      var properties = { imageURL: "wroxProgrammerSmall.jpg",
                  alternateText : "Wrox Programmer's Reference Series",
                  width: 155, height: 58 };
      var events = null;
      var references = null;
      var element = $get("myImage");
      $create(type, properties, events, references, element);
    }
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
      <Scripts>
        <asp:ScriptReference Assembly="Microsoft.Web.Preview"
        Name="PreviewScript.js" />
      </Scripts>
    </asp:ScriptManager>
   <img id="myImage" />
  </form>
</body>
</html>

The pageLoad method uses the $create shortcut method (the shortcut for the create static method of the Component base class) to instantiate and initialize an Image client control and to add the control to the _components collection of the Application object that represents the current ASP.NET AJAX application. pageLoad passes the following parameters into the create method:

  • type: This parameter references the constructor of the component being created, which is the Sys.Preview.UI.Image constructor in this case.
  • properties: This parameter references an object (normally an object literal) that contains the names and values of the properties of the component being created that you want to initialize. The create method internally assigns these values to the properties with the specified names. In this case, the object literal contains four name/value pairs where the name and value parts of each pair respectively contain the name and value of a particular property of the Image client control being created:
var properties = { imageURL: "wroxProgrammerSmall.jpg",
           alternateText : "Wrox Programmer's Reference Series",
           width: 155, height: 58 };
  • events: This parameter references an object (normally an object literal) that specifies the event handlers that you want to register for events with the specified names. In this case, the events object is null so any event handlers will be registered.
  • references: This parameter references an object (normally an object literal) that specifies the values of the properties of the component being created that reference other components in the _components collection of the current Application object. In this case, this object is null because the Image client control does not expose any properties that reference other components of the application.
  • element: This parameter references the DOM element on the current page that the newly created Image client control will represent. In this case, this parameter references the <img> HTML element with id HTML attribute value of " myimage" :
var element = $get("myImage");

The pageLoad method then invokes the create method, passing in the five parameters to create the Image client control:

$create(type, properties, events, references, element);

This article is excerpted from Chapter 8, "Developing Client Controls," of the book, ASP.NET AJAX Programmer's Reference (Wrox, 2007, ISBN: 978-0-470-10998-4,) by Dr. Shahram Khosravi. Shahram is a senior software engineer, consultant, author, and instructor specializing in ASP.NET, Web services, Windows Communication Foundation (WCF), ASP.NET AJAX, Windows Workflow Foundation (WF), IIS7 and ASP.NET Integrated Programming, .NET, Web Services, XML technologies, ADO.NET, and C#. He has more than 10 years of experience in object-oriented analysis, design, and programming. Shahram has written articles on the .NET Framework, ADO.NET, ASP.NET, and XML technologies for industry leading magazines such as Dr. Dobb's Journal, asp.netPRO magazine, and Microsoft MSDN Online. His other recent articles on Wrox.com are Developing Ajax-Enabled Controls and Components: Periodic Refresh Pattern and ASP.NET 2.0 WebPartChrome, both adapted from his first book, Professional ASP.NET 2.0 Server Control and Component Development (Wrox, 2006, ISBN: 0-471-79350-7). Shahram's next book, Professional IIS 7 and ASP.NET Integrated Programming, is also being published in 2007 by Wrox (ISBN: 978-0-470-15253-9).