Macromedia FLEX-CREATING ADVANCED COMPONENTS User Manual

Page 1
Creating Advanced Components
Page 2
Trademarks
1 Step RoboPDF, ActiveEdit, ActiveTest, Authorware, Blue Sky Software, Blue Sky, Breeze, Breezo, Captivate, Central, ColdFusion, Contribute, Database Explorer, Director, Dreamweaver, Fireworks, Flash, FlashCast, FlashHelp, Flash Lite, FlashPaper, Flex, Flex Builder, Fontographer, FreeHand, Generator, HomeSite, JRun, MacRecorder, Macromedia, MXML, RoboEngine, RoboHelp, RoboInfo, RoboPDF, Roundtrip, Roundtrip HTML, Shockwave, SoundEdit, Studio MX, UltraDev, and WebHelp are either registered trademarks or trademarks of Macromedia, Inc. and may be registered in the United States or in other jurisdictions including internationally. Other product names, logos, designs, titles, words, or phrases mentioned within this publication may be trademarks, service marks, or trade names of Macromedia, Inc. or other entities and may be registered in certain jurisdictions including internationally.
Third-Party Information
This guide contains links to third-party websites that are not under the control of Macromedia, and Macromedia is not responsible for the content on any linked site. If you access a third-party website mentioned in this guide, then you do so at your own risk. Macromedia provides these links only as a convenience, and the inclusion of the link does not imply that Macromedia endorses or accepts any responsibility for the content on those third-party sites.
Copyright © 2005 Macromedia, Inc. All rights reserved. This manual may not be copied, photocopied, reproduced, translated, or converted to any electronic or machine-readable form in whole or in part without written approval from Macromedia, Inc. Notwithstanding the foregoing, the owner or authorized user of a valid copy of the software with which this manual was provided may print out one copy of this manual from an electronic version of this manual for the sole purpose of such owner or authorized user learning to use such software, provided that no part of this manual may be printed out, reproduced, distributed, resold, or transmitted for any other purposes, including, without limitation, commercial purposes, such as selling copies of this documentation or providing paid-for support services.
Acknowledgments
Writing: Stephen Gilson
Editing: Linda Adler
Production Management: Patrice O’Neill
Media Design and Production: Adam Barnett
First Edition: January 2005
Macromedia, Inc. 600 Townsend St.
San Francisco, CA 94103
Page 3
CONTENTS
About creating components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Writing the component’s ActionScript code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Simple example of a class file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Selecting a parent class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
About the component instantiation life cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Writing the constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Implementing the init() method. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Implementing the createChildren() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Implementing the commitProperties() method. . . . . . . . . . . . . . . . . . . . . . . . . 10
Implementing the measure() method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Implementing the layoutChildren() method. . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Implementing the draw() method. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Defining getters and setters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Defining MXML Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Embedding assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Handling events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
About invalidation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Compiling components. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Making components accessible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Adding versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Best practices when designing a component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Using the ModalText example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3
Page 4
4 Contents
Page 5
Creating Advanced Components
This article describes the details of creating advanced components for use in Macromedia Flex applications. The majority of the work is in writing the ActionScript class file, which derives from Flex existing classes, and adding your own custom functionality.
For an additional article on creating Flex components, including examples, see
www.macromedia.com/devnet/flex/articles/creating_comp.html.
About creating components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Writing the component’s ActionScript code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Compiling components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Making components accessible. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Adding versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Best practices when designing a component. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Using the ModalText example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

About creating components

Most new controls extend an existing class. If you want to create a component that is based on the Button control, for example, you can subclass the mx.controls.Button class. However, if you want to invent your own component, you will likely extend either the mx.core.UIComponent or mx.core.UIObject class. Choosing one of these base classes is discussed later, but Macromedia recommends that most custom components extend the UIComponent class rather than the UIObject class.
Use the following general process for creating a new Flex component in Flash:
1 If necessary, create symbols for skins or icons in Flash. 2 Create an ActionScript class file.
a Extend one of the base classes (UIObject or UIComponent) or another component. b Specify properties that the user can set using an MXML tag property. c Embed graphics and skins. d Implement the init() method.
5
Page 6
e Implement the createChildren() method. f Implement the commitProperties() method. g Implement the measure() method. h Implement the layoutChildren() method. i Implement the draw() method. j Add properties, methods, styles, events, and other metadata.
3 Compile a SWC file using compc.
The ordering of methods that you implement in this process mirrors that in the component instantiation life cycle. By understanding which methods are called and in what order, you can better understand how you write a component’s class file. For more information, see “About the
component instantiation life cycle” on page 8.
Each of the steps in this process is described in more detail in the remainder of this document.

