Adding Events to Flex Custom Components

After having discussed how to apply styles to Flex custom components, let’s learn how to add Events to Flex custom components such that they can be smoothly integrated in any other custom component or in any application.

The covered subjects are:

Introduction to Flex Events

When developing Flex Custom Components you’ll spend a lot of time working with events.

Events can be generated by user actions (such as a mouse click), by other external inputs (such as the return of a web service call), when changes happen in the life cycle of the component (such as when the component is created) or when the appearance of the component is modified (when the component is resized or hidden for example).

Events, in addition to notifying listeners that something happened, usually carry some contextual data related to the event. For example, the event indicating the end of a web service call provides the status for the service execution; or the event indicating a mouse click indicates if special keys were pressed when user clicked.

An event is identified by:

  • its class. For example: FlexEvent, MouseEvent, DragEvent, UpdateEvent, …
  • its type (or name). For example: FlexEvent.CREATION_COMPLETE, MouseEvent.CLICK, DragEvent.DRAG_START, UpdateEvent.INITIALIZED

Each available event class has a certain number of properties that provide the event context. Each event class has a different set of properties (all events sharing the same context information are usually of the same class, there can be exceptions to this statement).

There are many predefined event classes provided by the Flex framework1, but you can also create your own custom events. This is generally necessary when you want your events to carry some data specific to your component. If you don’t need to carry specific data in your events, you can safely use one of the existing predefined event classes.

For each predefined event class, several event types are available (MouseEvent, for example, has 17 associated different event types such as CLICK, MOUSE_WHEEL, MOUSE_MOVE, MOUSE_OVER, MOUSE_DOWN, …). All these events carry the same data (i.e. defined by the event class) but are triggered at different times or in different conditions. You can listen individually to any of these events (i.e. if you want to know if the user has clicked on your button, CLICK is the event to listen to; on the other hand, you don’t really care to know if user played with his/her mouse wheel so there’s no need to listen to MOUSE_WHEEL event type).

You can also create custom event types by reusing an existing event class and just providing a new event type specific to your component. For example, instead of generating a generic CLICK event, you may prefer to generate a DELETE_CLICKED event that clearly indicates what has been clicked.

Listening to Events

Components that can send events inherit from the EventDispatcher class (or implements the IEventDispatcher interface when inheritance isn’t possible).

Note: DisplayObject inherits from EventDispatcher, meaning that any object put in the Display List can fully participate in the event model (sending and listening to events).

EventDispatcher provides the addEventListener() method that you must call whenever you want to be notified of any particular event. The first two arguments of addEventListener() are the event type and a function (usually called a Handler) that will be triggered when the event is fired.

Let’s look at an example showing how to determine when a button is clicked.

In Action Script:

myButton.addEventListener(MouseEvent.CLICK, onButtonClickHandler);

In MXML:

<mx:Button id="myButton" click="onButtonClickHandler(event);" />

and declare your event handler as:

private function onButtonClickHandler(event:MouseEvent):void { ... }

Note that, when registering event listeners in MXML, you can’t use the static string constant (such as MouseEvent.CLICK); you must directly use the string (click in our case). You could also use the string in Action Script by doing:

myButton.addEventListener("click", onButtonClickHandler);

but you should always prefer using the static string constant to avoid any typing mistake that could lead to some headaches when trying to understand why your component doesn’t work as intended.

We’ll discuss the addEventListener() optional arguments useCapture, priority and weakRef in the Event Flow section.

When you aren’t interested anymore by a particular event type, it’s time to stop listening for it. Just call removeEventListener() with the same event type and event handler function that you used when calling addEventListener().

Ex:

myButton.removeEventListener("click", onButtonClickHandler);

Forgetting to remove unused event listeners is one of the major causes of memory leaks, so always ensure that you remove your event listeners when you don’t need them anymore (see the Using weak references for Event listeners section).

Sending Events

Sending events is as easy as listening to events and rely on the dispatchEvent() method of the EventDispatcher class.

