How to Raise Custom Events (EventDispatcher)
This article follows on the heels of How to Raise Custom Events (AsBroadcaster), so if the following paragraphs seem to move too quickly, you may want to read that one first. To recap, the AsBroadcaster class provides a means to create custom events that may be subscribed to and acted upon by listener objects. The mechanism it uses is relatively straightforward.
Another way to accomplish the same goal, only marginally more complicated, is provided by the EventDispatcher class. (For what it’s worth, EventDispatcher is the class used by Flash’s v2 UI Components to raise events. Kind of neat.)
An answer, relatively short and sweet
Import the EventDispatcher class via its full path. Doing this means you can refer to it in subsequent code without having to retype the mx.events prefix.
import mx.events.EventDispatcher;
Create an arbitrarily named variable, broadcaster, and use it to store a reference to an instance of the Object class. The static EventDispatcher.initialize() method converts broadcaster into a bona fide broadcasting object.
var broadcaster:Object = new Object();
EventDispatcher.initialize(broadcaster);
Create another arbitrarily named variable, listener, and use it to store a reference to a second Object instance.
var listener:Object = new Object();
This listener will “hear” whatever events are sent to it. In order to respond to these events, the listener must have a function assigned for each event raised. Notice that parameters may be provided (see the onDropped function assignment, below). With EventDispatcher, parameters arrive as properties of yet another object. The function literal, below, accepts this third object as its only parameter, but this object may contain as many properties as you like, of any data type (even other objects!). In this example, we’re only looking for one string property.
listener.onPickedUp = function() {
trace("Picked up.");
};
listener.onDropped = function(evt:Object) {
trace("Dropped on " + evt.msg + " side.");
};
Finally, invoke the EventDispatcher.addEventListener() method on the broadcaster object, supplying both the event to listen for (as a string), and the listener object as parameters. The reason broadcaster knows how to perform this method is because EventDispatcher initialized it earlier.
broadcaster.addEventListener("onPickedUp", listener);
broadcaster.addEventListener("onDropped", listener);
As this point, use the EventDispatcher.dispatchEvent() method to raise the event(s) subscribed to the broadcaster object above elsewhere in your code.
dispatchEvent({type:"onPickedUp"});
broadcaster.dispatchEvent({type:"onDropped", msg:"right"});
Notice the shorthand way of instantiating a generic Object instance, {}. That second example, onDropped, could be written like this …
var eventToSend:Object = new Object();
eventToSend.type = "onDropped";
eventToSend.msg = "left";
dispatchEvent(eventToSend);
… but the shortcut — the curly braces approach — requires less typing.
At minimum, you must supply a type property, which specifies the sort of event being raised (see the onPickedUp event), but understand that you may include whatever additional properties you need.
For a practical example, download EventDispatcher.fla (opens as far back as Flash MX 2004). In the FLA file, you’ll find a draggable “ball” movie clip. Its draggability is due to functions assigned to the MovieClip.onPress and MovieClip.onRelease events. Note that these functions also invoke the EventDispatcher.dispatchEvent() method.
var halfway:Number = Stage.width / 2;
mcBall.onPress = function() {
this.startDrag();
broadcaster.dispatchEvent({type:"onPickedUp"});
};
mcBall.onRelease = function() {
this.stopDrag();
if (this._x < halfway) {
broadcaster.dispatchEvent({type:"onDropped", msg:"left"});
} else {
broadcaster.dispatchEvent({type:"onDropped", msg:"right"});
}
};
Another example is demonstrated in Part 2 of “Loading and Tracking Multiple Files at the Same Time” — probably a good idea to read Part 1 first. Yet another is demonstrated in “How to Play a Timeline Backwards (with Easing!)”
Additional reading
For an example of custom events raised from inside a custom class, see Kenneth Toley’s article at Adobe.com:
http://www.adobe.com/devnet/flash/articles/creating_events.html
July 26th, 2006 at 2:30 am
other than minor syntax differences, in what other way does eventDispatcher differs from the asBroadcaster?
or in other words - when do i use this and when do i use the other?
July 26th, 2006 at 8:52 am
eRez,
Really, the choice is yours. Personally, I prefer
EventDispatcher, but that’s only because it sends an event object with optional arguments supplied as object parameters, rather than just as arguments. But really, that’s like saying I prefer coffee ice cream because it tastes like coffee, rather than vanilla.For what it’s worth, the v2 Components that ship with Flash use
EventDispatcher— but honestly, just use the one that makes more sense to you.September 8th, 2006 at 6:38 am
Hi there David,
I have read your two articles about custom events and they have been really helpful and influenced me greatly in how I go about writing my flash projects. It is a much cleaner way of doing things than my current dodgy sub-movie loops to try and work out whether something has happened.
However I have a problem that I don’t know if you can shed some light on. I am using the webserviceconnector class to connect to an asp.net webservice. Sometimes my webservice takes forever or hangs, and I would like to give the user a way of cancelling the webrequest.
There don’t seem to be any methods for “abort” so I was thinking would it be possible to raise an event for the connector - i.e. raise an error event, so that I can stop it? If there is any other simpler way that you know of I would love to know - am scratching my head with this one…
Many thanks - great article,
Dom.
September 8th, 2006 at 10:22 am
Well - further to my last - there is no timeout property in Flash 8 either (although there is still a timeout faultcode??????) but i have been able to accomplish a cancel by using removeEventListener for the status and result events.
September 8th, 2006 at 11:21 am
Dom,
Thanks for the kind words!
As far as I know, there is not a way to cancel a web request in AS1/AS2 until it times out on its own (and you’re right, there is no
timeoutproperty — that has always baffled me). I seem to remember a Flashcoders workaround suggestion … something along the lines of requesting a new, known-good, very small file to “cancel” the pending request, but I’m afraid the details are hazy in my memory.I was about to suggest that you use
To my thinking, you’re on the right track. Good luck with that!
setInterval()orsetTimeout()[undocumented], when I saw your follow up post.December 1st, 2006 at 3:21 pm
Flash sucks. As such you took a great topic and gave a concise example that someone using a real programming language/IDE could work with it quickly.
Kudos.
December 1st, 2006 at 5:42 pm
Ed,
It sounds like we don’t have a whole lot in common, but I’m happy if this blog entry helped you.
I enjoy the occasional venture into other territory, be it C#, Java, Python, PHP … I have my hunch which of the above you’d consider “real,” but in many ways, it’s just a matter of where and how you care to spend your time.
I’m certainly thankful for developers who spend their efforts in lower level languages, because they allow me to do the things that float my boat. I’d say there’s plenty of elbow room for whoever wants to dive in.
Have you checked out Flex and ActionScript 3.0? I think you’ll find there’s quite a difference.
March 20th, 2007 at 11:30 pm
David,
Thanks for the informative article. I’ve successfuly implemented custom events with a class, but I’m having trouble dispatching an event from within an anonymous function (another event handler) within the class.
For instance, this code does not seem to reach the listeners outside the class.
MyXMLSocket.onData = function(sData:String){
var eventObject:Object = {target:this, type:”onDataReceived”};
this.dispatchEvent(eventObject);
}
Any clue what I’m doing wrong?
March 21st, 2007 at 12:02 am
Michael,
What you’re seeing is the result of how that function is scoped. The global
thisproperty is scoped to theMyXMLSocketobject by way of the anonymous function. Its “point of view,” therefore, is theMyXMLSocketobject, which almost certainly doesn’t feature adispatchEvent()method. Make sense?In a case like this the
Delegateclass is a good idea. Importimport mx.utils.Delegate;before your class declaration, then replace that function with this …What this does is re-route the scope of your function to the class itself, which does support a
dispatchEvent()method, assuming you usedEventDispatcher.initialize(this);in the constructor.Note: It didn’t appear you were using the
sDataparameter, so I dropped it.March 21st, 2007 at 8:06 am
Thanks David. Makes perfect sense, and your suggestion worked perfectly. By the way, I am using the data parameter, however, I had removed it during debugging attempts to simplify the problem.
xsMCE.onData = Delegate.create(this, function(sData:String) {
var eventObject:Object = target:this, type:”onDataReceived”, stream:sData};
this.dispatchEvent(eventObject);
});
Thanks for the insight.