Writing the component’s ActionScript code

A component’s ActionScript class extends another class, adds methods, adds getters and setters, and defines events and event handlers for the component. When you extend an existing component class, you can inherit from only one class. ActionScript 2.0 does not allow multiple inheritance.
To edit ActionScript class files, you can use any text editor or an Integrated Development Environment (IDE).

Simple example of a class file

The following is a simple example of a class file called MyComponent.as. This example contains a minimal set of imports, methods, and declarations for a component that inherits from the UIObject class.
//Import packages. import mx.core.UIComponent;
class MyComponent extends UIComponent {
// Define an empty constructor. function MyComponent() { }
// Override the init method, and call the parent’s init method. function init():Void {
super.init(); // Call invalidate() to display graphics. invalidate();
}
}

Selecting a parent class

Most components share some common behavior and functionality. Flex includes two base classes to supply this commonality: UIObject and UIComponent. By extending these classes, your components have a basic set of methods, properties, and events.
6 Creating Advanced Components
Page 7
Note: Macromedia recommends that you base your components on the UIComponent class rather than the UIObject class. The UIComponent class provides more built-in functionality, but maintains the flexibility of extending the UIObject class.
UIObject and UIComponent are the base classes of the component architecture. Understanding the principles at work in these two classes is important for building components.
The following table briefly describes the two base classes:
Class Extends Description
mx.core.UIComponent
mx.core.UIObject
UIObject UIComponent is the base class for all Flex components. It can
participate in tabbing, accept low-level events such as keyboard and mouse input, and be disabled so it does not receive mouse and keyboard input.
The UIComponent class lets you perform the following tasks:
Create focus navigation
Enable and disable components
Resize components
Macromedia recommends using the UIComponent class rather than the UIObject class as the base class for your custom components.
MovieClip UIObject is the base class for all graphical objects. It can have
shape, draw itself, and be invisible. The UIObject class lets you perform the following tasks:
Edit styles
Handle events
Resize by scaling
Macromedia does not recommend using the UIObject class rather than the UIComponent class as the base class for your custom components.

About the UIObject and UIComponent classes

Components based on version 2 of the Macromedia Component Architecture descend from the UIObject class, which wraps the MovieClip class. The MovieClip class is the base class for the classes in Flash that can draw on the screen. By providing a wrapper around its methods and properties, Flex makes the UIObject syntax more intuitive and improves the conceptual management of representing graphic objects.
The UIObject (mx.core.UIObject) class hides the mouse handling and frame handling in the MovieClip class. The UIObject class also defines the styles, skins, and event aspects of the component architecture. The UIObject class and its subclasses broadcast their events just before drawing. If you are familiar with Flash, this event is analogous to the
enterFrame() MovieClip
event. The UIObject class posts events to its listeners just before drawing when loading and unloading, and when its layout changes (
move, resize).
A UIObject class or UIObject subclass resizes itself by scaling. When you change its size using the
setSize() method, the new dimensions are passed to the _width and _height properties of the
MovieClip class, which scale the subclass.
Writing the component’s ActionScript code 7
Page 8
The UIComponent (mx.core.UIComponent) class is a subclass of the UIObject class. It defines high-level behaviors that are specific to a graphical object. The UIComponent class handles end­user interactions (such as clicking and focus) and component enabling and disabling. The UIComponent class inherits all the methods, properties, and events of the UIObject class.
Note: UIComponent also handles dragging, but you should not override the startDrag() method when defining custom components.

Extending other classes

To make component construction easier, you can extend a subclass for any class in the component architecture; you do not need to extend the UIObject or UIComponent class directly. For example, if you want to create a component that behaves almost the same as a Button component does, you can extend the Button class instead of recreating all the functionality of the Button class from the base classes. If you extend any other component’s class, you extend these base classes by default, because all components are subclasses of the UIComponent class, which is a subclass of the UIObject class.
However, if you only want to modify a Button’s behavior, it is simpler to extend the Button class as a custom MXML component. For more information, see Developing Flex Applications.

About the component instantiation life cycle

When you instantiate a new component, Flex calls a number of methods, and those methods call other methods that you can override. Instantiating a new component in your application triggers the following method calls by Flex:
1 Constructor
2 init() 3 createChildren() 4 commitProperties()
5 measure() 6 layoutChildren() 7 draw()
Each of the
commitProperties(), measure(), layoutChildren(), and draw() methods has a
corresponding invalidate method. For more information, see “About invalidation” on page 17.
The remaining sections describe each of these methods. For the purposes of initialization, you do not need to add any explicit calls to these methods, because Flex initiates each call.

Writing the constructor