Sending an event is done in two (or three) steps:

  • creating the event (using the new operator),
  • configuring the event properties (this is an optional step depending on the event),
  • dispatching the event.

When creating a new event, you just use the new operator to call the constructor of the event class that you want to instantiate.

Event constructors are following the same pattern:

  • the first argument, a String, is the event type;
  • the second, a Boolean, indicates if the event object participates in the bubbling stage of the event flow (see the Event Flow section);
  • the third, another Boolean, indicates if the event object can be canceled see the Event Flow section).

Note: The second and third arguments are available only for certain events. When they are, only the first parameter is mandatory, the two others are optional and set to true and false by default.

An event constructor can have more arguments when it provides contextual information with the event.

The HTTPStatus event, for example, has a fourth argument to provide the HTTP status code returned by the server. The MouseEvent has 11 additional arguments to provide the coordinates at which the mouse was pressed, whether Ctrl or Alt keys were pressed, etc.

In some cases, the event properties can’t be defined using the constructor and must be set after the instantiation2.

Once the event is instantiated, just pass it to the dispatchEvent() method to send it:

var myClickEvent:MouseEvent = new MouseEvent(MouseEvent.CLICK);

dispatchEvent(myClickEvent);

You can also “ask” a component to send an event (dispatchEvent() is a public method). If you want one of your buttons to send a CLICK event, just call its dispatchEvent() method:

myButton.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

Manually dispatching events can also be done from MXML (here the button sends a CLICK event as soon as the mouse is over it):

<mx:Button id="btn" mouseOver="btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));" />

Creating a Custom Event Type

Creating a custom event type means reusing an existing event class and just declaring a new type for it (i.e. a new name if you prefer).

To send such a custom event type, just use your custom event name when instantiating the event:

dispatchEvent(new MouseEvent("myClick"));

As already mentioned, it’s a good practice to define a static constant for your event types; so add in your class something like:

public static const MYCLICK:String = "myClick";

such that components wanting to listen to your event can do:

component.addEventListener(Component.MYCLICK, handler);

To allow your event to be usable from MXML, you have to use the [Event] metadata tag above your class definition:

[Event(name="<myEventTypeName>", type="<EventClass>")]

Ex: [Event(name="myClick", type="flash.events.MouseEvent")]

Defining custom event types can help developers a lot to understand the meaning of the events sent by your component. For example, instead of sending a generic CLICK event, sending a custom DELETE_CLICK event is much clearer.

Creating a Custom Event Class

When your want your event to carry context data specific to your component, you have to create a custom event class and attach your properties to it.

Usually, custom event classes extend flash.events.Event but you can use any existing event class as your base class.

In a custom event class, it’s better to follow these rules:

  • Declare a static string constant for your event type (choose a descriptive name)
  • Keep the first three arguments of your constructor consistent with Flex events (i.e. event type, bubbles, cancelable)3.
  • Add your specific properties in the constructor (it’s common that custom properties be defined just after the event type argument, keeping bubbles and cancelable at the end)
  • Override the clone() method
  • Override the toString() method

The clone() method is necessary if an event must be re-propagated during the Bubbling phase for example (see the Event Flow Section).

The toString() method is used, for example, when tracing your event. If you don’t override it, the trace won’t contain your specific properties.

Here’s a complete example of a custom event class:

import flash.events.Event;

public class DeleteClickEvent extends Event
{
	// define a descriptive event name using a static String constant
	public static const DELETE_CLICK:String = "deleteClick";

	// define your custom properties
	public var deleteObjectName:String;

	// add arguments to your constructor to provide values for your
	// custom properties (you can keep bubbles and cancelable arguments
	// as the last arguments).
	public function DeleteClickEvent(type:String, objName:String,
		bubbles:Boolean=false, cancelable:Boolean=false)
	{
		super(type, bubbles, cancelable);

		// Store your custom properyy
		deleteObjectName=objName;
	}

	// override clone for event bubbling (if you don't, the cloned events
	// won't have your custom property available)
	override public function clone():Event {
		return new DeleteClickEvent(type, deleteObjectName, bubbles, cancelable);
	}

