Written by: Patrick Mineault, , http://www.5etdemi.com
Difficulty Level: Beginner
Requirements: MX 2004
Topics Covered: Delegate class, component events
Assumed Knowledge: actionscript, experience with components

The Delegate class

The Delegate class is a utility class written by Mike Chambers that makes working with component events easier. The class is included in 'Ellipsis', the second update to Flash MX 2004. Before continuing, grab the updater from Macromedia's site, which includes the Delegate class, and makes Flash more stable, faster, and generally better.

There are two ways in which you can assign an event handler for a component using addEventListener: assigning the event to an object, or assigning it to a function. For objects we have:

var obj = new Object();
obj.click = function(evt)
{
        trace(evt.target);
        trace(this);
}
myBtn.addEventListener("click",obj);

And for functions:

import mx.utils.Delegate;
function myClickHandler(evt)
{
        trace(evt.target);
        trace(this);
}
myBtn.addEventListener("click", Delegate.create(this,myClickHandler));

The problem with the first approach is that the handler method has to have the same name as the event. Hence if you want to assign several component events to the same object, you will need to create to create an awkward and long-winded conditional statement, looking at the event's target, like so:

obj = new Object();
obj.click = function(evt)
{
        if(evt.target == 'myBtn')
        {
                trace('first button');
        }
        if(evt.target == 'myBtn2')
        {
                trace('second button');
        }
}
myBtn.addEventListener("click",obj);
myBtn2.addEventListener("click",obj);

The problem with the second approach is scope: this refers to the component that triggered the event, regardless of where the function was declared. Thus if you want to assign several events to the same function, 'this' will be different every time the function is reached, with the most immediate workaround being a long conditional statement similar to the above.

Mike Chambers figured out an elegant solution to this whole multiple object/bad scope mess with the Delegate class. Instead of setting the listener directly, you assign a delegate function to listen to the event, which will take care of calling the real handler with the right scope. Here's how you would use it with a button:

Notice that we first import the delegate class, located in mx.utils.Delegate. Then when it's time to assign a handler, we create a Delegate, specifying scope and the final event handler. The Delegate class has only one static function, create, which has the following signature:

static function create(obj:Object, func:Function):Function

Here obj is the scope you want to apply to the func function. What if you want to remove an event listener after setting it? Simple:

import mx.utils.Delegate;
var md = Delegate.create(this, someFunction);
myBtn.addEventListener('click',md);
//other stuff happens
//then deletion occurs:
myBtn.removeEventListener('click',md);

As you see, Delegate.create gives you a very elegant and simple way to handle component events in a clean and centralized manner. To paraphrase Jesse Warden: using Delegate makes you slick!

Delegate also works for low-level events outside of the component framework. For example, you can use it with a movieclip's onEnterFrame event or an XML object onLoad event like so:

import mx.utils.Delegate;

function xmlLoaded(success)
{
        trace(this);
        trace(success);
}

function handleEnterFrame()
{
        trace(this);
        trace(getTimer());
}

var myXML = new XML();
myXML.onLoad = Delegate.create(this, xmlLoaded);

//There is a movieclip called myMC in the current timeline
myMC.onEnterFrame = Delegate.create(this, handleEnterFrame);

By using Delegate in conjunction with low-level events it's much easier to figure out the scope in which a callback is called, which means you can become less dependant on using _root. If you've had trouble with loadMovie and actions not working anymore, you know the you should keep references to _root to a bare minimum as it makes your movies much less portable.

Another neat way in which you can use Delegate is with setInterval. Internally when functions are called from an interval the scope is sometimes not passed along, so that trace(this); will show undefined. You can properly impose a scope on an interval using the following actioncript:

import mx.utils.Delegate;

function traceThis()
{
        trace(this);
}

var myInt = setInterval(Delegate.create(this, traceThis), 1000);

This works fine whether you are on the main timeline or inside of a class, meaning you don't have to use the second 'class form' of setInterval which uses a string as the second parameter. That's great because that form seems a bit flaky and unreliable sometimes, while the first form is rock-solid. In addition, the first form can be checked by the compiler because it's a reference to a function and not a string, meaning if you mistype the function name the compiler will catch it and throw an error, saving you from hunting for the error.

Up to now we've used Delegate.create with this as the first parameter, and 99% of the time that's how you will use it. However in some circumstances you can use Delegate in other contexts than this; for example you could scope it as though a function was called 'from the inside' to simulate a click or a key action. Delegate can thus become a powerful way to control your movie.

If you need professional help with ActionScript, please visit 5etdemi.com for my portfolio and contact info. Happy flashing!