Generally, component constructors should be empty so that the object can be customized with its properties interface. For example, the following code shows a constructor for MyComponent:
function MyComponent() { }
In this example, when a new component is instantiated, Flex calls the MyComponent() constructor.
You do not generally set properties in the constructor because the properties can be overridden by later method calls. For more information, see “Implementing the init() method” on page 9.
8 Creating Advanced Components
Page 9
Each class can contain only one constructor method; overloaded constructor methods are not supported in ActionScript 2.0.

Implementing the init() method

Flash calls the should call the superclass’s
init() method when the class is created. At a minimum, the init() method
init() method. The width and height of the component are not set
until after this method is called.
function init(Void):Void {
super.init(); invalidate();
}
Note: Do not create child objects in the properties.
init() method. You should use it only for setting up initial
This init() method calls the invalidate() method. The invalidate() method signals to Flex that just the visuals for the object have changed, but the size and position of subobjects have not changed. The
invalidate() method calls the draw() method.

Implementing the createChildren() method

Components implement the
createChildren() method to create subobjects (such as other
components) in the component. Rather than calling the subobject’s constructor in the
createChildren() method, you call the createClassObject() method to instantiate a
subobject of your component.
The
createClassObject() method has the following signature:
createClassObject(className, instanceName, depth, initObject)
The following table describes the arguments:
Argument Type Description
className
instanceName
depth
initObject
To c al l th e
createClassObject() method, you must know what those children are (for example,
Object The name of the class.
String The name of the instance.
Number The depth for the instance.
Object The object that contains the initialization properties.
a border or a button that you always need), because you must specify the name and type of the object, plus any initialization parameters in the call to
createClassObject().
The following example calls the createClassObject() method to create a new Label object for use inside a component:
createClassObject(Label, "label_mc", 1); // Create a label in the holder
You set properties in the call to the createClassObject() method by adding them as part of the
initObject argument. The following example sets the value of the label property:
createClassObject(CheckBox, "cb", 0, {label:"Check this"});
You can also pass style properties within the initObj argument, as the following example shows:
createClassObject(CheckBox, "cb", 0, {label:"Check this", fontSize : 14});
Writing the component’s ActionScript code 9
Page 10
Application performance is better when you pass style properties in the initObject argument instead of calling the
setStyle() method.
The following example creates TextInput and SimpleButton components:
function createChildren():Void {
if (text_mc == undefined)
createClassObject(TextInput, "text_mc", 0, { preferredWidth: 80,
editable:false });
text_mc.addEventListener("change", this); text_mc.addEventListener("focusOut", this);
if (mode_mc == undefined)
createClassObject(SimpleButton, "mode_mc", 1,
{falseUpSkin:modeUpSkinName, falseOverSkin: modeOverSkinName, falseDownSkin: modeDownSkinName });
mode_mc.addEventListener("click", this);
}
At the end of the createChildren() method, call the necessary invalidate methods (
invalidate(), invalidateSize(), or invalidateLayout()) to refresh the screen. For more
information, see “About invalidation” on page 17.

Implementing the commitProperties() method

Flex calls the variables that are used by the
commitProperties() method before it calls the measure() method. It lets you set
measure() method after the constructor has finished and MXML
attributes have been applied.
Flex only calls the
commitProperties() method after it calls the invalidateProperties()
method.
For example, the ViewStack container uses the performance. When you set the
ViewStack.selectedIndex property, the ViewStack container
does not display a new page right away. Instead, it privately stores a
commitProperties() method to maximize
pendingSelectedIndex
property. When it is time for Flash Player to update the screen, Flex calls the
commitProperties(), measure(), layoutChildren(), and draw() methods. In the commitProperties() method, the ViewStack container checks to see whether the pendingSelectedIndex property is set, and it updates the selected index at that time.
You use the
commitProperties() method to delay processing so that Flash Player avoids doing
computationally expensive, redundant work. For example, if a script changes the
ViewStack.selectedIndex property 15 times, you would want to minimize the number of
times the display of the ViewStack container updates when the
selectedIndex property changes.
By using the commitProperties() method, you can update the pendingSelectedIndex property 15 times, and then do the rendering only once.
This is most useful for properties that are computationally expensive to update. If setting a property is inexpensive, you can avoid using the
commitProperties() method.

Implementing the measure() method