	// override toString for complete traces (i.e. including your custom properties)
	override public function toString():String {
		return formatToString("DeleteClickEvent", "type", "deleteObjectName",
			"bubbles", "cancelable", "eventPhase");
	}
}

Click here to download the above code sample

The Event Flow

The Flex event flow is quite complex to explain and understand but I think it’s really useful to take time to clarify as much as possible what is behind the Event flow since most flex applications are event driven.

The Flex event flow is divided in three phases:

  • Capturing phase
  • Targeting phase
  • Bubbling phase

During each of these phases, different components have a chance to react to the processed event. In other words, for each phase, Flex determines what components are interested by the event and triggers their listeners accordingly. Let’s see below the three phases before clarifying when they happen and how you can determine which phase is currently happening.

Capturing Phase

The Capturing phase concerns all the nodes from the Application node down to the parent of the component sending the event. If any of these nodes have declared an event listener for the current event type, the listener is triggered.

What is key here is that the nodes listener don’t need to be defined on the component sending the event to be fired. In the previous sections, when listening to events, we always mentioned calling the addListener() method on the component sending the events. During the capturing phase, event listeners assigned to any node of the hierarchy4are triggered as long as they listen to the right event type. So, during this phase, only the event type really matters.

The listeners are called in the order of the hierarchy exploration: on Application first, down to the component parent last.

Targeting Phase

The Targeting phase concerns all the nodes that registered a listener directly on the component sending the event. So all listeners of the right event type that have been registered on the component are triggered one by one (see the Multiple Listeners section for more details).

This is “the normal” phase, at least the one that everybody knows the best ;-)

Bubbling Phase

The Bubbling phase is very similar to the Capturing phase. The concerned nodes are the same as in the Capturing phase (i.e. the hierarchy from Application to the component parent). The difference is the order on which the event listeners are triggered.

The listeners are called in the reverse order compared to the Capturing phase: component parent first, up to Application last.

Not all events participate to the three phases of the Event Flow. Some events are only processed during the Targeting Phase for example. Any event may also be canceled which would stop the event continuing to the other phases. The Event Flow is determined mainly by optional arguments used when calling addEventListener() or when calling the event constructor as detailed below.

Event Flow Configuration

The Capturing phase is rarely used. The Bubbling phase is much more common.

By default, listeners aren’t triggered during the Capturing phase. To force a listener to participate to the Capturing phase, the third argument of the addEventListener() method must be set to true (use_capture) when registering the listener5.

If an event is processed during the Capturing phase, it can still be processed also during the Bubbling phase but listeners that have been defined for the Capturing phase (i.e. with use_capture set to true) won’t be called during the Bubbling phase. If you want your listener to be called during the two phases, you must call addEventListener() twice (once with use_capture set to true, once with use_capture set to false).

An event is processed in the Bubbling phase only if its bubbles property has been set to true (this is the second argument of event constructors supporting bubbling). Some events can’t bubble at all, in this case they don’t have the bubbles argument in their constructor.

The third argument of event constructors (cancelable), when set to true, allows any listener to cancel the event. When canceled, the event won’t continue to the other phases (click for more details on cancelable events).

Useful Event properties

The Event class provides some very useful properties allowing to determine when or why one of our event listeners has been called. As Event is the base class of all events, you can use these properties in all cases.

target: defines the component that sent the event
currentTarget: defines the listened component (i.e. the one on which addEventListener() was called)

Note: When you listen for Button events, the target isn’t the Button but usually one of its subcomponents (a UITextField); the currentTarget is the Button. During the Capturing of Bubbling phases, the target remains the same (the UITextField) but the currentTarget is the container on which the listener has been defined, not the Button. So be careful when using target and currentTarget, the objects that they refer to aren’t always those that you’d thought.

eventPhase: defines the current phase (CAPTURING_PHASE, AT_TARGET, BUBBLING_PHASE).

Stopping Event Propagation

