Written by: Patrick Mineault, , http://www.5etdemi.com
Difficulty Level: Expert
Requirements: MX 2004
Topics Covered: EventDispatcher, Classes, MVC
Assumed Knowledge: Working knowledge of AS2 classes
Download FLAs

Using EventDispatcher

As your Actionscript projects become more involved it will become increasingly important to separate functionality between classes. Compartmentalizing helps you debug your scripts more easily, facilitates collaboration between multiple developers and promotes reuse.

For example, say you want to create a horizontal ticker that uses an RSS feed as a source. Your first instinct might be to create a single class that does the XML loading, the parsing, and then the ticking. If you want to promote reuse however, you'll want to make modules for reusable things and then create a controller class to bind everything together. In our RSS ticker case, you could create a RSSReader class that takes in a URL, creates an XML object, parses it, and then gets the relevant data and places it in a data array. Then you could create a HorizontalTicker class that takes an array of titles and creates a horizontal ticker out of it. Finally a Controller class could bind the RSSReader and the HorizontalTicker together, filling the gap. That way you can use the RSSReader and the HorizontalTicker for other projects outside of the RSS ticker context.

There are several places where RSSReader and HorizontalTicker will need to notify the Controller; for example, when the RSS feed is properly loaded, the controller will need to start the ticker. The question here is how to glue everything together. There are a few ways in which this could be done, for example:

  • the controller could continually check for a loaded variable in the RSSReader instance to see if it's true (polling)
  • the RSSReader could store a reference to the controller and call a certain function directly in it when the feed is loaded (hard links)
  • the controller could overwrite a function inside of the RSSReader that will be called by RSSReader (callbacks).

All of these have some inconveniences; the first method is wasteful and is just plain hacky; the second method is not very flexible because the class will not work properly when used out of context; the third method is in fact the method used by v1 components and can be quite useful but it necessarily creates one-to-one links when sometimes one-to-many relationships can be quite useful.

The method I'll discuss today is binding these classes using mx.events.EventDispatcher. Macromedia has kindly included several classes with Flash MX 2004 to help in certain redundant tasks. Among the most useful are:

  • mx.transitions.Tween
  • mx.utils.Delegate
  • Today's subject mx.events.EventDispatcher

This tutorial will be split in 3 sections:

  • Introduction to using EventDispatcher
  • Example 1: a pseudo-button class
  • Example 2: conditional form elements

If you already use EventDispatcher, you can skip right ahead to the examples, you could still learn some rather clever tricks. The tutorial zip file includes the source code to the examples, be sure to grab it to follow along.

If you've used v2 components, chances are you already have used EventDispatcher unknowingly when calling addEventListener or removeEventListener. EventDispatcher is at the core of the v2 component framework. Amazingly however, it has very little dependencies, so including mx.events.EventDispatcher in your projects adds less than 2k to a movie. First let's see a minimalist Timer class that uses EventDispatcher to see what it looks like:

import mx.events.EventDispatcher;
import mx.utils.Delegate;
class Timer
{
        var timeInt:Number;
        var addEventListener:Function;
        var removeEventListener:Function;
        var dispatchEvent:Function
       
        function Timer(len)
        {
                EventDispatcher.initialize(this);
                timeInt = setInterval(Delegate.create(this, handleTimer), len);
        }
       
        function handleTimer()
        {
                dispatchEvent({type:'timeout', target:this, message:'Hey dude the timer is done', time:getTimer()});
                clearInterval(timeInt);
        }
}

By calling EventDispatcher.initialize(this), three methods are added to the current class instance: addEventListener, removeEventListener, and dispatchEvent. We define these functions as class variables so that the compiler will not whine about undefined functions when we test our movie. When the timer is over, we dispatch an event. dispatchEvent takes one argument, an object with two properties: target and type. 'type' is the name of the event you want to dispatch, while target is whatever dispatched the event, which 99% of the time will be 'this'. Once those required fields are filled you can add whatever else you feel would be interesting to know about the event; in this case we've added a message and a time variable to show how this can work.

Dispatching an event is all fine and dandy, but isn't very useful unless someone is listening to it (you can insert a remark about trees falling in a forest with no one around here). We set up a listener like so:

import mx.utils.Delegate;
function handleConstruct(eventObj)
{
        trace(eventObj.target);
        trace(eventObj.type);
        trace(eventObj.message);
        trace(eventObj.time);
}
var myClass = new Timer(1000);
myClass.addEventListener('timeout', Delegate.create(this, handleConstruct));

Here we create an instance of the Timer class and add an event listener to it to link to the timeout event. We can see that although there isn't a direct hard link between the controller and the dispatcher, nevertheless the two are linked indirectly through EventDispatcher.

So what's so great about this method of doing things, especially when compared to simpler methods like callbacks? Well:

  • Several objects can listen to the same events. That means that dispatching one event can trigger calls to several methods seamlessly. In the third part of this tutorial I will show you how you can use the power of one-to-many event relationships to solve complicated problems elegantly.
  • You don't need to define a new method in your class for every new event you create, so you can dispatch as much events as you see fit without polluting your class with empty callbacks.
  • If you create components, EventDispatcher is the only way to go, because v2 components have a special tie-in between EventDispatcher and on(event) handlers in the IDE; meaning that if you add the proper metatag to your class all that needs to be done to handle the event is writing on(eventtype) in the actions panel, meaning it's easier for beginners and designers to use your components.
  • The event object you receive always includes a reference to the caller. This may sound a bit esoteric, if you don't understand don't lose sleep over it, but the scope in which the callback is called can be forced using a Delegate, yet you still know the original piece of code that dispatched the event. That gives you a lot of flexibility, since classes can handle events within their scope while keeping track of the real caller and all of this without ugly hacks, compiler fooling and other things that make babies cry.
  • Don't forget that mx.events.EventDispatcher is installed on every machine that has Flash MX 2004 on it, so that means one less piece of code to worry about including when you pass your code around.

At this point, I'd really like to believe that I've convinced you of the good of EventDispatcher, but I'm afraid that you still might be skeptical about it. What follows is two advanced examples of things you can do with EventDispatcher that would be a pain in the neck to do using other methods.