Generally, Flex calls the
measure() method only once, when the component is instantiated. The
component’s container sets the initial size and Flex calculates the preferred minimum and maximum sizes. You can use the
measure() method to explicitly set the size of the component,
although Macromedia does not recommend doing this when developing components.
10 Creating Advanced Components
Page 11
You can set the following properties in the measure() method. Flex calculates the values of these properties, but you can override them in the
measure() method:
_measuredMinWidth
_measuredMaxWidth
_measuredMinHeight
_measuredMaxHeight
_measuredPreferredWidth
_measuredPreferredHeight
The properties define limits for when the object is resized. These measured properties are used for layout in containers if your component doesn’t explicitly set a
preferredHeight attribute.
Controls calculate the values of these based on runtime properties. For example, the Button control’s
_measuredPreferredWidth property.
Note: Although you can let Flex determine these values for you, your application startup time will decrease if you set them yourself.
measure() method examines how wide its label is in order to compute the value of the
By default, Flex sets the values of _measuredPreferredWidth and _measuredPreferredHeight to the values of the current height and width, but you should override them. The following example of the
measure() method from the Label component sets a default width and height if
the label text field is empty:
function measure(Void):Void {
var myTF = _getTextFormat(); var txt = text;
preferredWidth or
if (txt == undefined || txt.length < 2) {
txt = "Wj"; } var textExt = myTF.getTextExtent2(txt); var textW = textExt.width + 4; var textH = textExt.height + 4;
if (textW == undefined) {
textW = 20; }
if (textH == undefined) {
textH = 8; }
trace("Label: " + textW + " " + textH);
_measuredPreferredWidth = textW; _measuredPreferredHeight = textH;
}
Writing the component’s ActionScript code 11
Page 12

Implementing the layoutChildren() method

The
layoutChildren() method positions subobjects within the confines set by the
UIObject.layoutWidth and UIObject.layoutHeight properties of your component. Each
component should implement this method. Use the
size() method, which is used primarily by Macromedia Flash designers to perform the same
layoutChildren() method rather than the
function.
Use the
UIObject.width and UIObject.height properties of the child controls when changing
the size of the children. These properties are scaled for use inside the component. Use the
layoutWidth and layoutHeight properties when changing the size of the component itself.
These properties are not scaled, because the component is at the top level.
The
layoutChildren() method does not update the screen unless you call an invalidation
method. Flex only calls the
layoutChildren() method if the invalidateLayout() method was
previously called. For more information, see “About invalidation” on page 17.
The following example checks for the value of the
labelPlacement property and lays out the
mode_mc object accordingly:
function layoutChildren():Void {
text_mc.setSize(layoutWidth - mode_mc.width, layoutHeight);
if (labelPlacement == "left") {
mode_mc.move(layoutWidth - mode_mc.width, 0);
text_mc.move(0, 0); } else {
mode_mc.move(0, 0);
text_mc.move(mode_mc.width, 0); }
}

Implementing the draw() method

The
draw() method displays objects on the screen. Whenever the component needs to draw an
interface element, it calls a
draw() method. You use the draw() method to create or modify
elements that are subject to change.
Everything is made visible in the until its
draw() method is called. Any graphical assets that you bring in for the purposes of
draw() method. A border does not actually call the drawing API
measuring are invisible until the draw() method is called.
Do not call the
draw() method directly. Instead, call one of the invalidation methods, which then
calls the draw() method. Flex also calls the draw() method from the redraw() method. However, Flex calls the call an invalidation method if you want Flex to invoke the
redraw() method only if the object is invalidated, so you should actually
draw() or redraw() method. If you
do not call an invalidation method, the component remains invisible unless you set its visibility property to
Flex also calls the
12 Creating Advanced Components
true in MXML. For more information, see “About invalidation” on page 17.
draw() method after the layoutChildren() method.
Page 13
Inside the draw() method, you can use calls to the Flash drawing API, defined by the MovieClip class, to draw borders, rules, and other graphical elements. You can also call the which removes the visible objects. In general, to set lengths in the
this.layoutWidth and this.layoutHeight properties instead of width and height. For more
draw() method, you should use
clear() method,
information on the drawing API, see the MovieClip class in Flex ActionScript Language Reference.
The following example clears the component and then draws a border around the component:
function draw():Void {
clear(); if (bTextChanged) {
bTextChanged = false;
text_mc.text = text; } // Draw a border around everything. drawRect(0, 0, this.layoutWidth, this.layoutHeight);
}

Defining getters and setters

Getters and setters provide visibility to component properties and control access to those properties by other objects.
To define getter and setter methods, precede the method name with
get or set, followed by a
space and the property name. Macromedia recommends that you use initial capital letters for the second word and each word that follows. For example:
public function get initialColorStyle(): Number
The variable that stores the property’s value cannot have the same name as the getter or setter. By convention, precede the name of the getter and setter variables with two underscores (__). In addition, Macromedia recommends that you declare the variable as private.
The following example shows the declaration of
__initialColor, and getter and setter methods
that get and set the value of this property:
... private var __initialColor:Color = 42; ... public function get initialColor():Number {
return __initialColor;
} public function set initialColor(newColor:Number) {
__initialColor = newColor;
}
You commonly use getters and setters in conjunction with metadata keywords to define properties that are visible, bindable, and have other properties.

