March 27, 2008
itemRenderers: Part 4: States and Transitions
Communicating the with the user of your application is what your itemRenderer does best. Sometimes that communication is as simple as presenting a name; sometimes more elborately using colors; and sometimes with interactivity.
itemEditors are truely interactive controls, but they are not the focus of this article. In these examples we'll look at itemRenderers that change their visual appearance based on either the data itself or the user's actions.
States
The Flex <mx:State> is a very good way to change the appearance of an itemRenderer. States are easy to use, and when combined with Transitions, give the user feedback and pleasent experience.
In this example we'll create a new MXML itemRenderer (and remember, you can do this completely in ActionScript if you prefer) for the List. All this shows is the image, title, author, price, and a Button to purchase the book.
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Image id="bookImage" source="{data.image}" />
<mx:VBox height="115" width="100%" verticalAlign="top" verticalGap="0" paddingRight="10">
<mx:Text text="{data.title}" fontWeight="bold" width="100%"/>
<mx:Label text="{data.author}" />
<mx:HBox id="priceBox" width="100%">
<mx:Label text="{data.price}" width="100%"/>
<mx:Button label="Buy" />
</mx:HBox>
</mx:VBox>
</mx:HBox>
What we want however, is if the book is not in stock (the data has <instock> nodes which are either yes or no) for the price and Book to be invisible. I've made things a bit convenient for myself here because I gave the HBox parent of the price and Button an id. This allows me to change the visibility of both of those items by changing the visibility of the HBox, priceBox.
You can do this by overridding the set data function, which we'll do, but instead of directly changing the visibility of priceBox, we'll use this state defintion:
<mx:states>
<mx:State name="NoStockState">
<mx:SetProperty target="{priceBox}" name="visible" value="false"/>
</mx:State>
</mx:states>
Place this just below the root tag.
This example is a bit far-fetched in that it is overly complicated to do a simple task, but it shows how to use states. There are 2 states:
- The base state - this is the normal state of a component. Components that do not use states simply have this base state. In this example, the base state has the priceBox visible property as true (the default). This is the case when instock is "yes".
- The NoStockState - this is the state when the value of nostock is "no". When this state is active the SetProperty instructions are carried out. The target determines which member of the class is in question, the name property is the name of the property to change on the target, and value is the new value for the property.
The set data function determines which state to use by looking at the value of instock:
override public function set data( value:Object ) : void
{
super.data = value;
if( data )
{
if( data.instock == "yes" )
currentState = "";
else
currentState = "NoStockState";
}
}
The currentState is a property of all UIComponent controls. It determines which State is the active one. When switching between states the Flex framework begins with the base state and then applies the rules for the given state.
Remember that itemRenderers are recycled, so you must always restore values; never leave an if without an else in an itemRenderer.
If you are feeling adventurous, you can do away with the set data override in this example. Instead, set currentState directly in the root tag by using data binding expression:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400"
currentState="{data.instock == 'yes' ? '' : 'NoStockState'}" >
The currentState's value is set by examining data.instock right inline with the root tag. A nice trick, but it might be harder to maintain.
Adding Elements
In this itemRenderer the price and Buy button appears only if the instack value is yes. You could do this without a state of course, but if your itemRenderer has more controls to be added or removed, a state will make more sense as their appearance is controlled simply by setting the itemRenderer's currentState property.
Instead of just removing the price and Button, we'll have the state add a label telling the user the item is out of stock. Here's the modified state:
<mx:states>
<mx:State name="NoStockState">
<mx:SetProperty target="{priceBox}" name="visible" value="false"/>
<mx:AddChild relativeTo="{priceBox}" position="before">
<mx:Label text="-- currently not in stock --" color="#73DAF0"/>
</mx:AddChild>
</mx:State>
</mx:states>
The <mx:AddChild> tag says to add the Label into the priceBox. In addition to setting the priceBox's visible property to false, a friendly message replaces it.
Again, you could add this label in the set data function - or add it initially and just set its visibility to false and change it to true in the set data function. But I think you can see the value of the State: if the requirement for the instock being no condition gets more complex, all you need to do is adjust the NoStockState; the ActionScript code which switches the state remains the same.
You can modify states in Flex Builder's Design View.
Expanding List
This example does not work well for list controls but does perform nicely for a VBox and Repeater. This question of expanding an item in place becomes dicy when the list has to be scrolled. Imagine this: you've got a list of items will all the same height. Now you exand the height of item 2. So far so good - item 2 is taller than the other visible items. And there's the catch: the visible items. Now scroll the list. Remember that itemRenderers are recycled. So when item 2 scrolls out of view, its itemRenderer will be moved to the bottom of the list. You've got to reset its height. OK, that can work pretty simply. Now scroll the list so item 2 is back in view. You would expect it to be the expanded height. How does the itemRenderer know to do that? From previous articles you know that information either comes from the data itself or from some external source.
I think a resizing itemRenderer is too complex and not really worth the effort. I believe there is a better way to do this using VBox and Repeater. However, the catch with Repeater is that every child will be created. If you have 1,000 records and use a Repeater you will get 1,000 instances of your itemRenderer.
For this example you'll still write an itemRenderer but will use it as the child of a VBox. The elements of a list look pretty simple: the name of a book and its author. But click the itemRenderer and it expands in place. This is accomplished using:
- The itemRenderer has a state which includes the additional information.
- The itemRenderer uses a Resize transition to give a smoother expansion and contraction of the itemRenderer.
The base state of the itemRenderer is pretty simple:
<mx:HBox width="100%">
<mx:Label text="{data.author}" fontWeight="bold"/>
<mx:Text text="{data.title}" width="100%" fontSize="12" selectable="false"/>
</mx:HBox>
The ExpandedState adds the additional elements which contribute to the itemRenderer's height:
<mx:states>
<mx:State name="ExpandedState">
<mx:AddChild position="lastChild">
<mx:HBox width="100%">
<mx:Image source="{data.image}"/>
<mx:Spacer width="100%"/>
<mx:Label text="{data.price}"/>
<mx:Button label="Buy"/>
</mx:HBox>
</mx:AddChild>
</mx:State>
</mx:states>
Getting the itemRenderer to change size is as simple as adding a Transition:
<mx:transitions>
<mx:Transition fromState="*" toState="*">
<mx:Resize target="{this}" />
</mx:Transition>
</mx:transitions>
Place this below the <mx:states>
The Transition is applied whenever the state changes because its fromState and toState properties are wildcards. Now all you have to do is add event handler for clicking on the itemRenderer (add a click event to the root tag) and change the state:
<mx:Script>
<![CDATA[
private function expandItem() : void
{
if( currentState == "ExpandedState" )
currentState = "";
else
currentState = "ExpandedState";
}
]]>
</mx:Script>
Summary
States are a great way to make a number of modifications to the visual appearance of the itemRenderer. You can group the changes in a State and simply make it all happen by setting the currentState property of the itemRenderer.
In the next article we'll look at writing more efficient itemRenderers by extending UIComponent.
Posted by pent at 04:22 PM | Comments (1)
February 06, 2008
creationPolicy vs Binding
I just read a comment on an article I wrote a few years ago, creationPolicy vs. Form Data. The commenter suggested that the Save button must be able to read the values from the controls, even if those controls haven't been created - probably to make the code easier to write or at least consistent.
I disagree with this, but after reading that article again, I see that I could have given more information to support my position.
The article suggests using a <mx:Model> to hold the data and then use data binding to transfer the information from the model to the UI controls. That's fine for presenting the information, but it doesn't help when you to save the information.
ActionScript 3 Approach
Here's a different approach that works better with ActionScript 3 (for Flex 2 or Flex 3). Let's suppose you have an Accordion with two forms: Payment Information and Shipping Address. Let's also suppose you have two ActionScript classes that correspond to these forms: PaymentInfo and ShippingAddress. Here's the definition of the PaymentInfo class:// file: PaymentInfo.as
package mydata
{
[Bindable]
public class PaymentInfo
{
public var cardType:String;
public var cardNumber:String;
public var expireDate:Date;
}
}
Here's the definition of the ShippingAddress class:
// file: ShippingAddress.as
package mydata
{
[Bindable]
public class ShippingAddress {
public var firstName:String;
public var lastName:String;
public var street:String;
public var city:String;
public var state:String;
public var zipCode:String;
}
}
These look like ordinary ActionScript classes, but notice the [Bindable] meta tag above the class definition? That tag tells the compiler that all public properties can be used in data-binding. It is a short-cut to putting [Bindable] before each public variable.
In the Form to be used for the Payment Information you may have controls like this one to get the credit card number:
<mx:TextInput id="creditCardNumber" />
Since you want the PaymentInfo's cardNumber property to appear in the TextInput, you can use data-binding to associate the property with the field:
<mx:TextInput id="creditCardNumber" text="{data.cardNumber}" />
That's great for showing the credit card number if the value already exists. But how about saving that information?
Binding Tag
What you want to do is a "reverse" data-binding. Right now the {data.cardNumber} binding is one-way: whatever appears in the PaymentInfo's cardNumber property appears in the TextInput control. What you'd like is that if you change the value in the UI control to be automatically copied back into the PaymentInfo.You can do this by using the <mx:Binding> tag. Here's how to do that for the card number:
<mx:Binding source="creditCardNumber.text" destination="data.cardNumber" />
Any change made to the text property of the creditCardNumber TextInput control is reflected back to the cardNumber property of the PaymentInfo class instance for this Form.
Note: You can also do this type of binding in ActionScript using the mx.binding.utils.BindingUtils class.
Example
Picture this: the Payment Information form appears (blank). You fill in the fields and pick Save. The Save button's code does not have to access the UI controls at all: it saves the contents of the PaymentInfo class because the Binding took care of transferring the data from the control back to the data class.<mx:Script><![CDATA[
[Bindable] private var payment:PaymentInfo; // remember to create an instance using the new operator
[Bindable] private var shipping:ShippingAddress; // remember to create an instance using the new operator
]]></mx:Script>
<mx:Accordion>
<forms:PaymentInfoForm data="{payment}" />
<forms:ShippingForm data="{shipping}" />
</mx:Accordion>
Now suppose the Shipping Address were used in the same way, but in this case, the user never opened that form. Since the Save button code is not accessing any of the UI controls on the form, only the ShippingAddress properties, it doesn't matter if the UI controls were ever created.
In the above code example, the Binding tags will transfer changes to the controls in the PaymentInfoForm to the payment variable. Changes to the controls in the ShippingForm (if created by the user visiting it in the Accordion) will likewise be transferred to the shipping variable.
The Save button code then saves the contents of the payment and shipping variables.
Summary
Using the Binding tag to "reverse" data-binding on editable controls will make your code easier to write, understand, and more reliable. You won't have to worry if user's never touch containers on controls like Accordion or TabNavigator. And if the user does visit a form, data binding will populate the UI controls and Binding will read them.And yes, if you have 100 UI controls and fields you will need 100 Binding tags.
Posted by pent at 08:50 AM | Comments (9)
January 04, 2008
Using SQL with Adobe AIR
I've not been happy about the performance of my Atmospheres Music Player. I installed it on my home computer where I have a network disk that holds all my CDs - about 240 of them. When I pointed Atmospheres at the network drive, it took a long time for it to read all of the music information from the disk. I had taken steps to make the program responsive while the files were being read, but still, it took a long time. I told my teammate, Kyle Quevillon, about this and he suggested that I cache the information much like other music players do.
What he meant was that Atmospheres shouldn't read the file system, especially one on a remote disk, as its primary source of information in the music library. A light went off in my head when I realized I could use the SQL Lite database built into Adobe AIR. This was a perfect application of the database: I could have Atmospheres read the file system and store information into a local database, reading that information whenever it started. I'd only read the file system when new CDs were ripped.
If you didn't know it already, AIR comes with a genuine, lightweight, database - an implementation of SQL Lite. You can create tables, views, insert data, delete data, and run queries. The API to do this is the flash.data package. If you are interested in using the SQL capabilities of AIR, then read on.
Here's what I did:
I created a class to read the file system and build the Album objects. The Album class already existed and in fact, the code to read the file system already existed, too, in the MusicLibraryViewer class. I created a new class (FileReader) which creates the Album objects and notifies another class once an album has been read.
The other class is the Cache. One function of the Cache class is to take the information from the FileReader (ie, Albums) and store them into a database. Another function is to read the database and build a list of Albums from that information.
Atmospheres starts by getting the Cache to read the database and build the Album list. This is presented in the Atmosphere's user interface. At any time, especially the first time you run Atmospheres, you can refresh the music library. Doing this causes the FileReader to create an Album which the Cache compares to what it has read from the database. Anything new is put into the database as well as in memory.
Scanning the file system isn't lightning fast, but when you are expecting it to be slow, it moves along quickly enough and builds the local database. Reading the database is, in my opinion, astonishingly fast. I've got about 2,800 tracks from those CDs stored in the local database. It reads that data very quickly and builds the Album list in an eye-blink.
Download
You can download the code here. This is a Flex 3 Beta 3 project archive. If you do not hav Flex 3 Beta 3, find it here.
Now when Atmospheres starts on my home computer, it comes up right away, showing all 240 albums and is ready to play any of them.
Posted by pent at 06:36 PM | Comments (1)
December 24, 2007
Component Pack from ILOG
I've been asked a number of times if there are more chart types, as well as other controls, available for Flex. Perhaps I'm late to the party, but I just came across this announcement (from October 2007) that Adobe and ILOG are teaming up to enhance Flex 3.
Here's a quick list of what's available in the ILOG ELIXR package. You can find out more on Adobe Labs: http://labs.adobe.com/wiki/index.php/Flex_3:ILOG
- Radar charts (also named spider charts)
- Full 3D charts including bar/column, area, line and pie
- Treemap component, for analyzing large data sets
- Scheduling component, allowing users to view and manipulate time series data
- Organizational charts
- Country maps for creating interactive reports or dashboards
Posted by pent at 11:07 AM | Comments (1)
December 20, 2007
Component Class - Part Five
The last article in this series showed how to write the CycleSelectButton from scratch. In this article I'll look at styling and skinning the component.
Skins versus Styles
One frequently asked question is "what's the difference between styles and skins?" This is a good question and it is confusing a bit because you specify a component's skins using specific styles on the component. For example, the upSkin style on the Button component.
Styles control the appearance of a component while skins are the appearence. Or put another way, skins use styles to present the component. Take the borderColor style. It's purpose is to specify the color of a component's edge. The component's skin can use that style to draw its border - which may be round or square, thick or thin (using the borderThickness style). The skin contains the look of the component.
The reason skins are specified as style is so you can build an entire look or theme using just style sheets (.CSS files). If skins were specified in ActionScript you would have to deliver a new SWF for each theme. Having the skins and other styles in CSS means you can change the theme of an application with a new style sheet.
There are two types of skins: graphical and programmatic. Graphical skins are bitmaps: GIFs, JPGs, PNGs, etc. In Flex 3 you can import graphical skins from Adobe Illustrator and Adobe Photoshop. You can use Adobe Flash CS3 to create animated skins (picture a button that pulses with color).
Programmatic skins are written in ActionScript. They are class files that usually extend mx.skins.ProgrammaticSkin which is a very lightweight class. The class uses its override of updateDisplayList() to render the skin using the drawing API (see flash.display.Graphics).
Each component has its own set of skins. For a Button there are 8 possible skins: upSkin (its normal state), overSkin (when the mouse hovers over it), downSkin (when the mouse is pressed over it), disabledSkin (when the Button's enabled property is false), selectedUpSkin (when the Button's toggle property is true), selectedOverSkin (toggle is true and mouse is hovering over the Button), selectedDownSkin (toggle is true and mouse is pressed over it), and selectedDisabledSkin (toggle is true and enabled is false). If you want to use graphical skins for a Button, you should supply 8 different image files. If your Button will never be a toggle, then you can supply just 4 skins.
Download Example
This is a zip file and contains a full Flex Builder 3 project. You will either need Flex Builder 3 from Adobe Labs or you can use Flex Builder 2 and import the sources into a project. This project contains the source from the previous articles as well.
If you decide to use a programmatic skin you can either make separate skin classes, or use a single class, or a combination. A programmatic skin can detect which skin style it is being used for and code within the programmatic skin class can adjust for it. If, for example, the skin class is being used for a Button's upSkin, overSkin, downSkin, and disabledSkin, the class can decide to draw a green-filled circle for the upSkin, a blue-filled circle for the overSkin, a blue-filled circle for the downSkin, and a gray-filled circle for the disabledSkin.
You decide what works best for the look you want. You can wind up with a collection of skins - both graphical and programmatic - that make your application look unique (or follow your company's user interface guidelines).
Applying Skins
Applying the skins is simple. I prefer to do it in a style-sheet to make them easier to change:
Button {
upSkin: Embed('assets/BlueButtonUp.gif');
overSkin: Embed(source='assets/CompanyIcons.swf',symbol='GreenButton');
downSkin: ClassReference('com.mycompany.skins.StandardButtonSkin');
disabledSkin: ClassReference('com.mycompany.skins.StandardButtonSkin');
}
This pretty wild Button has a mixture of skins: one is a GIF, another is a symbol out of a SWF, and two come from the same ActionScript class.
Specifying a graphical skin uses the Embed directive. For a simple image file the Embed names the file using a path that is relative to the application's main file, or an absolute path within the project. The example above uses a relative path. When a Flash SWF is used, the skin can be the entire SWF file or a specific symbol within the SWF. If you chose to use a specific symbol, the Embed directive names the file and the symbol within it.
Specifying a programmatic skin uses the ClassReference directive. The full class name, including its package, is given for the reference. The compiler will find that class and pull it into the SWF.
The main advantage of programmatic skins over graphical skins is scaling. Because programmatic skins use the Flash Drawing API, the skins scale and rotate very well. Graphical skins can easily become distorted unless you scale9grid specifications in the Embed directive. The scale9grid specifications let you specify a grid overlay on the graphic that tells the Flash Player which parts of the graphic to scale. Think of a rectangle where you want the 4 corners to never scale, the top and bottom to scale only when the graphic is stretched horizontally, the left and right edges to scale when the graphic is stretched vertically, and the center to always scale.
Going back to the CycleSelectButton component, here is how the createChildren() function looks currently:
override protected function createChildren() : void
{
arrows = new Arrows();
arrows.width = 20;
arrows.height= 20;
addChild(arrows);
linkButton = new LinkButton();
addChild(linkButton);
// add a listener for the click on the LinkButton.
linkButton.addEventListener(MouseEvent.CLICK, handleClick);
super.createChildren();
}
To redo this component using skins, you have to think about which parts of the component should be skinable. It seems like a good idea for the circle of arrows to be a skin. Maybe you want to make your own arrows using Photoshop, for instance.
Here is the modified createChildren() function that introduces skins:
var skin:Class;
skin = getStyle("arrowSkin");
if( skin == null ) skin = CycleSelectArrowSkin;
_arrowSkin = new skin();
_arrowSkin.name = "arrowSkin";
if( _arrowSkin is ProgrammaticSkin ) (_arrowSkin as ProgrammaticSkin).styleName = this;
_arrowSkin.width = 20;
_arrowSkin.height= 20;
addChild(_arrowSkin as DisplayObject);
This is a bit different. First, the value for the arrowSkin style is retrieved. The arrowSkin style is specified by metadata above the class declaration:
[Style(name="arrowSkin",type="Class",inherit="yes")]
Notice that the type of the style data is "Class" - you want to load the class definition for the skin, not just the name of the class. This works for Embed as well since a class is created from the embedded image data.
If no arrowSkin style has been specified, then the default class, CycleSelectArrowSkin, is given. Then the arrowSkin member variable is set with a new instance of whatever skin class was selected. This is the standard way to specify skins using styles.
Once the class instance has been created and arrowSkin is now set, you'll see it is given a name ("arrowSkin") and its style is set to this. What it means is that the skin will get all of the styles set on the component. For example, the CycleSelectArrowSkin uses a style called "arrowColor" to draw the arrow graphic. There isn't any way from outside of the CycleSelectButton code to associate this style with the arrow skin; the style is set on the component, along with the arrowSkin style shown above:
[Style(name="arrowColor",type="Number",format="Color",inherit="yes")] [Style(name="arrowSkin",type="Class",inherit="yes")]
With the skin inheriting the component's style, arrowColor among them, the skin code can draw the arrows.
Details
In this section I go through the steps in more detail . I'll use the sample CycleSelectButton available from the download with this article, but I will only show the skin for the arrows; the skins for the rest of the component work the same way and it will be less confusing to focus on one skin.
Step 1: Figure out what you want the skin to be used for. In this case, it is for the cycle of arrows and by making it a skin, gives a developer the chance to change the look of the component without re-writing the component.
Step 2: In the component class file (CycleSelectButton.as), define the style for the skin above the class definition:
[Style(name="arrowSkin",type="Class",inherit="yes")]
public class CycleSelectButton extends UIComponent
{
Make sure the type of the style is "Class". The name will be used in the style sheet or on the MXML tag for the component:
StyleSheet.css:
CycleSelectButton {
arrowSkin: ClassReference('com.adobe.examples.skins.CycleSelectArrowSkin');
or
arrowSkin: Embed('assets/ArrowSkin.png');
}
MXML:
<buttons:CycleSelectButton arrowSkin="com.adobe.examples.skins.CycleSelectArrowSkin"... />
or
<buttons:CycleSelectButton arrowSkin="@Embed('assets/ArrowSkin.png')" ... />
Step 3: Declare a member variable to hold the skin instance:
private var _arrowSkin:IFlexDisplayObject;
Notice that the type of the variable is IFlexDisplayObject - not CycleSelectArrow skin, not UIComponent, and not even ProgrammaticSkin. If you want your skin to be either a programmatic skin or a graphic skin, you need to use a data type that is common to both. IFlexDisplayObject fills that need. It is generic enough, but also allows you to position and size the skin.
Step 4: Create the skin. You can do this either in createChildren() or in commitProperties().
var skin:Class;
skin = getStyle("arrowSkin");
if( skin == null ) skin = CycleSelectArrowSkin;
_arrowSkin = new skin();
_arrowSkin.name = "arrowSkin";
if( _arrowSkin is ProgrammaticSkin ) (_arrowSkin as ProgrammaticSkin).styleName = this;
_arrowSkin.width = 20;
_arrowSkin.height= 20;
addChild(_arrowSkin as DisplayObject);
The getStyle() function is used to get an alternative skin class (ProgrammaticSkin or graphic) from the styles for the component. This is how a custom skin can be used from a style sheet or MXML tag (from Step 2 above). If no skin was specified getStyle() returns null. In this case a default skin is used. It is important that when using skins you are consistent and create a default skin; creating a skin as a default is always a good idea and perhaps it too can be extended and customized.
Once the skin class is chosen, the arrowSkin member (from Step 3) is set with an instance of this class. Now it is either a graphic skin or a ProgrammaticSkin. If the latter you must set the styleName of the skin to be this (or some other object instance which holds the styles). If you don't do this, the ProgrammaticSkin will fail when it uses getStyle().
You can size the skin at this step IF you know the size. If your skin is going to occupy the entire component's space, you can set it within updateDisplayList() (see Step 5).
Finally you add the skin as a child of the component. Note that you have to cast the skin as a DisplayObject since addChild does not accept IFlexDisplayObject parameters.
Step 5: Position (and optionally, size) the skin in updateDisplayList():
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
// position the arrowSkin
_arrowSkin.move( 10,10 );
Here the arrowSkin is moved into position. If you were to have a skin that required it to be sized, then you can do that too using skin.setActualSize( width, height ) where the width and height might be unscaledWidth, unscaledHeight or some derivative of those values.
That's all you need to do to use a skin in your component. Notice that none of the component's look has been done by the actual component code - it is all done by the skin. This gives your component a tremendous amount of flexability in how it is presented, not in how it behaves.
The Skin Itself
The CycleSelectArrowSkin is one of the files available in the download with this article. Here are some of the highlights:
public class CycleSelectArrowSkin extends ProgrammaticSkin
The class extends mx.skins.ProgrammaticSkin which extends flash.display.Shape. That's because skins should be very light-weight and be limited to just presenting graphics. However, one of the most powerful properties of Flex and the Flash Player is its flexability. You do not have to make your skins extend ProgrammaticSkin. You can use any class which implements the IFlexDisplayObject interface.
Since skins are so lightweight there isn't much else they do except override updateDisplayList():
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
var color:Number;
switch( name )
{
case "arrowSkin":
color = getStyle("arrowColor");
if( isNaN(color) ) color = getStyle("themeColor");
break;
case "arrowDisabledSkin":
color = 0xAAAAAA;
break;
}
drawArrows( graphics, color );
}
In this function, the skin's name is used to determine its color. If the skin is the "arrowSkin" then the color is extracted from the "arrowColor" style. If that was not defined, then the skin's color defaults to the "themeColor".
Using the themeColor as a default - whether it is the actual color or a darker or lighter version (see mx.utils.ColorUtil class) - is a good idea since that color flows nicely with the style sheet and theme idea.
Once the color is chosen the skin is drawn. The drawArrows function is the same as it was before:
private function drawArrows( g:Graphics, color:uint ) : void
{
g.clear();
g.lineStyle(0, color, 1);
g.moveTo(-10,0);
g.curveTo(-10,-10,0,-10);
g.curveTo(2.5,-11,8,-8);
g.moveTo(8,-8);
g.lineTo(6.5,-13);
g.moveTo(8,-8);
g.lineTo(2.5,-6);
g.moveTo(10,0);
g.curveTo(10,10,0,10);
g.curveTo(-2.5,11,-8,8);
g.moveTo(-8,8);
g.lineTo(-6.5,13);
g.moveTo(-8,8);
g.lineTo(-2.5,6);
}
Summary
That's all there is to skinning components:
- Figure out what you want to skin,
- extract the drawing into a class based on ProgrammaticSkin,
- add the skin as a style to your component,
- create an instance of the skin, and position it.
You should now be able to make reusable components for all of your Flex projects.
Posted by pent at 02:35 AM | Comments (4)
December 13, 2007
Flex 3 Beta 3 Available
The third and final public beta releases of Flex 3 and Adobe AIR are now available for download on Adobe labs. These releases are focused on quality and performance, resolving numerous bugs from beta 2. This is your final chance to provide feedback on the release before launch, so please take this opportunity to take a final, thorough look. You can download the final beta releases of Flex 3, Flex Builder 3, and Adobe AIR on Adobe Labs at: http://labs.adobe.com/. Please also be sure to check out BlazeDS, the newest open source Remoting and Messaging project from Adobe.
Posted by pent at 08:20 AM
November 29, 2007
PopUp, Can You Hear Me?
Communicating with a PopUp is a common task. This article explores how to do that.
The first example shows how to use Alert to pose a question and get answer.
private function showQuestion() : void
{
Alert.show("Do you like Flex?", "Question", Alert.YES|Alert.NO,this,processAnswer);
}
private function processAnswer( event:CloseEvent ) : void
{
if( event.detail == Alert.YES ) {
response.text = "Of course you do!";
} else {
response.text = "Hmm. Really? Keep reading then.";
}
}
Now that you've seen how Alert works, let's look at a more complex example. Here, a TitleWindow is used to present a choice of themes (you can see one like it in my Atmospheres AIR Music Player). When the user picks the Apply button the theme changes.
This is the code for the pop-up, ThemeChooser.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
width="338" height="252"
title="Choose Your Theme"
showCloseButton="true"
horizontalAlign="center"
verticalAlign="middle"
close="PopUpManager.removePopUp(this)">
<mx:Metadata>
[Event(name="changeTheme", type="theme.ThemeEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import theme.ThemeEvent;
import mx.managers.PopUpManager;
private function dispatchThemeChoice( themeName:String ) : void
{
dispatchEvent( new ThemeEvent(themeName) );
PopUpManager.removePopUp(this);
}
]]>
</mx:Script>
<mx:VBox horizontalAlign="left" verticalGap="17">
<mx:RadioButton label="Adobe Red" click="dispatchThemeChoice('adobeRed')"/>
<mx:RadioButton label="Orange Neon" click="dispatchThemeChoice('orangeNeon')"/>
<mx:RadioButton label="Blue Wave" click="dispatchThemeChoice('blueWave')"/>
</mx:VBox>
</mx:TitleWindow>
Notice that when a RadioButton is clicked, an event is dispatched. The event is a
ThemeEvent - a custom event type. This is the code for the event, ThemeEvent.as:
package theme
{
import flash.events.Event;
public class ThemeEvent extends Event
{
public static const CHANGE_THEME:String = "changeTheme";
public function ThemeEvent(themeName:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(CHANGE_THEME, bubbles, cancelable);
this.themeName = themeName;
}
public var themeName:String;
}
}
This is the code for the main application to demonstrate it:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="271" width="532"
borderStyle="solid"
borderColor="black"
borderThickness="1">
<mx:Script>
<![CDATA[
import theme.ThemeEvent;
import theme.ThemeChooser;
import mx.managers.PopUpManager;
import mx.core.IFlexDisplayObject;
private function showThemeDialog() : void
{
var pop:IFlexDisplayObject = PopUpManager.createPopUp(this, theme.ThemeChooser, true );
pop.addEventListener( ThemeEvent.CHANGE_THEME, selectTheme );
PopUpManager.centerPopUp(pop);
result.text = ""; // reset
}
private function selectTheme( event:ThemeEvent ) : void
{
result.text = event.themeName;
}
]]>
</mx:Script>
<mx:Button x="24" y="27" label="Select Theme" click="showThemeDialog()"/>
<mx:Label x="24" y="57" fontWeight="bold" color="#FFFFFF" id="result"/>
</mx:Application>
I think this is the easiest solution: a custom event. The event contains all of the information from the pop-up and lets the pop-up disappear without having to hang around while you get information from it. If you have a lot of data (e.g., a user's profile) consider creating a class to represent it and have the custom event contain an instance of the class. This also makes it easier to change the information later.
Both of these examples use window-type classes as the base for the pop-ups. But you can use any component for a pop-up. Here's a pop-up that's a shape with text; it has no buttons nor a frame. Click it make it go away. While not very intuitive, it does make the point.
Download Files
You can download the code for this specialized pop-up here.This zip file is a Flex Builder 3, Beta 2, project archive. If you do have Flex Builder 3, Beta 2, you can import the source files directly from the zip.
When you use a modal pop-up, which is what all these examples have been so far, you'll notice that the background becomes lighter and blurred. This is governed by styles on the Application which you can change. You can change the color and amount of blur. You can even get rid of both.
Mode-less pop-ups are common as floating tool bars. There's no difference in how you make them nor interact with them, just in how you present them. Using a mode-less pop-up enables the user to continue working with the application while the pop-up is still present.
I hope this gives you some ideas in case you need to pop-up dialogs or warnings. Be unconvential and use events to communicate the result of the pop-up back to the main application.
Posted by pent at 11:52 AM | Comments (0)
October 31, 2007
Component Class - Part Four
In the previous article in this series you saw how the Arrow part of the CycleSelectButton was created. In this article we'll write the CycleSelectButton from scratch by extending UIComponent. Watch how similar this component's construction is to the V2 and Arrows components.
Start by creating a new ActionScript class and call it
CycleSelectButtonV3 . Have it extend
UIComponent :
public class CycleSelectButtonV3 extends UIComponent
{
/**
* constructor function
*
* This is a good place to set inital styles
*/
public function CycleSelectButtonV3()
{
super();
}
To make this simple for you - and to drive home the point of how similar things are - copy the following items from the V2 component into this V3 component:
- The
[Event]metadata; - the linkButton and arrows variable definitions (be sure to copy the import statements, too);
- the
createChildren()function; - The
dataProviderproperty set and get functions; - The
selectedIndexproperty set and get functions; - The
commitProperties()function; - The
handleClick()event handler function.
So what's left? I'm not sure all of that will compile, but give it a try and if it does, put it into a Flex application and test it out. Not quite right, huh?
This zip file contains the source for this component and a sample application.
The V2 version of this component extends HBox which does a couple of things for you: it handles the placement or layout of the component. By using HBox you don't have to worry about how big things are and where they go. HBox always measures each child and sticks one after the other.
Since this V3 component extends UIComponent you don't have any of that help. You have to implement a couple of the Flex framework functions to make the component behave correctly.
measure()
Look back at the Arrows component and you'll see two things that are missing from this V3 component: the
measure() and
updateDisplayList() functions. Measure() is important because the Flex framework needs to know how big the component is in order to position it within a container. The updateDisplayList() function is important to position the arrows and linkButton - something HBox did for you.
override protected function measure() : void
{
super.measure();
measuredWidth = arrows.getExplicitOrMeasuredWidth() + linkButton.getExplicitOrMeasuredWidth();
measuredHeight= Math.max( arrows.getExplicitOrMeasuredHeight(), linkButton.getExplicitOrMeasuredHeight() );
}
The measure() function must set the
measuredWidth and
measuredHeight properties. Since the component's design is to be horizontal with the arrows followed by the linkButton, the width is then the sum of each child's width. The height is the largest of the two.
If your component is also given an explicit width and height, then this measure() method will not be called.
Noticed the call to
getExplicitOrMeasuredWidth (and
getExplicitOrMeasuredHeight ). Since the arrows child has been given a size of 20x20, these functions return the explicit size of 20. The linkButton however, was not given a size, so it has to be measured.
updateDisplayList()
Once the child components have been measured and an overall size for the component has been determined, the Flex framework calls the updateDisplayList() function.
Just as with the Arrows component, updateDisplayList's purpose is to position and size the child components to make this component look the way it is supposed to look.
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
arrows.move(0,0);
linkButton.move(arrows.width,0);
linkButton.setActualSize(unscaledWidth-arrows.width,unscaledHeight);
}
The arrows are moved to the (0,0) position. Then the linkButton is positioned immediately after it. The linkButton is also given a size. Here, unscaledWidth and unscaledHeight will be whatever measure() determined or they will be the explicit sizes given to your component.
And that's all there is. The download source file has it all put together for you along with a sample test program. But take a look at the V2 and V3 components; compare them with the Arrows component. In all cases the Flex framework operates consistently. Because V2 was based on HBox, the measure and updateDisplayList functions were not necessary. When writing a component from scratch you have to do these things yourself.
Now you can write components from scratch - either based on existing components or from scratch. The next article in this series looks at skinning and styling components.
Posted by pent at 11:36 AM | Comments (6)
October 24, 2007
Component Class - Part Three
In the previous article you saw how to create a component in ActionScript and how that mimics a component written in MXML:
| MXML | ActionScript |
|---|---|
| Root tag | class extends |
| Metadata tag | [Metadata above class definition] |
| Child component tags | override createChildren(), using new operator and addChild() function. |
| Properties | Set and Get functions; override commitProperties() |
| Events | Use addEventHandler and specify an event argument to the handler function. |
In this article we'll look at how to make that circle of arrows rotate.
This is a link to the same file download in the previous article; nothing has changed.
Arrow Class
In the first article the cycle arrows is a GIF. Easy to place using the Image component (either in MXML or in ActionScript). That would be enough, except we want to rotate the arrows as the user clicks on the link in the component.
So what's wrong with that? Rotation in Flash is pretty easy, you just set the rotation property to an angle and the object rotates - about its (0,0) point. That's the catch - in Flex a component has (0,0) as its upper-left corner. When you rotate a Flex component by changing its rotation property, it pivots on this corner - it does not rotate about its center.

There are ways around this using a translation matrix, but I think this alternative will prove educational and help you out when you have some awkward things to do in Flex.
Principle
Keep in mind that (0,0) is the upper-left corner of a Flex component and to make life very easy and simple in the Flex framework, the Arrow component is going to keep it this way. The difference is that inside of the Arrow component, the circle of arrows will appear and it will rotate and not the Arrow component itself.
Here's the Arrow component in its entirety, but broken into sections. I think it will be easier to explain this way.
package com.adobe.examples.skins
{
import mx.core.UIComponent;
import flash.display.Graphics;
import flash.display.Shape;
public class Arrows extends UIComponent
{
There really isn't any existing Flex component to extend so the Arrow class extends UIComponent - the base class for every Flex component. UIComponent is what every Flex component inherits from.
createChildren
private var canvas:Shape;
/**
* createChildren (override)
*
* Creates the shape in which the arrows appear. This shape can then
* be rotated.
*/
override protected function createChildren():void
{
canvas = new Shape();
// after drawing the arrows below, I realized they were too big, but my
// calculations for the lines and curves were already figured out. So I
// just scaled the graphic a bit to make it look better.
canvas.scaleX = 0.6;
canvas.scaleY = 0.6;
addChild(canvas);
}
This component has a single child, a
flash.display.Shape , where the arrows will appear. This is the part that actually rotates. The Shape class is a very basic, lightweight Flash class for drawing. It has very little overhead and is ideal for this purpose.
measure
/**
* measure (override)
*
* Return the default width and height
*/
override protected function measure():void
{
measuredWidth = measuredMinWidth = 20;
measuredHeight = measuredMinHeight = 20;
}
So far you've seen createChildren() and commitProperties() - functions which you override to make your component. Here is another -
measure() . This function is critical to making components behave properly with the Flex framework's layout manager. You don't need to have measure() in the CycleSelectButton components because the HBox does this for you - a benefit of using a Container as a basis for your own components.
The measure function is called only when the Flex framework does not know how large the component should be. If you supply an explicit width and height to a component, measure() is never called because the layout manager knows how big it is. Keep this in mind and do not write anything in this function that is critical since it is not always called.
The measure() function's job is to set the
measuredWidth and measuredHeight properties (and optionally, like here, the measuredMinWidth and measuredMinHeight properties). Sometimes measure() can be complex if you have lots of children to the component - you have to measure all of them and then figure out how large the overall component is.
In this case, I create the circle of arrows to occupy a 20x20 area. So that's what I set measuredWidth and measuredHeight to.
updateDisplayList
/**
* updateDisplayList (override)
*
* Position the canvas containing the arrows in the middle of the component. Then
* draw the arrows.
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
canvas.x = unscaledWidth/2;
canvas.y = unscaledHeight/2;
drawArrows( canvas.graphics );
}
The
updateDisplayList() is another function in the Flex framework called in the life cycle of a component. The updateDisplayList() function is perhaps the most fun - it is were you actually make things happens. In this case two things are done: the canvas Shape with the arrows is positioned and the arrows are drawn.
Remember that the arrows are being drawn within the canvas at (0,0) - so we need to place the canvas Shape where we want its (0,0) to be - and that's in the middle of the component which is (unscaledWidth/2, unscaledHeight/2). If this is confusing, change it to (0,0) and see what happens.
I moved the arrow-drawing into a separate function to make it clearer (this function appears at the end of the class):
/**
* drawArrows
*
* Draws the circle of arrows in the given graphic. The circle is centered
* at (0,0) to make it easy to rotate
*/
private function drawArrows( g:Graphics ) : void
{
g.clear();
g.lineStyle(0, 0x0000CC, 1);
g.moveTo(-10,0);
g.curveTo(-10,-10,0,-10);
g.curveTo(2.5,-11,8,-8);
g.moveTo(8,-8);
g.lineTo(6.5,-13);
g.moveTo(8,-8);
g.lineTo(2.5,-6);
g.moveTo(10,0);
g.curveTo(10,10,0,10);
g.curveTo(-2.5,11,-8,8);
g.moveTo(-8,8);
g.lineTo(-6.5,13);
g.moveTo(-8,8);
g.lineTo(-2.5,6);
}
The
flash.display.Graphics (g) is the Flash entity which places instructions into the Flash Player's display list. Ultimately every component does this. The first thing is almost always clear() so you don't add more graphics - you usually want to replace them. You can read more about the Drawing API in the Flex documentation. You can see that the arrows are a series of
lineTo and curveTo functions about (0,0).
The rotation Property
/**
* rotation - apply rotation to the canvas shape, not to this component
*/
override public function set rotation(value:Number):void
{
canvas.rotation = value;
}
override public function get rotation():Number
{
return canvas.rotation;
}
Here's a trick you may not expect: overriding a very basic property like rotation. If you were to do something like
arrows.rotation = 45 you would and should expect the component to rotate to 45 degrees. But if that happens, the component rotates about its upper-left corner. What we want to do is rotate the Shape instead. By overriding the component's rotate property, we re-direct the value down to the Shape and prevent the component itself from being rotated.
Another benefit of overriding the property is that it will make sense to the developer who uses the component. Telling them that they can rotate the arrows by changing the rotation property is what they expect. Trying to explain they should not use rotation but instead use another set of functions is awkward.
Use in CycleSelectButtonV2
Now open the CycleSelectButtonV2 code and replace the Image component with an instance of the new Arrows component. The code provided in the download already does this; you use the Arrows component like anything else:
- You have to create an instance of it using new Arrows().
- You can add it to the CycleSelectButtonV2 component with addChild().
override protected function createChildren() : void { arrows = new Arrows(); arrows.width = 20; arrows.height= 20; addChild(arrows); linkButton = new LinkButton(); addChild(linkButton); // add a listener for the click on the LinkButton. linkButton.addEventListener(MouseEvent.CLICK, handleClick); super.createChildren(); } - You make it spin by changing its rotation property in the LinkButton click event handler:
arrows.rotation = arrows.rotation + 45;
What's Next
In the next article in this series we'll write this component one more time, but completely from scratch by extending UIComponent.
Posted by pent at 07:22 PM | Comments (3)
October 22, 2007
Component Class - Part Two
In the previous article I showed you how to make a component based on an existing MXML component, the HBox. In this article we'll look at writing that same component in ActionScript. I think this is a worthwhile exercise because you'll see how similar MXML and ActionScript components are.
One good thing to try is to add
-keep-generated-actionscript=trueto your Flex compiler options. MXML files are first translated into ActionScript classes, then compiled into the SWF file. The-keep-generated-actionscriptswitch will leave those translations and you can compare what we're about to do with what the compiler has generated.
Take a look at the V1 MXML component. There are four parts to it: the root tag (the HBox), the Event metadata, the Script block, and the child components (LinkButton and Image). You'll see how these elements are reflected when you write the ActionScript version of the component.
This is the source code for this example in a package structure. The file for the icon is included.
Create a new ActionScript class called CycleSelectButtonV2 and have it extend HBox. Use the Flex Builder wizard if you can. Otherwise create the class using the following code:
package com.adobe.examples.buttons
{
import mx.containers.HBox;
public class CycleSelectButtonV2 extends HBox
{
public function CycleSelectButtonV2()
{
super();
}
}
}
One of the four elements has already been taken care of: the class extends HBox.
The MXML component also includes event metadata. Add that to the ActionScript class just above the class definition:
package com.adobe.examples.buttons
{
import mx.containers.HBox;
[Event(name="change",type="flash.events.Event")]
public class CycleSelectButtonV2 extends HBox
{
...
The child components, Image and LinkButton, require ActionScript code. In the first article of this series you read how the Flex framework operates with properties being set, with commitProperties() being called, and so forth. Creating child components is also part of that framework.
When a component is being created the Flex framework invokes its
createChildren() function. This is as true for Buttons as it is for Container classes like HBox. To create the LinkButton and Image components, you'll need to add your own override of createChildren().
Below the import statement for HBox in the V2 ActionScript file, add:
import mx.controls.LinkButton; import mx.controls.Image;
The MXML tags for Image and LinkButton created two members of the class. You'll do the same here, below the class constructor function:
private var linkButton:LinkButton; private var image:Image;
Component children in MXML files are public. I like to make them private, but you can use protected or public if you like.
You also need to bring in the cycle button graphic. In the MXML file it was embedded directly in the Image tag. There is no ActionScript equivalent for that. Instead, add this code below the variable declarations:
[Embed(source="../assets/cycle_component.gif")] private var cycleIcon:Class;
The embed metadata tells the compiler to grab the bytes from the file and generate a class to make an image out of them. The data type of the cycleIcon variable is
Class because you don't want to have duplicates of the image (takes too much memory) but rather have shared instances that refer to those bytes.
Now add the createChildren() function:
override protected function createChildren() : void
{
image = new Image();
image.width = 20;
image.height = 20;
image.source = cycleIcon;
addChild(image);
linkButton = new LinkButton();
addChild(linkButton);
super.createChildren();
}
An instance of the Image class is created, then its width and height are set as is its source property. The LinkButton is also created. Notice how both components are added as children to the component using
addChild() . This puts the children onto the component's display list. If you do not do this the children will not appear. Creating the component children with
new and using
addChild() is equivalent to the MXML tags.
The createChildren function is called before commitProperties() so that you can apply properties to the children. You should now copy the commitProperties() function, along with the setter and getter property functions from the V1 class.
With the LinkButton and Image child components created and set, you've knocked off another MXML component equivalent.
In the previous article, you read that part of the Flex framework includes the measurement of the components. One of the big things the MXML component does for you is figure out the size of everything and where to place it. You'll have to do that yourself with an ActionScript component.
Because we are using HBox as the base class for the component, the sizing and positioning of the children are taken care of. Later, when we write this component from scratch, you'll see how to do that for yourself.
At this point the only thing missing is the click event handler on the LinkButton. To add that, go back into the createChildren() method and add the following line after the linkButton has been created:
linkButton.addEventListener(MouseEvent.CLICK, handleClick);
You'll need to include an import statement for
flash.events.MouseEvent with the other import statements.
Using addEventListener() is the equivalent of putting the event handler in the MXML tag.
Now copy the handleClick() function from the V1 component and make the change shown:
private function handleClick( event:MouseEvent ) : void
{
selectedIndex = selectedIndex + 1;
// wrap back to zero if more than the # of items in the dataProvider.
if( selectedIndex >= dataProvider.length ) selectedIndex = 0;
dispatchEvent( new Event(Event.CHANGE) );
}
When you are using MXML tags you have the luxury of deciding what, if any, arguments to pass to an event handler. When using addEventListener the event handler function must take a single argument of some event type. If you read the documentation on the LinkButton and look at the click event, you'll see it dispatches a MouseEvent, so that's the type of data that will be passed.
Open the main Flex application and change the tag from CycleSelectButtonV1 to CycleSelectButtonV2 and run it. The component should look and behave the same way.
Rotating the Arrows
I said in the previous article that, in addition to writing this component in ActionScript, we'd also rotate the arrow cycle each time the LinkButton was clicked. I've included the code to do that in the source download, replacing the Image. Take a look and it will be the topic of the next article in this series.
Posted by pent at 04:50 PM | Comments (6)
October 12, 2007
Childhood Memories
I was cleaning out my hard drive the other day and came across a directory of old Flash programs. One of these is a demo of the Flash Drawing API that I had set into the background of old my website.
The algorithms are based on the Spirograph(tm) drawing kit I had as a kid. As I recall, no one but me was interested in it (I guess because it didn't come with an 'action figure').
Anyway, here it is running below and you can download the code from the link here Enjoy.
Posted by pent at 10:56 AM | Comments (2)
June 12, 2007
Adobe® Flex™ 3 Public Beta and Adobe® AIR™
The first public beta release of Flex™ 3 is now available on Adobe labs. This major functional release adds rich new UI capabilities, enhanced developer productivity, desktop deployment and enterprise testing and performance profiling tools. The Flex 3 public beta also marks the first major deliverable for the open source Flex project, beginning the availability of nightly builds, a public bug base and roadmap.
Beta 1 is an early preview of the Flex 3 release. The goal of the release is to give the community an opportunity to provide feedback on the feature design and to help identify bugs and missing capabilities. Not all of the planned features have been implemented, and you should expect to find significant bugs, so be sure to save and back up your work often.
Some of the new major features include:
- Built-in support for the new Adobe Integrated Runtime (Apollo)
- code re-factoring
- memory and performance profilers
- SWF file size reduction using a persistent framework cache in Flash Player
- advanced datagrid
- CS3 and Flex integration
- web service introspection
Get the complete details and download the beta on Adobe Labs at: http://labs.adobe.com/technologies/flex.

The Adobe Integrated Runtime - AIR™ - code named Apollo - is now available on Adobe labs. Using AIR, developers can use their web skills and tools (Flex Builder) to builld desktop applications that fit seamlessly with web applications. AIR applications can contain an integrated HTML control for displaying web pages and interact with those pages using Flex controls and events. AIR applications also have the ability to use system resources, such as the local file system.
Get complete details on Adobe Labs at: http://labs.adobe.com/technologies/air
The AIR SDK is included with the Flex 3 Beta; all you need is the AIR player.
Posted by pent at 12:28 PM | Comments (1)
Adobe® Flex™ 3 Public Beta and Adobe® AIR™
The first public beta release of Flex™ 3 is now available on Adobe labs. This major functional release adds rich new UI capabilities, enhanced developer productivity, desktop deployment and enterprise testing and performance profiling tools. The Flex 3 public beta also marks the first major deliverable for the open source Flex project, beginning the availability of nightly builds, a public bug base and roadmap.
Beta 1 is an early preview of the Flex 3 release. The goal of the release is to give the community an opportunity to provide feedback on the feature design and to help identify bugs and missing capabilities. Not all of the planned features have been implemented, and you should expect to find significant bugs, so be sure to save and back up your work often.
Some of the new major features include:
- Built-in support for the new Adobe Integrated Runtime (Apollo)
- code re-factoring
- memory and performance profilers
- SWF file size reduction using a persistent framework cache in Flash Player
- advanced datagrid
- CS3 and Flex integration
- web service introspection
Get the complete details and download the beta on Adobe Labs at: http://labs.adobe.com/technologies/flex.

The Adobe Integrated Runtime - AIR™ - code named Apollo - is now available on Adobe labs. Using AIR, developers can use their web skills and tools (Flex Builder) to builld desktop applications that fit seamlessly with web applications. AIR applications can contain an integrated HTML control for displaying web pages and interact with those pages using Flex controls and events. AIR applications also have the ability to use system resources, such as the local file system.
Get complete details on Adobe Labs at: http://labs.adobe.com/technologies/air
The AIR SDK is included with the Flex 3 Beta; all you need is the AIR player.
Posted by pent at 12:28 PM | Comments (1)
June 07, 2007
Flex, Apollo, and Flash CS3
The Flex/Flash/Apollo Project
I've been at my latest project for a while, writing bits and pieces of it when I've gotten a chance. Since the Flash CS3 Flex Component kit was announced I've wanted to build something that used it. I had the idea of making an Apollo application to show that the kit wasn't just for Flex. Then I had a better idea: write an application for both Apollo and Flex, using Flash CS3 Flex Component Kit as well as a Flex Library Project to tie everything together.
I made what is perhaps a simple or silly application, but it does show how to do a number of different things. I call it the Flickr Scrapbook. The idea is that you make a scrapbook for your friends and family using photographs from Flickr - ideally your own photos, but anything public can be displayed. Each page can hold multiple photographs with captions and notes can be added as well. The pages and text can be colored, the photographs rotated and resized, and the whole thing can be displayed on a web page using Ely's FlexBook control.
Scrapbook Editor |
![]() |
Scrapbook Viewer |
![]() |
The project is two applications. One is written for Apollo and is the Scrapbook Editor. The other is written for Flex and is the Scrapbook Viewer. It seemed appropriate that an editor would be written as an Apollo application because you may want to save your work onto your local machine. The viewer would of course, be on a web page so Flex was the obvious choice. I also created a Flex Library project to keep the common classes they both use. I used Flash CS3 to make skins and components that would be difficult in Flex alone.
You can view a couple scrapbooks I made by following these links:
If you have the Apollo runtime (labs.adobe.com/apollo) you can download the Scrapbook Editor and try it out:
(note: you may be asked to save the file as a .zip - do not do that - rename the file with a .air extension)
Disclaimer
Aside from the usual, "use at your own risk, this is not supported by Adobe, no animals were harmed in the making of this application, etc. etc." disclaimers, I have to emphasize that I am NOT a developer. So my architecture may not be what you are used to. I know I didn't use enough interfaces to make software gurus swoon, but I think there is enough substance to at least give you ideas and to show you how to do things with Flex.
Source Code
The link here will download a zip file. The zip file contains everything: the source to both projects, the Flash CS3 source, and the Scrapbook Flex library project. You should be able to import the projects right into Flex Builder 2. If you have later version of Flex Builder, create new projects and just import the source files.
Bits and Pieces
- Use these two applications as a resource. Here are some things you'll find as you explore them:
- Reading and Writing Files using the Apollo File class. This class is so cool. It is cross-platform and easy to use.
- Making data service calls with HTTPService. I used my own Flickr API, but the one from Adobe Labs is cool too. I would have used that one but I had already started down my own path.
- Displaying Images. Using the Image tag is relatively straightforward. But mostly you'll want to show something while the image is loading and then switch to the image once it has loaded. I used Alex Uhlmann's "Flip" effect to spin the images into view.
- Using States and Transitions. This is one of the neatest features of Flex. I use states in several places, but if you start the Editor and start selecting things like photographs, notes, and the cover page, you'll see the property panels slide in and out. Hiding and showing the property area in the Editor is also a state change.
- Using Effects. As I just mentioned, I use effects to show the images, to switch property panels, among other things.
- Using Drag and Drop with the DragManager. Components like the DataGrid and Tree have drag and drop capability built in. I show you how to use it when that isn't available - just drag a photograph onto a scrapbook page and see.
- Making Flex Components and Skins with Flash CS3. Finally, Flash is in its rightful place along side Flex. I use the Flex Component Kit for Flash CS3 two ways: to make actual components (eg, the handle to hide and show the property area) and skins (most of the buttons).
- Creating and Using Custom Events. You can't write a decent application without custom events. And they are pretty easy, too.
- Using Cascading Style Sheets. I tried to put most of the styles into an external style sheet.
- Interaction Using the Mouse: Drag, Resize, and Rotate. Dragging things around the screen is very helpful with an editor. I show you how to do that along with resizing and rotating the photographs - using effects no less.
- Using Embedded Fonts. You must use embedded fonts if you want to scale text or modify it in any graphical way.
- Creating Components using ActionScript. Laying out your application using MXML tags is great, but it makes your application static. You'll need to know how to create and add components while the application is running. An example is creating a new photograph on a scrapbook page.
- Deploying Flex Applications to a Web Server. Once you've made your scrapbook file you need to put it to your webserver for people to see.
- Using a Custom HTML Wrapper. The Scrapbook Viewer uses a custom HTML wrapper which takes reads the scrapbook file name from the URL (query parameter) and passes it to the Flex application.
- Subclassing Components and Using Interfaces. You need to do this to get mileage out of your components. No need to write everything twice when you can extend something.
The majority of these items are found in the Scrapbook Editor. The Srapbook Viewer makes use of a few of the techniques and if you are interested in Ely's FlexBook, the Scrapbook Viewer code shows how to use it.
Here's an example of what you'll find in the code: I created the ActionScript class Photograph to encapsulate all the things a photograph on a scrapbook page can be. The Photograph class has properties such as the URL to the image on Flickr, the caption with the photograph, its rotation, its placement on the page, and so forth. However, that's not enough for either the Editor or the Viewer. In the Editor I want you to be able to select a photograph and use the mouse to resize it, rotate it, and delete it. In the Viewer I want you to click it to enlarge it.
To do those things I put the Photograph class in the Flex Library project to be used as the base class for both applications. Then I created the EditorPhotograph class for the Editor application and added the extras (resizing, rotation, etc). I did a similar thing with the Viewer.
There is also an example of an interface. A page in the scrapbook can hold two types of objects: Photographs and Notes. I wanted to write code as cleanly as possible and not be burdened with figuring out what type of object it had. This is a good place for an interface and I created the IScrapbookItem interface (also in the Flex Library project). Both Photographs and Notes implement it. The Editor extends that with IEditorItem as its needs a few extra things.
Scrapbook Editor Instructions
When you start the Scrapbook Editor you'll see two main areas: the one on the left is where your Flickr search results appear; the area on the right is where you build the scrapbook, page by page.
Begin by entering some tags (separated by commas) into the search box at the top left. Try simply miami beach and press Enter. After a bit of searching a page of images will appear.
![]()
Now give your scrapbook at title. Just enter the new title into the text box below the cover page.
You need a scrapbook page to place photos, so use the large green plus-sign above the scrapbook area to add a page. The page will be placed after the page you are viewing.
Now you can drag photos over to the page and drop them in place. The photos will become selected with the non-selected photos dimmed.
![]() |
The red x deletes the photograph. Drag the green arrow to resize the photograph. Drag the blue arrows to rotate the photograph. |
When a photograph is selected you can change its properties. Use the controls on the photograph to delete it, resize it, or rotate it. Below the page are other properties.
![]() |
You can change the photograph's caption, remove the drop shadow, change the matte color, text color, and so forth. |
Click on the background of the page to de-select the photograph and the properties change to allow you to work with the page itself.
![]() |
You can change the page's color and also add notes which you can position around the page by dragging them with the mouse. |
Continue to add pages and photographs. When are ready to save your work, use the File menu above the scrapbook. Select Save, locate a place, and give your file a name; the file's name will be changed to add .xml as the extension.
![]() |
New will make a new, empty scrapbook. Open will open an existing scrapbook. Save and Save as save the current scrapbook. Print prints the scrapbook. |
Deploying the Scrapbook
When you are ready to show the scrapbook to the world, copy the scrapbook .xml file you made, along with the contents of the deployment directory, to your web server. Send a URL to your friends and family:
http://your.domain/scrapbooks/Viewer.html?book=miami_vacation.xml
Adding the query string, book=miami_vacation.xml will automatically load that scrapbook file into the viewer.
Things to To Do
As you use these applications you may find yourself asking why this or that feature is missing. Well, I wanted to get this project done so I left some things out and I wanted to give the 'interested reader' the opportunity to modify the code. Here's some items I'd like to see added and I may get around to doing them myself some day:
- You can add photographs from Flickr to your scrapbook. It would be nice to use photographs that are already on your computer, too.
- How about adding video clips? Imaging flipping to a page and there's a clip of you on the beach. Or may be not.
- It would be nice to add more properties for the title and notes fields such as size and font family.
- It would be nice to rotate and resize the notes on the scrapbook pages.
- You should be able to add textures to pages. Actually, some of the code is already there and needs to be finished.
- You should be able to add photographs and additional text to your scrapbook cover.
- It would be nice to add gradient fills to the scrapbook cover; may be the pages, too.
- It would be cool to have transparent pages. Check out Ely's FlexBook demo and the medical text book example.
- It would be nice to view thumbnails of the scrapbook pages in the editor so you could re-arrange them (or delete them).
- Right now in the Scrapbook Viewer, clicking on a photograph zooms it up on the current page. It would be nice if the photograph spanned the pages and could become larger.
This list could probably go on and on. But you get the idea - even if you don't want a scrapbook editor or viewer, this should provide you with a good opportunity to explore and try some things out. I hope the comments in the code are helpful enough to guide through these applications.
Summary
The time is right to put these technologies together. You can really make the experience matter.
Posted by pent at 09:22 PM | Comments (12)
April 27, 2007
Example of the Flex Component Kit for Flash CS3
I've now had a chance to use the Flex Component Kit for Flash CS3. It worked as advertised for me. Check out Glenn's presentation on the Adobe Labs page. If you are interested in this example, you will need to follow the instructions given on that page to upgrade Flex 2 and get the Kit for Flash CS3. Both are pretty easy to install.
I'm going to use the ball-and-star Flash SWF from the previous examples. This really shows how far things have come. When you compare this to the first article using ActionScript 2 SWFs with Flex 2, you'll see what I mean.

Here's how I went about the process of using the Kit. I started with the same FLA I used in the last article.
Create a New Flash CS3 Document
It's important to create a new Flash CS3 document. If you want to use symbols from another Flash 8 (or earlier) document, copy them to the new document's Library. That's what I did with the example and called it star_and_ball.fla. I copied over the Star and Planet symbols.
You can only use symbols with the Flex Component Kit. Since my previous example had all of the tweens and ActionScript code on the root timeline, I needed to create a new symbol for them in the new document. I created a new, plain, MovieClip symbol which I called StarAndBall (yes, it should have been StarAndPlanet). I placed the Star and Planet symbols into the new StarAndBall symbol in separate layers and copied the guide layer as well and the tween.
Note: Symbols should have (0,0) as their registration point. I made sure that my Star and Planet symbols were positioned so that as the Planet orbits the Star it all stays within these boundries. However, you can also add a special boundingBox to your symbol and Flex will use that for your component's size. You can read about that in the documentation.
Creating the Flex Component
Once the symbol was working I selected it in the Library. Then I picked the new command, "Make Flex Component" from the Commands menu. Several things happened:
- Since my movie wasn't set to 24fps, I was asked if I wanted to change it to 24fps. I responded Yes (see documentation for explanation).
- Then the output window showed that UIMovieClip was added to my Library and that my symbol, StarAndBall, was ready for export.
That's really all there was to it. If you looked at the symbol's properties before doing this you would have seen that it didn't have a linkage name. Now, you'll see that it has an ActionScript class (more on that later), that its base class is UIMovieClip, and that it is being exported.
Publish
You must publish your movie. This not only creates the SWF, but it also creates a SWC. Since my file is named star_and_ball.fla, publishing created star_and_ball.swf and star_and_ball.swc. When using the Flex Kit, ignore the SWF. Maybe a future version will even let you avoid creating the SWF, but for now just ignore it.
Using the Flash Component in Flex
Being able to pick any MovieClip symbol in your Flash libary and selecting a command to turn it into a Flex component was easy. Now the fun part. I created a new Flex project and opened its Project Properties dialog. I then went to the Build Path and selected the Library tab. Then I picked "Add SWC" and browsed to the star_and_ball.swc and brought it in.
Flex now believes that star_and_ball.swc contains a true Flex component named StarAndBall. To use I started typing:
<Star
Flex Builder's code assistant brought up the StarAndBall class, so I selected it and <local:StarAndBall was inserted. I closed the tag and ran my application. The Flex application ran and the planet was orbiting the star.
At this point you probably think this may be no better than just using SWFLoader. That would be less steps, but did you see how Flex Builder found the Flash symbol as a class? That's due to UIMovieClip in the SWC, making the symbol a true Flex component.
Going For Objects
In the past examples you could control the Flash SWF (stopping the ball from orbiting, scaling and rotating the star). In the first example it was all controlled by LocalConnection. In the second example (just a few days ago, really), you can directly invoke functions on the root timelime.
Using the Flex Kit also allows you to invoke methods, not on the root timeline, but on the symbol itself, just like any Flex component.
Going backt to the Flash CS3 document, star_and_ball.fla, I opend the properties for the StarAndBall symbol. You can see that there is a class named: StarAndBall. If I click on the pencil (edit) button, I'm told that the class does not exist but one will be generated for me in the SWC. Very nice. But you can make your own, too.
I created a class, StarAndBall.as and used the root timeline functions as class methods:
package {
import flash.display.MovieClip;
import mx.flash.UIMovieClip;
public class StarAndBall extends UIMovieClip
{
public function StarAndBall():void
{
}
public function rotateStar( angle:Number ) : void {
star_mc.rotation = angle;
}
public function zoomStar( factor:Number ) : void {
star_mc.scaleX = factor;
star_mc.scaleY = factor;
}
public function stopPlanet() : void
{
stop();
}
public function resumePlanet() : void
{
play();
}
}
}
Hmm, very similar to Flex component code. Of course, this is ActionScript 3, so you have packages and public functions and import statements. Now when the FLA is published, my class gets put into the SWC.
Back to Flex
In the Flex application I gave the component a name: <local:StarAndBall id="star" />. I added a button to pause the orbit of the ball/planet and made the click event handler invoke the stopPlanet() method of the StarAndBall class:
star.stopPlanet();
Flex Builder was happy to code-assist me with this one, too. When I typed the period, Flex Builder showed me all of the possible properties and methods, stopPlaying() being one of them!
Now that interacting with the Flash symbol as a bonafide Flex component works, I added the Pause/Play button and Slider controls for rotation and scaling. The event handlers for those controls directly invoke the methods on the Flash component. For example, the rotation HSlider does this:
star.rotateStar(event.target.value)
I hope this gives you some idea of the possibilies with Flash CS3 and Flex. Read the information on the Flex Connectivity Kit for Flash CS3 page; there are more things you can do with this tighter integration between Flex and Flash.
Posted by pent at 02:19 PM | Comments (19)
March 29, 2007
LiveCycle Data Services Beta
Check out Adobe® LiveCycle Data Services 2.5 Beta on Adobe Labs. Here are just a few of the new features:
- Server-side PDF Generation
- AJAX Data Services
- A new SQL Adaptor
- JSP Tag Library
This is a beta test and your input is welcome and appreciated.
Posted by pent at 02:52 PM
February 28, 2007
USA Map for Flex 2.0.1
I've ported my original USA Map Project to Flex 2.0.1. You can read about it and download the code on my new (temporary) Flex 2 Blog.
You'll find a number of differences, most notably that the USA Map SWF is now an ActionScript 3 SWF, built with the Flash 9 preview. This eliminates the need for LocalConnection as the Flex app can work directly with the objects in the Flash movie.
Posted by pent at 03:00 PM
November 10, 2006
Example of Flex 2 Coldfusion Connectivity
Many months ago I wrote this sample application to explore the new connectivity package for Flex 2 and Coldfusion 7.0.2. It was a lot of fun and a great learning experience. I've taken this long to post it because there isn't any way to run it from here since it requires Coldfusion and a database. This blog doesn't have access to that and my personal ISP does not have Coldfusion.
So I've decided to just put up some pictures, explain the application, and let you download it and run it for yourselves.
What You'll Need
- Flex 2 (Flex Builder 2 is needed to get the full flavor of the Connectivity package)
- Coldfusion MX 7.0.2 (the 7.0.2 is critical)
- MySQL (or some database that works with CF)
In addition to the source and assets, the download contains a readme.txt, instructions.rtf, and in the sql directory, the schema for creating the database.
Mystic Support
Back when the CF Connectivity package was in development it was code-named Mystic and I've called my program Mystic Support. Since I'm a technical support engineer, I decided to put together my vision of what a support engineer needs to open new cases (tickets) and track their progress. We use a similar tool internally, but it isn't written in Flex :-(
The application is pretty simple and assumes that every support engineer is assigned one or more customers that they work closely with (modeled on Adobe's Gold and Platinum support plans). A customer can have zero or more tickets open at any given time. A ticket has some details (which product, version, description, etc.) and zero or more notes that track the progress of the case from start to finish.
At any given time the support engineer can see what tickets they have open and can easily open a new ticket for any of their customers.
The program also assumes that some administrator has populated the database with engineers and customers and made the relationship between them (assigning customers to engineers). This might be something you can do as an exercise.
When the program first starts you'll see the inital screen to let you log in. There's no security here - you just pick your name from the list. Should this be the first time the program is used, there may be a wait as Coldfusion and the database spin up.
Figure 1: Initializing |
Figure 2: Select an Engineer |
![]() |
![]() |
Once an engineer is selected, their customers and tickets are fetched and displayed in the panels on the left.
Figure 3: Open tickets and customers
To work on an open ticket, the engineer selects it from the list. This causes the ticket's details to be fetched and a ticket form slides out (I used a Move effect).
Figure 4: Editing a ticket
Coldfusion Connectivity
The list of engineers that is presented is read from the database. Rather than covering all the details of the application in this blog entry, I'll focus on this one detail, but it should give you an idea of how everything works.
I started with the database table, engineers. The table is pretty simple. Here is what it looks like using the RDS Viewer in Flex Builder 2.
Figure 5: The Flex Builder 2 RDS viewer
To make the application work I needed CFCs and ActionScript classes. Not being a wiz at CF (which proves anyone can use this stuff), I right-clicked the table in the RDS Viewer and selected the CF wizard.
Figure 6: The Coldfusion CFC Wizard
The wizard constructed the CFC for me. Notice that it actually created 2 CFCs: one represents an "engineer" as an "object". The other CFC provides an interface (gateway) with functions to get all of the engineers, get a specific engineer, update an engineer, or delete an engineer. How cool is that?
Once I had the CFC created I wanted a corresponding ActionScript object. So I right-clicked the CFC and picked the CF wizard that generates an ActionScript object. Here is the class it created:
Figure 7: The generated CFCs and ActionScript class
| The engineers_gateway.cfc | The engineers.cfc |
![]() |
![]() |
| The Engineers.as ActionScript class | |
![]() |
Notice the [RemoteClass] metadata - it names the CFC. This is key to the whole thing. When the program makes a RemoteObject call to the CFC (see figure 8), the CFC is set to return an Array of engineering CFC structures. The new 7.0.2 AMF gateway in Coldfusion will package them up into a binary format with clues as to what they are.
Figure 9: The Flex RemoteObject
When the data is received by the Flash Player, the clue for the CFC structure is matched with the RemoteClass metadata and rather than plain Objects, com.adobe.actors.Engineer objects are created with their properties populated from the data.
Figure 9: Flex Builder 2 debugging showing the contents of the returned result
Figure 9 shows what this looks like in the Flex Builder 2 debugger perspective. The program has a break-point in the result handler of the RemoteObject. You can see that when the event.result is expanded, it is in fact, an Array of Engineers.
Summary
You can whip up a Flex application fairly quickly using the Coldfusion Connectivity add-in for Flex Builder 2. Once you have your database tables in place, the CF wizards can create your CFCs and ActionScript classes.
In the code for Mystic Support you'll see that I have condensed the 'gateway' CFCs (there would be one for each of the structure CFCs) into a single file. That's another option you have: the ability to edit the files and use them as you please. After I worked with the code enough, I tossed out all of the functions I wasn't going to use and repacked them into a more efficient bundle.
If you have Coldfusion and want to give your applications some FLEXappeal, give the Coldfusion Connectivity package a try.
Posted by pent at 02:07 PM
November 07, 2006
Tree Drag and Drop Part 2
In Tree Drag and Drop Part 1 I covered how to drag items within the Tree control and from outside of the Tree control onto the control. In this article I cover how to drag items from the Tree.
To drag items from a Tree you have to set the Tree's dropEnabled property to false and its dragEnabled property to true:
<mx:Tree x="34" y="81" width="181" height="189"
dataProvider="{treeData.node}"
labelField="@label"
dropEnabled="false"
dragEnabled="true"
dragMoveEnabled="false"
/>
For this example, suppose the data in the tree represents US states, cities in those states, and restaurants within those cities. Further, suppose the target of the drop is a DataGrid which is defined like this:
<mx:DataGrid x="291" y="81" height="189"
dataProvider="{dataGridProvider}"
dragEnter="onDragEnter(event)"
dragOver="onDragOver(event)"
dragDrop="onGridDragDrop(event)"
dragExit="onDragExit(event)">
<mx:columns>
<mx:DataGridColumn headerText="Label" dataField="label"/>
<mx:DataGridColumn headerText="Type" dataField="type"/>
</mx:columns>
</mx:DataGrid>
As with any drag-and-drop operation, the event handlers are the key to making it work successfully.
The dragEnter event (on the DataGrid) determines whether or not the drop will be processed. In this example, only tree nodes which are restaurants will be accepted by the DataGrid. A typical tree node would look like this:
<node label="Lobster Pot" type="restaurant" />
where the @type attribute can be used to determine if the items being dropped are acceptable. The dragEnter event can be defined as:
private function onDragEnter( event:DragEvent ) : void
{
if( event.dragInitiator is Tree ) {
var ds:DragSource = event.dragSource;
if( !ds.hasFormat("treeItems") ) return; // no useful data
var items:Array = ds.dataForFormat("treeItems") as Array;
for(var i:Number=0; i < items.length; i++) {
var item:XML = XML(items[i]);
if( item.@type != "restaurant" ) return; // not what we want
}
}
// if the tree passes or the dragInitiator is not a tree, accept the drop
DragManager.acceptDragDrop(UIComponent(event.currentTarget));
}
If the control initiating the drag is a Tree control, then the dragSource is examined to see if holds any "treeItems" and if they are all of @type "restaurant". Should the test succeed, the DataGrid (the event.currentTarget) accepts the drop.
The dragOver event handles the feedback which the user is moving the dragProxy:
private function onDragOver( event:DragEvent ) : void
{
if( event.dragInitiator is Tree ) {
DragManager.showFeedback(DragManager.COPY);
} else {
if (event.ctrlKey)
DragManager.showFeedback(DragManager.COPY);
else if (event.shiftKey)
DragManager.showFeedback(DragManager.LINK);
else {
DragManager.showFeedback(DragManager.MOVE);
}
}
}
When the dragInitiator is the Tree, the feedback is always set to COPY. You can of couse, set the feedback to anything you like, but for the sake of the example, the feedback is for a copy operation so the information does not have to be deleted from the Tree.
The dragDrop event on the DataGrid handles the release of the mouse over the DataGrid. The data is examined and then added to the DataGrid.
private function onGridDragDrop( event:DragEvent ) : void
{
var ds:DragSource = event.dragSource;
var dropTarget:DataGrid = DataGrid(event.currentTarget);
var arr:Array;
if( ds.hasFormat("items") ) {
arr = ds.dataForFormat("items") as Array;
} else if( ds.hasFormat("treeItems") ) {
arr = ds.dataForFormat("treeItems") as Array;
}
for(var i:Number=0; i < arr.length; i++) {
var node:XML = XML(arr[i]);
var item:Object = new Object();
item.label = node.@label;
item.type = node.@type;
dataGridProvider.addItem(item);
}
onDragExit(event);
}
Since it is possible that another control has initiated the drag-drop, the dragSource is examined and the treeitems extracted. Then a loop is created to make new items to add to the DataGrid. Finally, the onDragExit method is called:
private function onDragExit( event:DragEvent ) : void
{
var dropTarget:ListBase=ListBase(event.currentTarget);
dropTarget.hideDropFeedback(event);
}
This function just makes sure the visual feedback is removed.
Dragging from the Tree is pretty easy - just examine the dragSource for "treeItems" and handle them according to what behavior your application should have. In this example, only"restaurant" type tree nodes are allowed to be dropped onto the DataGrid and, by being handled early in the DragEnter method, eliminates the need to test the value again in later steps.
Posted by pent at 02:27 PM
November 06, 2006
Filtering Collections
Example
This article is a basic introduction to Collection filtering.
This example uses 2 ComboBoxes. You may have seen ComboBoxes like these on automotive web sites. One ComboBox contains a list of makes (Ford, Kia, Volkswagen, etc.) and the other ComboBox contains a list of models (Mustang, Passat, A4, etc.). The second ComboBox however, only displays models which come from the maker choosen in the first ComboBox. In other words, the models are filtered based on the selection of the make.
| Figure 1: The Make ComboBox: | Figure 2: The Model ComboBox: |
![]() |
![]() |
Run the Example
Suppose you have a database that you query for all of the auto makers and all of the models. Normally you might get just the makers and fill the first ComboBox. When the user picks a make, another query is made for all of the models that match the maker. In this example, ALL of the models are returned, each keyed with their maker. Something like:
{make:"Ford", model:"Mustang"},
{make:"Ford", model:"Fusion"},
...
{make:"Volkswagen", model:"Beetle"},
{make:"Volkswagen", model:"Passat}
Suppose the data were assigned to ArrayCollections when retrieved from the server:
[Bindable] private var makeCollection:ArrayCollection;
[Bindable] private var modelCollection:ArrayCollection;private var queryResultHandler( event:ResultEvent ) : void
{
makeCollection = new ArrayCollection( event.result.makes );
modelCollection = new ArrayCollection( event.result.models );
}
and these Collections are the dataProviders for the ComboBoxes:
<mx:ComboBox id="makeCombo" prompt="Select" dataProvider="{makeCollection}" labelField="make" />
<mx:ComboBox id="modelCombo" dataProvider="{modelCollection}" labelField="model" />
At this point the modelCombo shows ALL of the models - no matter what is selected in the makeCombo. This is not what you want. Initially, without anything selected in the makeCombo, the modelCombo should be empty.
filterFunction
To do this you need two things: filter criteria and a filter function.
The filter criteria for this example is simple: the selection of the makeCombo. The filter function is almost as simple:
private function filterByMake( item:Object ) : Boolean
{
return( makeCombo.selectedItem.make == item.make );
}
Assuming that the item parameter in the filterByMake function is an element of the modelCollection, this function returns true whenever the item's make property value matches the makeCombo's selectedItem's make property value.
The filterByMake function needs to be assigned to the modelCollection as its filterFunction:
modelCollection.filterFunction = filterByMake;
The final piece is triggering the filter by calling the Collection's refresh() method:
modelCollection.refresh();
Putting it all together
Now that you have the parts, here is how it all fits together with some missing statements to make it work.
[Bindable] private var makeCollection:ArrayCollection;
[Bindable] private var modelCollection:ArrayCollection;private function queryResultHandler( event:ResultEvent ) : void
{
makeCollection = new ArrayCollection( event.result.makes );
modelCollection = new ArrayCollection( event.result.models );
// set the filterFunction and refresh the data so that the collection is initially empty
// (there is no selection on the makeCombo so the test fails for every item)
modelCollection.filterFunction = filterByMake;
modelCollection.refresh();
}private function filterByMake( item:Object ) : Boolean
{
var makeItem:Object = makeCombo.selectedItem;
if( makeItem == null ) return false;
return( makeItem.make == item.make );
}<mx:ComboBox id="makeCombo" prompt="Select"
dataProvider="{makeCollection}" labelField="make"
change="modelCollection.refresh(); modelCombo.selectedIndex=-1" />
<mx:ComboBox id="modelCombo" dataProvider="{modelCollection}" labelField="model" />
When a selection is made on the makeCombo, the change event handler calls the modelCollection's refresh() function and clears the selection from the modelCombo. This runs all of the modelCollection items through the filterFunction, filterByMake(), screening out all those models that do not have the same maker.
The modelCombo dropdown list is automatically updated because it is data-bound to the modelCollection.
Posted by pent at 11:29 AM
November 02, 2006
Tree Drag and Drop Part 1
This article is about dropping leaves - or rather dragging and dropping leaves.Because this is a somewhat complex topic and blog entries shouldn't be pages long, I'm breaking this topic into several entries. I'll cover dragging and dropping within a Tree control, from a Tree control to something else, and from something else onto a Tree control.
I










