Coding a custom ClickBehavior with MS AJAX

Published: 02 Oct 2006
By: Alessandro Gallo

The MS AJAX framework provides an event model similar to the .NET framework's one. We can create, raise and handle events in a manner similar to what happens in a code-behind file of an ASP.NET page. Plus, the MS AJAX framework allows to attach pieces of re-usable client-side functionality through the concept of behaviors. Let's give an example of these concepts with the help of a little example.

Introduction

The MS AJAX framework provides an event model similar to the .NET framework's one. We can create, raise and handle events in a manner similar to what happens in a code-behind file of an ASP.NET page. Plus, the MS AJAX framework allows to attach pieces of re-usable client-side functionality through the concept of behaviors. Let's give an example of these concepts with the help of a little example.

Scenario

The simple MS AJAX behavior that we are going to code is called CustomClickBehavior and, when attached to a MS AJAX control, allows to subscribe to four events:

  • mouseDown, is fired when the user presses one of the mouse buttons into the area occupied by the control's associated DOM element.
  • mouseUp, is fired when the user releases one of the mouse buttons into the area occupied by the control's associated DOM element.
  • leftClick, is fired if the user clicks the left mouse button.
  • rightClick, is fired if the user clicks the right mouse button.

This behavior, that could be easily extended (for example to detect double-clicks or mouse wheel movements), shows how to create, raise and handle events in the MS AJAX framework.

The first thing to do is creating a new JavaScript file and name it CustomClickBehavior.js. Then, drop the following code:

Type.registerNamespace('DotNetSlackers');
DotNetSlackers.CustomClickBehavior = function() {
    DotNetSlackers.CustomClickBehavior.initializeBase(this);
    
    var _mousedownHandler;
    var _mouseupHandler;
    
    this.mouseUp = this.createEvent();
    this.mouseDown = this.createEvent();
    this.leftClick = this.createEvent();
    this.rightClick = this.createEvent();
    
    this.initialize = function() {
        DotNetSlackers.CustomClickBehavior.callBaseMethod(this, 'initialize');
        
        _mousedownHandler = Function.createDelegate(this, mousedownHandler);
        _mouseupHandler = Function.createDelegate(this, mouseupHandler);
        
        this.control.element.attachEvent('onmousedown', _mousedownHandler);
        this.control.element.attachEvent('onmouseup', _mouseupHandler);
    }
    
    this.dispose = function() {
        this.control.element.detachEvent('onmousedown', _mousedownHandler);
        this.control.element.detachEvent('onmouseup', _mouseupHandler);
        
        _mousedownHandler = null;
        _mouseupHandler = null;
        
        DotNetSlackers.CustomClickBehavior.callBaseMethod(this, 'dispose');
    }
    
    this.getDescriptor = function() {
        var td = DotNetSlackers.CustomClickBehavior.callBaseMethod(this, 'getDescriptor');
        
        td.addEvent('mouseDown', true);
        td.addEvent('mouseUp', true);
        td.addEvent('leftClick', true);
        td.addEvent('rightClick', true);
        
        return td;
    }
    
    function mousedownHandler() {
        var evt = window.event;
        
        this.mouseDown.invoke(this, Sys.EventArgs.Empty);
    }
    
    function mouseupHandler() {
        this.mouseUp.invoke(this, Sys.EventArgs.Empty);
        
        var button = window.event.button;
        
        if(button == 0 || button == 1) {
            this.leftClick.invoke(this, Sys.EventArgs.Empty);
        }
        else if(button == 2) {
            this.rightClick.invoke(this, Sys.EventArgs.Empty);
        }
    }
}
DotNetSlackers.CustomClickBehavior.registerClass('DotNetSlackers.CustomClickBehavior', Sys.UI.Behavior);
Sys.TypeDescriptor.addType('script', 'customClickBehavior', DotNetSlackers.CustomClickBehavior);

Code explanation

The code for the CustomClickBehavior starts with the creation of a namespace called DotNetSlackers, that will contain the behavior class. Notice that the behavior (as any other MS AJAX class) is declared using its full qualified name (i.e. prefixing its name with the name of the container namespace).

The initializeBase() method (we use the term "method" since we are dealing with the MS AJAX OOP patterns for JavaScript) must be called by every class that has a parent class. Every behavior inherits from the base Sys.UI.Behavior class, and the inheritance relationship is specified in the call to the registerClass() method, that is the first statement outside the body of the class. The parent class is the second argument passed to the registerClass() method.

To create an event, we use the object of type Event that is returned by the createEvent() method. This method is provided by every MS AJAX component (a component is a class that inherits from the base Sys.Component class and a behavior is a MS AJAX component) and the returned object is assigned to a particular class member that, at this point, represents a MS AJAX event.