Defining MXML Properties

Your custom components are usable in MXML files. For example, if you define a component in the class called MyComponent in the ActionScript file MyComponent.as, you can reference it in MXML as the following example shows:
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*">
<MyComponent/>
</mx:Application>
Writing the component’s ActionScript code 13
Page 14
To make your components reusable in MXML, you can set component properties using tag attributes. For example, you might want to allow the user to pass a value to your component, as the following example shows:
<MyComponent prop1="3" />
To create components that take tag attributes in MXML, you define a variable with the same name in your class definition:
class MyComponent extends UIComponent {
var prop1:Number;
... }
Flex automatically initializes prop1 based on the value set in the MXML tag.
You can also add metadata to the variable definition. For example, if you are using Flex Builder, you can insert the Inspectable metadata tag to define the property as user-editable (or inspectable), as the following example shows:
[Inspectable(defaultValue="0")] var prop1:Number;
You might also want to generate an event when the property is modified. For example, if you use data binding, you can broadcast an event when the property is modified so that Flex can update anything bound to the property. The following example causes Flex to broadcast an event named changeProp1 when the property is modified:
[ChangeEvent("changeProp1")] var prop1:Number;
You can also instruct your component to generate an event when a getter or setter method is called, as the following example shows:
[ChangeEvent("change")] function get selectedDate():Date
In most cases, you set the change event on the getter function, and dispatch the event on the setter function using the
dispatchEvent() method. For more information on dispatching
events, see “Dispatching events” on page 15.
For more information on using metadata, see the chapter ‘Creating ActionScript Components’ in Developing Flex Applications.

Embedding assets

Some components use new skins or other graphical assets to define the look of the component. In these cases, you must embed the assets in the component. The source of the asset can be a file such as a JPEG or you can extract symbols from SWF files.
To embed assets that your component uses, use the
Embed metadata keyword. You precede a
variable declaration with the Embed statement. The variable then contains a reference to the embedded asset, which you use in the class.
The following example embeds two assets, arrowImageLarge and arrowImageSmall:
[Embed(source="arrowImageLarge.jpg")] var largeArrow:String; [Embed(source="arrowImageSmall.jpg")] var smallArrow:String;
14 Creating Advanced Components
Page 15
You add these keywords anywhere inside the class definition. For more information on embedding assets, see Developing Flex Applications.
To create a SWF file from which you embed assets, create a new FLA file and insert new symbols. For each symbol, select Export for ActionScript or place the symbols on the stage before saving the file as a SWF file.

Handling events

The event model is a dispatcher-listener model based on the DOM Level 3 proposal for event architectures. Every component in the architecture emits events in a standard format, as defined by the convention. Those events vary across components, depending on the functionality that the component provides.
Components generate and dispatch events and consume (listen to) other events. An object that wants to know about another object’s events registers with that object. When an event occurs, the object dispatches the event to all registered listeners by calling a method requested during registration. To receive multiple events from the same object, you must register for each event.
Although every component can define unique events, events are inherited from the core classes of the architecture, mx.core.UIObject and mx.core.UIComponent. These classes define low-level component events, such as
draw, resize, move, load, and others that are fundamental to all
components. Subclasses of these classes inherit and broadcast these events.

Dispatching events

In the body of your component’s ActionScript class file, you broadcast events using the
dispatchEvent() method. The dispatchEvent() method has the following signature:
dispatchEvent(eventObj)
The eventObj argument is the event object that describes the event. You can explicitly build an event object before dispatching the event, as the following example shows:
var eventObj = new Object(); eventObj.type = "myEvent"; dispatchEvent(eventObj);
You can also use the following shortcut syntax that sets the value of the type property for the event object and dispatches the event in a single line:
dispatchEvent({type:"myEvent"});
The event object has an implicit property, target, that is a reference to the object that triggered the event.
For each event that your custom component dispatches, you add an
Event metadata keyword
defining that event; for example:
[Event("myEvent")]
You add these keywords immediately before the class definition. If you do not identify an event in the class file with the
Event metadata keyword, the compiler ignores the event during
compilation, and Flex ignores this event triggered by the component during runtime.
Writing the component’s ActionScript code 15
Page 16
For example, you define a component called ModalText as the following example shows:
[Event("myEvent")]
class ModalText extends UIComponent {
...
}
Within the body of your class definition, you then use the dispatchEvent() method to dispatch
myEvent. You can then handle the event in MXML as the following example shows:
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*" >
<mx:Script>
<![CDATA[
function handleText(event) {
...
}
]]> </mx:Script>
<ModalText ... textChanged="handleText(event);" />
</mx:Application>
For more information on events, see Developing Flex Applications.