Any event handler can stop the event propagation (in any phase) by calling stopPropagation() or stopImmediatePropagation().

These two methods differ only in whether the current node’s remaining event listeners are triggered or not. They are triggered if you use stopPropagation(), they aren’t if you call stopImmediatePropagation().

Multiple Listeners

Multiple listeners can be registered for the same event. The listeners are registered in the order in which addEventListener() is called. When triggering an event, the listeners are called using this order (i.e. the first registered listener is called first).

It is anyway possible to influence the order in which listeners are triggered by using the fourth argument of the addEventListener() method, called priority. Flex calls event listeners in priority order, from highest to lowest (registration order is used for listeners of the same priority). The default priority is zero, you can set it to any integer value (positive or negative).

Note: It is important to understand that Flash Player does not necessarily wait until the first event listener finishes processing before proceeding with the next one so even if you give a listener a higher priority than other listeners, there is no way to guarantee that the listener will finish executing before the next listener is called (source: Adobe LiveDocs).

Using weak references for Event Listeners

The fifth argument of the addEventListener() method is a Boolean called weakRef. This parameter defines whether the component keeps a weak or a strong reference to the listener. Without entering into details6, a weak reference allows the listeners to be garbage collected if the only reference to the listener is the EventDispatcher. This means that a listener can be deleted even if you didn’t call the removeEventListener() explicitly.

List of footnotes:
  1. Most of the predefined Flex events are available in the mx.events and flash.events packages []
  2. Flex predefined events are all allowing to configure all their properties using the constructor []
  3. When you don’t want your event to support bubbling or to be canceled, don’t keep these arguments in your constructor []
  4. This is the Display List hierarchy, not the class inheritance tree []
  5. Note that you can’t register a listener for the Capturing phase from MXML []
  6. When to use Weak References could be a good topic for a future article! []
VN:F [1.9.8_1114]
Rating: 5.0/5 (8 votes cast)
Adding Events to Flex Custom Components, 5.0 out of 5 based on 8 ratings
  1. Sri
    June 7th, 2010 at 22:33 | #1

    vivid explanation.. thanks

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  2. June 7th, 2010 at 23:01 | #2

    @Sri
    Glad you liked it :)

    VN:F [1.9.8_1114]
    Rating: 0.0/5 (0 votes cast)
  3. Anaya
    July 30th, 2010 at 11:26 | #3

    Nice one…really useful..
    Would be more helpful if u can attach sample code..

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  4. July 30th, 2010 at 19:05 | #4

    @Anaya
    Thanks Anaya.

    Good point, I just added a download link below the code sample. Right click on it, choose save as and enjoy :-)

    VN:F [1.9.8_1114]
    Rating: 0.0/5 (0 votes cast)
  5. Noisy
    January 20th, 2011 at 01:13 | #5

    great, easy to follow thanks, but any chance you could explain how to use the custom event class once created?

    I can’t see how to use one event type with several different classes, at the moment I’m fine with
    creating and dispatching an event in each class and then catching it like this
    component.addEventListener(Component.MYCLICK, handler);

    but what if we want to dispatch MYCLICK from several different objects and use the same listener?

    VA:F [1.9.8_1114]
    Rating: 0.0/5 (0 votes cast)
  6. JYC
    January 26th, 2011 at 23:57 | #6

    @Noisy
    Not sure I understand your problem :-)

    If you want to process a custom event that is sent by different classes, you just have to add a listener to each of the provider classes. You can use the same handler if you want.

    Is it what you meant?

    VA:F [1.9.8_1114]
    Rating: 0.0/5 (0 votes cast)
  7. April 20th, 2011 at 14:07 | #7

    Nicely explained Custom Event Class thanks this had helped me and i think all flex developer might clear with there problem after reading this post

    VA:F [1.9.8_1114]
    Rating: 0.0/5 (0 votes cast)
  1. July 26th, 2010 at 08:35 | #1
Put ActionScript or MXML code snippets between tags: [as3][/as3]
You can also resize the comment area by dragging the bottom right corner.