In the initialize() method, we are hooking-up the mousedown and mouseup events raised by the DOM element that the associated control is wrapping. Notice how we are declaring the event handler by first creating a delegate with the createDelegate() method and then assigning the returned reference to a class member. Then, we are using this reference to attach the handler to the DOM event through the attachEvent() method called on the DOM element wrapped by the associated control. This approach, that is the recommended one, allows to handle DOM events by preventing the potential memory leaks that could be caused by an improper use of JavaScript closures. Finally, notice how we are effectively wrapping a DOM event with a MS AJAX event (for example, the mouseUp event is wrapping the mouseup DOM event).

The two methods that handle the DOM events, mousdownHandler() and mouseupHandler(), are used to raise the corresponding MS AJAX events (mouseDown and mouseUp) after performing some elaborations. Specifically, in the mouseupHandler() method we are testing against the button property of the event object (that is accessed in all the supported browsers through the window.event reference) to determine whether to raise the leftClick or rightClick event.

To be able to handle these four events with the declarative code (xml-script) we have to override the getDescriptor() method and use the addEvent() method on the type descriptor instance returned, like we did in the code snippet above.

To complete this overview of the MS AJAX event model, let's code a simple ASP.NET page that traces the events raised by the MS AJAX framework when the user clicks a simple HTML button:

<%@ 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>CustomClickBehavior Example</title>
</head>
<body>
    <form id="form1" runat="server">
        <atlas:ScriptManager ID="sm" runat="server">
            <Scripts>
                <atlas:ScriptReference ScriptName="custom" Path="CustomClickBehavior.js" />
            </Scripts>
        </atlas:ScriptManager>
    
        <input type="button" value="Click Me" id="button1" />
    
    </form>
    <script type="text/xml-script">
        <page>
            <components>
                <control id="button1">
                    <behaviors>
                        <customClickBehavior mouseUp="onMouseUp"
                                             mouseDown="onMouseDown"
                                             leftClick="onLeftClick"
                                             rightClick="onRightClick"
                                             />
                    </behaviors>
                </control>
            </components>
        </page>
    </script>
    <script type="text/javascript">
    <!--
        function onMouseUp(sender, e) {
            debug.trace('mouse up!');
        }
        
        function onMouseDown(sender, e) {
            debug.trace('mouse down!');
        }
        
        function onLeftClick(sender, e) {
            debug.trace('left click!');
        }
        
        function onRightClick(sender, e) {
            debug.trace('right click!');
        }
    //-->
    </script>
</body>
</html>

Regarding the declarative xml-script section, having provided a type descriptor for our behavior allows to map the MS AJAX events to XML attributes whose value is a reference to a JavaScript function (declared in the JavaScript block that follows the xml-script one) that is responsible for handling that particular event.

Conclusions

The above code snippet is a piece of reusable client-side code that we can attach to any MS AJAX control to raise custom events when the user clicks on the associated DOM element. We've also seen how the MS AJAX event model allows to raise and handle events in a manner similar to the .NET framework.

About Alessandro Gallo

Alessandro "Garbin" Gallo is a Microsoft MVP in the Visual ASP/ASP.NET category and a .NET developer/consultant. He is a contributor for the Ajax Control Toolkit project, owned by Microsoft. Alessandro won the Grand Prize at the "Mash-it-up with ASP.NET AJAX" contest held by Micr...

View complete profile

Top Articles in this category

Introduction to the PopupControl Extender
The PopupControl extender is a way to render a server control popup to the user, so that the user can perform some action (say click on a textbox), the user selects a value in a control that dynamically appears, and that value is posted back to the originating server control.

The UpdatePanel opened: what happens behind the scenes?
The UpdatePanel is one of the coolest features of Microsoft's ASP.NET AJAX. It magically lets you enhance your web sites with AJAX goodness, with very little cost to you as a developer. If you are like me though, you'll be feeling just a little bit uncomfortable simply dragging the UpdatePanel onto your web form, and then letting it do its stuff. We want to understand what it is doing on our behalf.

Xml-script tutorial Part 1
ASP.NET AJAX provides a way to instantiate client side types using a declarative programming model ala ASP.NET.

Xml-script tutorial Part 3
In the first and second part of this tutorial on xml-script we introduced the declarative programming model and illustrated how to handle events raised by the client Microsoft Ajax objects. Events can be handled by invoking a global JavaScript function or by using actions; in this third part we'll talk about the InvokeMethod action, which allows calling a method declaratively.

Xml-script tutorial Part 2
In the first part of this tutorial on xml-script we introduced type descriptors and learned how to read them. In this second part we'll see how to declaratively handle events raised by client side controls.

Top
 
 
 

Please login to rate or to leave a comment.

Product Spotlight