Defining event handlers for child components

Custom components implement the
createChildren() method to create subobjects (such as
other components) in the custom component. To create a subobject, you use the
createClassObject() method, as the following example shows:
function createChildren():Void {
createClassObject(TextInput, "text_mc", 0, {preferredWidth: 80,
editable:false});
text_mc.addEventListener("change", this);
}
This example creates a TextInput control as a child of your custom component. Note that the
createChildren() method also contains a call to the addEventListener() method to register
an event handler for the
change event generated by the TextInput control.
You can define the event handler function that listens for your events in your component’s ActionScript definition. The following example handles the
change, focusOut, and click events
of the children of the component:
function handleEvent(evt:Object):Void {
if (evt.type == "change") {
dispatchEvent({ type: "change" }); } else if (evt.type == "focusOut") {
text_mc.editable = false; } else if (evt.type = "click") {
text_mc.editable = !text_mc.editable; }
}
By default, Flex calls the handleEvent() method of a component whenever it generates an event.
16 Creating Advanced Components
Page 17

About invalidation

Macromedia recommends that a component not update itself immediately in most cases, but instead save a copy of the new property value, set a flag indicating what changed, and call one of the invalidation methods.
The following are the invalidation methods:
invalidateSize() Indicates that one of the _measured properties have changed. This results in a
call to the
_measured properties, it calls the layoutChildren() method.
invalidateLayout() Indicates that the position and/or size of one of the subobjects have
changed, but the
layoutChildren() method. If you change a subobject in the layoutChildren() method, call
the
invalidate() Indicates that just the visuals for the object have changed, but the size and position
of subobjects have not changed. This method calls the method from your implementation of the
invalidateProperties() Indicates that you have changed properties. This method calls the
commitProperties() method.
You should call the
measure() method. If the measure() method changes the value of one of the
_measured properties have not been affected. This results in a call to the
invalidate() method.
draw() method. You typically call this
init() method.
invalidateSize() method infrequently, because it measures and redraws
everything on the screen, and this can be a computationally expensive action. Sometimes you need to call more than one of these methods to force a layout, even though the computed sizes did not change.
You must call an invalidation method at least once during the instantiation of your component. The most common place for you to do this is in the
createChildren() or layoutChildren()
method.

Compiling components

When you finish writing the ActionScript classes and designing the embedded assets that comprise your component, you compile it into a SWC file using the compc utility.
The following example compiles the MyComponent component into the MyComponent.swc file:
compc –root .. –o MyComponent.swc MyComponent.as
After you generate a SWC file, you can use it in your Flex applications by copying it to the flex_app_root/WEB-INF/flex/user_classes directory. You can also copy SWC files to a directory specified by the top level of the user_classes directory or the directory specified by the <lib-path> element. You cannot store SWC files in subdirectories.
For more information on using the compc utility, see Developing Flex Applications.
<lib-path> child tag in the flex-config.xml file. SWC files must be stored at the
Compiling components 17
Page 18

Making components accessible

A growing requirement for web content is that it should be accessible to people who have disabilities. Visually impaired people can use the visual content in Flash applications by means of screen reader software, which provides an audio description of the material on the screen.
When you create a component, you can include ActionScript that enables the component and a screen reader to communicate. Then, when developers use your component to build an application in Flash, they use the Accessibility panel to configure each component instance.
Flash MX 2004 includes the following accessibility features:
Custom focus navigation
Custom keyboard shortcuts
Screen-based documents and the screen authoring environment
An Accessibility class
To enable accessibility in your component, add the following line to your component’s class file:
mx.accessibility.ComponentName.enableAccessibility();
For example, the following line enables accessibility for the MyButton component:
mx.accessibility.MyButton.enableAccessibility();
When developers add the MyButton component to an application, they can use the Accessibility panel to make it available to screen readers.

Adding versioning

When releasing components, you should define a version number. This lets developers know whether they should upgrade, and helps with technical support issues. When you set a component’s version number, use the static variable
static var version:String = "1.0.0.42";
If you create many components as part of a component package, you can include the version number in an external file. That way, you update the version number in only one place. For example, the following code imports the contents of an external file that stores the version number in one place:
#include "../myPackage/ComponentVersion.as"
The contents of the ComponentVersion.as file are identical to the previous variable declaration, as the following example shows:
static var version:String = "1.0.0.42";
version, as the following example shows:

Best practices when designing a component

