September 02, 2004
Making Delegate earn it's keep...
By now everyone should have at least heard of the "mx.utils.Delegate" class that was included in the Flash 7.2 updater. This post is not intended to explain the Delegate class, but rather, here's a simple little trick showing how to use Delegate to pass additional information to generic event handlers.
Here's the scenario: A user is presented with two buttons. Each button increments a value by a different amount (either 1 or 5) when the button is pressed.
Programming this interaction is easy enough - we simply create two functions, incrementBy1 and incrementBy5, and proxy the events to the appropriate function. Part of the code for that might look like this:
btn1.addEventListener("click", Delegate.create(this, onIncrement1));
btn2.addEventListener("click", Delegate.create(this, onIncrement2));
However, this solution isn't ideal. We've created two distinct increment functions that perform basically the exact same task. In this situation, what we really want to do is create a single increment function, and pass the amount to increment as a parameter.
We can mimic this behavior by proxying both of the events to the same onIncrement function, examine the event's target's name property to figure out which button was pressed, and then increment by the value associated with that button. The code for the new onIncrement might look like this:
function onIncrement(eventObj) { var which:Number; which = parseInt(eventObj.target._name.substr(3)); switch(which) { case 1: total += 1; break; case 2: total += 5; break; } trace(total); }
However, the problem with that is we're back to the reason that we wanted to proxy events in the first place! We don't want a generic event handler to have to determine where the event came from. Instead, we want the event handler to just "do it's thing" without worrying about context.
We can accomplish this by leveraging Function objects in Flash. Whenever you call Delegate.create, a function is returned. We can stuff properties into that Function object, and then access those properties from within the event handler by using the arguments.caller scope.
You can see this technique in action in the following complete code sample:
// 1) create a blank fla, drag a v2 button to the // stage then delete the button instance so that // the button only resides in the library. // 2) paste this code on frame 1 and test movie import mx.utils.Delegate; import mx.controls.Button; function init() { // create and place our buttons createClassObject(Button, "btn1", 1, {label:"plus 1"}); createClassObject(Button, "btn2", 2, {label:"plus 5", _y: btn1._y+btn1.height}); // both buttons use the same onIncrement event // handler - but the handler behaves differently // based on the step value set var f = Delegate.create(this, onIncrement); f.step = 1; // btn1 steps up by 1 btn1.addEventListener("click", f); f = Delegate.create(this, onIncrement); f.step = 5; // btn2 setps up by 5 btn2.addEventListener("click", f); // initialize our running total total = 0; } // generic increment function used as // event handler for the buttons function onIncrement() { // grab the step property from the // delegate function we created, and use // that as the increment value total += arguments.caller.step; trace(total); } init();
By using a generic increment function and using what I'll call "pseudo-parameters" we can easily add new buttons with different increment (or decrement) values. This enables us to easily reuse the code we already have and make maintenance easier.
In summary, we can pass parameters to a generic event handler using Delegate.create by stuffing data into the Function object returned by the create call and then using the arguments.caller scope in the event handler body to access that data.
More information on the Delegate class can be found here: Proxying Events with the mx.utils.Delegate Class

Comments
Sweet! Thanks for sharing!
Posted by: Andreas Weber at September 2, 2004 01:06 PM
If you prefer a little more type-safety, and you don't mind using my "unofficial" delegate implementation (http://www.peterjoel.com/blog/index.php?archive=2004_08_01_archive.xml), you can create specific subclasses of EventDelegate for a task, or a more general-purpose subclass, that allows properties to be stored for generic purposes.
To use your example, something more generic would be along the lines of:
import com.peterjoel.events.EventDelegate;
class ParamDelegate extends EventDelegate {
public var params:Array;
function ParamDelegate(targ:Object, func:Function){
super(targ, func);
params = [];
}
}
which you can use like this:
var f:ParamDelegate = new ParamDelegate(this, onIncrement);
f.params[0] = 1;
btn1.addEventListener("click", f);
f = new EventDelegate(this, onIncrement);
f.params[0] = 1;
btn2.addEventListener("click", f);
function onIncrement(event:Object, delegate:ParamDelegate){
total += delegate.params[0];
}
Posted by: Peter Hall at September 5, 2004 08:52 PM
For those of you who would rather use a custom class as a solution, check out Joey Lott's article here:
http://www.person13.com/blog/archives/000030.html
Posted by: darron at September 22, 2004 06:48 PM
I have used this method and it works great. How does one change the scope when dealing with ASBroadcaster like in the Tween class. I am trying to send a parameter with the onMotionFinished event with no luck.
var f = Delegate.create(this, onMotionFinished);
f.myParam = "hello";
myTween.addListener(f, "onMotionFinished");
Does not seem to work for me.
Posted by: Will at October 1, 2004 02:08 AM
NM, I got it working fine with the custom class wrapper.
Thanks
Posted by: Will at October 1, 2004 02:14 AM
doesn't this setup sort of contradict the main reason for using Delegate in the first place, namely to avoid creating an activation object for the function that assigns the callback?
since you store the Delegated function in local var 'f', all the local vars in init() (in this case, only 'f') will remain in memory until they're no longer pointed to, namely when the btn EventListeners are removed....
is this true? not entirely sure about it.
Posted by: depth at January 24, 2006 06:52 PM