Use the following practices when you design a component:
Keep the file size as small as possible.
Make your component as reusable as possible by generalizing functionality.
Use the Border class rather than graphical elements to draw borders around objects.
Use tag-based skinning.
18 Creating Advanced Components
Page 19
Assume an initial state. Because style properties are on the object, you can set initial settings for
styles and properties so your initialization code does not have to set them when the object is constructed, unless the user overrides the default state.
When defining the symbol, do not select the Export in First Frame option unless it is
absolutely necessary. Flash loads the component just before it is used in your Flash application, so if you select this option, Flash preloads the component in the first frame of its parent. The reason you typically do not preload the component in the first frame is for considerations on the web: the component loads before your preloader begins, defeating the purpose of the preloader.
Always implement an init() method and call the super.init() method, but otherwise keep
the component as lightweight as possible.
Use the invalidate() and invalidateStyle() methods to invoke the draw() method
instead of calling the
draw() method explicitly.

Using the ModalText example

The following code example implements the class definition for the ModalText component. The ModalText component is a text input whose default is the noneditable mode, but you can switch to editable mode by clicking its button.
Save the following code to the file ModalText.as, and then compile it into a SWC file using the compc utility.
// Import all necessary classes. import mx.core.UIComponent; import mx.controls.SimpleButton; import mx.controls.TextInput;
// ModalText sends a "change" event when the text of the child TextInput
control changes, a "textChanged" event when you set the text property of ModalText, and a "placementChanged" event when you change the labelPlacement property of ModalText.
[Event("change")] [Event("textChanged")] [Event("placementChanged")]
/*** a) Extend UIComponent. ***/ class ModalText extends UIComponent {n
/*** b) Implement init(). ***/ function init():Void {
super.init();
invalidate(); }
/*** c) Implement createChildren(). ***/ // Declare two children member variables. var text_mc:TextInput; var mode_mc:SimpleButton;
/*** d) Embed new skins used by the SimpleButton component. You can create a SWF file that contains symbols with the names ModalUpSkin, ModalOverSkin, and ModalDownSkin. ***/ [Embed(source="Modal2.swf", symbol="ModalUpSkin")] var modeUpSkinName:String;
Using the ModalText example 19
Page 20
[Embed(source="Modal2.swf", symbol="ModalOverSkin")] var modeOverSkinName:String;
[Embed(source="Modal2.swf", symbol="ModalDownSkin")] var modeDownSkinName:String;
// Note that we test for the existence of the children before creating them. // This is optional, but we do this so a subclass can create a different // child instead. function createChildren():Void {
if (text_mc == undefined)
createClassObject(TextInput, "text_mc", 0, { preferredWidth: 80,
editable:false });
text_mc.addEventListener("change", this);
// Identify skin states and apply embedded assets to those states using
// the createClassObject() method:
if (mode_mc == undefined)
createClassObject(SimpleButton, "mode_mc", 1,
{ falseUpSkin: modeUpSkinName, falseOverSkin: modeOverSkinName,
falseDownSkin: modeDownSkinName });
mode_mc.addEventListener("click", this);
}
/*** e) Implement the measure() method. ***/ // The default width is the size of the text plus the button. // The height is dictated by the button. function measure():Void {
_measuredPreferredWidth = text_mc.preferredWidth +
mode_mc.preferredWidth;
_measuredPreferredHeight = mode_mc.preferredHeight; }
/*** f) Implement the layoutChildren() method. ***/ // Place the button depending on labelPlacement and fit the text in the // remaining area. function layoutChildren() {
text_mc.setSize(width - mode_mc.width, height);
if (labelPlacement == "left") {
mode_mc.move(width - mode_mc.width, 0);
text_mc.move(0, 0); } else {
mode_mc.move(0, 0);
text_mc.move(mode_mc.width, 0); }
}
/*** g) Implement the draw() method. ***/ // Set flags when things change so we only do what we have to. private var bTextChanged:Boolean = true;
// Set text if it changed, and draw a border. function draw():Void {
clear(); if (bTextChanged) {
bTextChanged = false;
text_mc.text = text; }
20 Creating Advanced Components
Page 21
// Draw a simple border around everything. lineStyle(1,0x000000,100); drawRect(0, 0, width, height);
}
/*** h) Add methods, properties, and metadata. ***/ // The general pattern for properties is to specify a private // holder variable. private var __labelPlacement:String = "left";
// Create a getter/setter pair so you know when it changes. [ChangeEvent("placementChanged")] [Inspectable(defaultValue="left", enumeration="left, right")] function set labelPlacement(p:String) {
// Store the new value. __labelPlacement = p; // Add invalidateSize(), invalidateLayout(), or invalidate(), depending on // what changed. You may call more than one if you need to. invalidateLayout(); dispatchEvent({type:"placementChanged"});
}
function get labelPlacement():String {
return __labelPlacement;
}
private var __text:String = "ModalText";
[ChangeEvent("textChanged")] [Inspectable(defaultValue="ModalText")] function set text(t:String) {
__text = t; bTextChanged = true; invalidate(); dispatchEvent({type:"textChanged"});
}
function get text():String {
return text_mc.text;
}
// Handle events that are dispatched by the children. function handleEvent(evt:Object):Void {
if (evt.type == "change") {
dispatchEvent({ type: "change" }); } // If button is clicked, toggle the editable property of the text box. else if (evt.type = "click") {
text_mc.editable = !text_mc.editable; }
}
}
The following is an example MXML file that instantiates the ModalText control and sets the labelPlacement property to "left":
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns=”*”>
<ModalText labelPlacement="left"/>
</mx:Application>
Using the ModalText example 21
Page 22
You can also handle the textChanged event if you want to determine when the ModalText.text property is modified, as the following example shows:
<mx:Script>
<![CDATA[
function handleText(event)
{
myText.text = "ModalText.text changed";
} ]]>
</mx:Script>
<mx:TextArea id="myText" /> <ModalText labelPlacement="left" textChanged="handleText(event);" />
Note that to trigger the event, you have to modify the ModalText.text property directly; entering text into the TextInput control does not trigger the event.

Troubleshooting

This section describes some common problems and their solutions when creating components for Flex in Flash.
I get an error "don't know how to parse ..." when I try to use the component from MXML.
This means that the compiler could not find the SWC file, or the contents of the SWC file did not list the component. Ensure that the SWC file is in a directory that Flex searches, and ensure that your directory as the MXML file and setting the namespace to "*" as the following example shows:
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*">
I get an error "xxx is not a valid attribute ..." when I try to use the component from MXML.
Ensure that the attribute is spelled correctly. Also be sure that it is not private.
I don't get any errors, but nothing shows up.
Verify that the component was instantiated. One way to do this is to put a Button control and a TextArea control in the MXML application and set the component when the button is clicked. For example:
<!-- This verifies whether a component was instantiated. --> <zz:mycomponent id="foo" /> <mx:TextArea id="output" /> <mx:Button label="Print Output" click="output.text = foo" />
I tried the verification test and I got nothing or "undefined" in the output.
This means that one of your dependent classes was either not loaded or was loaded too late. Print various classes to the output to see whether they are being created. Any components created with the your component symbol.
The component is instantiated properly but does not show up (#1).
In some cases, helper classes are not ready by the time your component requires them. Flex adds classes to the application in the order that they need to be initialized (base classes, and then child classes). However, if you have a static method that gets called as part of the initialization of a class, and that static method has class dependencies, Flex does not know to place that dependent class before the other class, because it does not know when that method is going to be called.
xmlns property is pointing to the right place. Try moving the SWC file to the same
.text property to the ID for the
createClassObject() method as subcomponents of your component must be placed in
22 Creating Advanced Components
Page 23
One possible remedy is to add a static variable dependency to the class definition. Flex knows that all static variable dependencies must be ready before the class is initialized, so it orders the class loading correctly.
The following example adds a static variable to tell the linker that class A must be initialized before class B:
class mx.example.A {
static function foo():Number {
return 5;
}
}
class mx.example.B {
static function bar():Number {
return mx.example.A.foo();
}
static var z = B.bar(); // Dependency static var ADependency:mx.example.A = mx.example.A;
}
The component is instantiated properly but does not show up (#2)
Verify that the nonzero. If they are zero, ensure that you implemented the
_measuredPreferredWidth and _measuredPreferredHeight properties are
measure() method correctly.
.
Sometimes you have to ensure that subobjects are created in order to get the correct measurements.
You can also check the values of the code might have set those values to zero. If so, set a breakpoint in the setters for the
height, preferredWidth, and preferredHeight properties to see what is setting them.
You can also verify that the
_visible property is an internal property used by Flash Player. If visible=true and _visible=false, ensure that your component called the invalidate() method in its measure() or layoutChildren() methods.
visible property and the _visible property are set to true. The
The system does not call the must call the
invalidate() method at least once, because their layout changes as they are given
preferredWidth and preferredHeight properties. Other
width,
invalidate() method unless you explicitly do so. All components
their correct size during the layout process.
The component is instantiated properly but does not show up (#3).
It is possible that there is another class or SWC file that overrides your custom class or the symbols used in your component. Check the ActionScript classes and SWC files in the flex_app_root/WEB-INF/flex/user_classes directory to ensure that there are no naming conflicts.
Troubleshooting 23
Page 24
24 Creating Advanced Components
Loading...