June 17, 2008

itemEditors - Part 3

,

The previous article in this series discussed item editing events. Using events can make your application respond to what the user enters and help the user make fewer mistakes.

This article is about using itemRenderers as itemEditors - one class to do both display data and edit the data. I tend to think of it more as an itemEditor used as an itemRenderer. But that's just me.

Download source for these examples.

Further, I have to be honest and say I am not a big fan of the renderer-as-editor; I think renderers should present data and editors should edit it. There are a few occasions when I think it is a good idea to use a single class for both, but those times are very few in my opinion.

Examples

Here is an example of over-using the itemRendererAsEditor. The DataGrid on the left is a nice, clean DataGrid. All of the cells are editable and when you click or tab into a cell its editor appears. The DataGrid on the right uses itemEditors to render the cells and edit them. All you see are the editors: TextInput controls for some columns, a ComboBox for another, and a NumericStepper for the last. Lots going on, very busy to look at.

itemEditors only itemRenderers as itemEditors

Here is an example of using the CheckBox as both an itemRenderer and an itemEditor. I think the CheckBox works really well for this. It is clean, simple control and you can readily see whether a value is true or false. Plus you can just click it to change it. Straightforward implementation, good user experience.

Here is another example of using an itemEditor as a renderer. This List control represents a shopping cart. In it are all of the things you have added to your cart while shopping online at your favorite grocery store.

As you can see, the quantity of each item in the cart is represented by a NumericStepper. All the user has to do is change the quantity and the cart is updated. A delete button would also be a good idea here, too.

Shopping Cart

This complex editor/renderer class works as follows:

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" verticalAlign="middle" paddingRight="4" paddingLeft="4" >

    <mx:Script>
    <![CDATA[
        public function get quantity() : Number
        {
            return itemQuantity.value;
        }
    ]]>
    </mx:Script>
    
    <mx:CurrencyFormatter id="cfmt" precision="2" />
    
    <mx:Label text="{data.name}" fontWeight="bold" fontSize="12"/>
    <mx:Spacer width="100%"/>
    <mx:NumericStepper id="itemQuantity" value="{data.quantity}" 
        minimum="0"
        maximum="100"/>
    <mx:Label text="{cfmt.format(data.price*itemQuantity.value)}" width="66"/>
    
</mx:HBox>

As with every itemEditor, this one has a property used as the editorDataField. In this case it is the quantity property getter function. The function retrieves the value setting of the NumericStepper (with id itemQuantity).

As an itemRenderer, this component must also display the current quantity (as well as the product name, price, and sub-total). These values are displayed through data binding. The sub-total is actually an ActionScript expression, multiplying the price by the value of the NumericStepper. As the NumericStepper is changed so does the sub-total.

Now you are probably wondering how to get the grand total below the shopping cart to update as the NumericSteppers are changed. Simply changing the sub-total and the quantity field of the itemRenderer/Editor will not update the grand total. Remember that the editor does not commit the new value into the data provider until after the edit completes. In other words, if you increase the value of the NumericStepper for the Snow Peas row, the grand total will not update until focus leaves the Snow Peas row. This is so you can validate the information as shown in previous articles.

For a shopping cart like this, you want the grand total to update as the user changes the NumericSteppers. So you have to force the situation a little.

The first thing you do is have the itemRenderer class implment the IDropInListItemRenderer interface. This gives you access to the listData which contains a reference to the list itself and, through that, to the dataProvider.

The code demonstrating this is available in the download. Look for the ShoppingCartRendererExtra.mxml file.

Once you have the listData you can have the change event on the NumericStepper force an update on the dataProvider:

private function forceUpdate() : void
{
    // Access the collection - listData.owner is the List and from there you have its dataProvider.
    var ac:ArrayCollection = (listData.owner as List).dataProvider as ArrayCollection;
    
    // update the quantity field from the numeric stepper. This is what the List will automatically
    // do when editing completes, but since you want to see the grand total change as the NumericStepper
    // changes, you have to force things a bit.
    data.quantity = itemQuantity.value;
    
    // finally, tell the collection the data changed. this will cause the collection to
    // dispatch its own change event which is then picked up by the main application.
    ac.itemUpdated(data);
}

When the NumericStepper's change event triggers this event handler, the ArrayCollection has the item updated immediately, rather than waiting for the List to complete editing the cell. If the main application is listening for a COLLECTION_CHANGE event on the collection, the grand total can be calculated:

    <mx:ArrayCollection id="shoppingCartDB" 
        source="{shoppingCartArray}"
        collectionChange="updateCartTotal()" />
...
private function updateCartTotal() : void
{
    if( cartTotal ) {
        var total:Number = 0;
        for(var i:int=0; i < shoppingCartDB.length; i++)
        {
            var record:Object = shoppingCartDB.getItemAt(i);
            total += record.price * record.quantity;
        }
        
        cartTotal.text = cfmt.format(total);
    }
}

 

Conclusion

Take care when turning an itemRenderer into an itemEditor. The user should have a straightforward interface with a single purpose when editing a cell or record. I personally prefer to separate the functions, but there are times when using an itemRenderer as an itemEditor can make sense, even if you have to go the extra mile as with this shopping cart grand total example.


Posted by pent at 12:22 PM | Comments (9)

June 04, 2008

itemEditors - Part Two

Editing Events and Complex Editors

In the last article of this series you saw how to make some simple inline itemEditors. If you have read the series on itemRenderers, then you noticed how similar the two are.

The key to making an itemEditor work is a) naming the class using the itemEditor property and b) naming the value property of the itemEditor using the editorDataField property.

In this article I'll show you how to use events to do some simple data validation and to prevent certain cells from being edited. In the course of this you will see how to make more complex itemEditors.

You can download the source code for this example here.

A word of caution here: by "complex" I do not mean editors with many controls and layouts. I really mean slightly more complex than inline itemEditors. The reason being is that I think it is unfair to ask users to make complex edits within a list or cell of a DataGrid. An editor should be focused only one one thing: the contents of a cell. For example, if you are using the List control and presenting a shopping cart, it is not unreasonable to allow the user to change the quantity of the items in the cart by letting them edit that value right in the cell. What would be unreasonable is to allow them to change the item itself, the colors, quantity, special instructions, and so forth. Or in other words, allow them to shop for items right from the cart when you have a whole site that does that. The cart is just a checkout convenience. Sure, let them add an extra tub of ice cream or delete a bag of chips, but don't have them turn the bag of chips into a two boxes of whole wheat pasta.

The itemEditEnd event

Let's say you have a DataGrid which helps you mange inventory. One of things you can do is change part numbers, but you cannot allow a part number to be blank. Using the default itemEditor, the TextInput control, you can click on a cell in the "Part #" column, press the delete key and erase the part number. This is one technique to prevent that.

	<mx:DataGrid x="10" y="64" editable="true" dataProvider="{inventoryDB}" 
		itemEditEnd="verifyInput(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Product" dataField="product"/>
			<mx:DataGridColumn headerText="Part #" dataField="part"/>
			<mx:DataGridColumn headerText="Type" dataField="type"
				itemEditor="editors.ProductTypeEditor" editorDataField="type"/>
			<mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
		</mx:columns>
	</mx:DataGrid>

The list controls dispatch an itemEditEnd event whenever editing is about to be completed. The event happens before the data is commited back to the dataProvider. By handling this event you have the option of changing the data, validating the data, and stopping the commit if necessary. For this example, the verifyInput() function will make sure the product part number is not empty.

		private function verifyInput( event:DataGridEvent ) : void
		{
			// it is OK if the user cancels the edit
			if( event.reason == DataGridEventReason.CANCELLED ) return;
			
			// grab the instance of the itemEditor. For this DataGrid, only the
			// TextInput control is used as the editor, so it is safe to get the
			// editor no matter what column has been edited.
			var editor:TextInput = (event.currentTarget as DataGrid).itemEditorInstance as TextInput;
			
			// if  the edit is on the part number column, make sure it is not blank
			if( event.dataField == "part" )
			{
				if( editor.text.length == 0 ) {
					// call event.preventDefault() so the edit will not continue and store the
					// blank value
					event.preventDefault();
					// give the editor an error to display to the user
					editor.errorString = "You must enter a part number";
					return;
				}
			}
			
			// handle other columns here
		}

The event is a DataGridEvent and contains some very useful properties. The reason property tells you why the event was dispatched. If the user pressed the ESCAPE key or clicked outside of the DataGrid the reason will be DataGridEventReason.CANCELLED. You may want to ignore this event as I have done and just let the DataGrid to its default action which is to cancel the edit and restore the previous value.

If you have decided to handle the event then you will need the itemEditor to get to its properties. The event's currentTarget property contains the control which I have cast to DataGrid. The DataGrid has an itemEditorInstance property which I cast to TextInput which is the type of itemEditor for this example.

This event handler is called for any cell so you must determine if the edit is something you are interested in pursuing. I check the event's dataField property to make sure it is the "part" column. If so, I test the editor's text property to see if there are any characters in it. If there are no characters, two things happen:

First: the event.preventDefault() is called. This is how to prevent the edit from happening - prevent the DataGrid from storing the new value back into the dataProvider.  For the user, they will have pressed TAB or ENTER and nothing will appear to happen. The preventDefault() function will keep the itemEditor in place.

Second: I put an errorString onto the TextInput control. This is optional, but it does signal the user that there is something wrong. Afterall, they pressed the TAB or ENTER key and nothing happened.

The itemEditBeginning Event

There are times you might want to prevent a cell from being edited. You could set the DataGridColumn's editable property to false, but that prevents every cell from being edited. Suppose you just want to make some of the cells in the column uneditable? You can determine whether a cell is editable or not using the itemEditBeginning event.

    <mx:DataGrid x="10" y="64" editable="true" dataProvider="{inventoryDB}" 
        itemEditEnd="verifyInput(event)" 
        itemEditBeginning="allowForEdit(event)">
        <mx:columns>
            <mx:DataGridColumn headerText="Product" dataField="product"/>
            <mx:DataGridColumn headerText="Part #" dataField="part"/>
            <mx:DataGridColumn headerText="Type" dataField="type"
                itemEditor="editors.ProductTypeEditor" editorDataField="type"/>
            <mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
        </mx:columns>
    </mx:DataGrid>

Handling the itemEditBeginning event gives you the option of dynamically deciding the editability of a cell. In this example, the data has a field called permanent on each record. The idea is that permanent=true means the product name is an unchangable value so the product cell for that row cannot be edited. This is handled by the allowForEdit() function:

        private function allowForEdit(event:DataGridEvent) : void
        {
            // if the field to be edited is a product, prevent the user from making
            // changes if the permanent flag is true<. You can use more complex logic, 
            // of course.
            if( event.dataField == "product" ) {
                
                var item:Object = ((event.currentTarget as DataGrid).dataProvider as ArrayCollection)[event.rowIndex];
                if( item.permanent ) {
                    event.preventDefault();
                }
            }
            
            // handle other columns here
        }

Again, the event is a DataGridEvent and here I have checked the dataField property of the event to make sure it is the "product" field I am dealing with. I can then get the record from the dataProvider of the DataGrid using the currentTarget property of the event and cast that to DataGrid. I then cast the DataGrid's dataProvider to ArrayCollection and get the event.rowIndex value. I could also have used the inventoryDB ArrayCollection directly in this function since they are in the same file, but this is more generic.

Once I have the record I can query its permanent property and if it is true, call the event.preventDefault() function to disable editing of that cell. In the case, the default behavior of itemEditBeginning is to present the itemEditor; preventing the default behavior makes the cell uneditable.

Editing Limitations

While I was proof reading this article I thought of something you might try and do and offer a warning. When you are using these edit events and trying to determine if the event should proceed, you may be tempted to make a call to a backend or server process. For example, you may have a web service where you can validate a part number. You may be tempted, while inside of the itemEditEnd event, to make a web service call and validate what the user just entered. Seems logical, right?

Logical maybe, but it won't work. The reason is that data service calls are asynchronous. You can make the call, sure, but the result will be returned sometime later - well after your event handler has exited. In fact, your call won't actually be made until your function exits. Your call is queued and when the Flex framework exits the function the request will be made and then the result will be returned by your web service's result handler.

So there is no way to do this type of server-side validation while editing cells. You should query the server, when your application starts, for the data to validate against, then use that while the cells are being edited.

Conclusion

The ability to dynamically allow editing and to validate the edit is a excellent way to give your users a better experience. You can help them make fewer mistakes and give feedback during the editing process. You can prevent them from editing certain data and make it easier for yourself to write the application since you do not have to validate what the user cannot change.

In next article I'll cover itemRenderers used as itemEditors.

Posted by pent at 01:15 PM | Comments (4)

May 29, 2008

itemEditors - Part 1

inline itemEditors

I recently completed a series on itemRenderers - customizations to list controls which format the display of the list contents. Displaying and rendering content is very cool and with Flex you can do nearly anything you can imagine.

This new series covers itemEditors - a way to allow data to be changed directly inside of a list control. This first article covers inline itemEditors which are very simple, though quite useful, components you write directly inside your MXML files. The other articles in the series will cover more complex editing, validation, events, and using itemRenderers as itemEditors.

The source code to this article is available by downloading it here.

TextInput Editor

It is nice to edit directly in the list controls. You can imagine a DataGrid of warehouse inventory where you can adjust the content right in the grid without needing a special pop-up. The list controls have a built in editor - a TextInput control - which appears whenever the user clicks the mouse in an editable area, either a row (for a List), a branch (for a Tree), or a cell (for a DataGrid). All you need to do is set the list control's editable property to true. For a DataGrid you can exclude a column from being edited by setting the DataGridColumn's editable property to false.

Before editing a cell
After clicking on a cell, the editor opens and the content is ready for editing.

itemEditors differ from itemRenderers in that only one instance of the itemEditor is seen - just on the cell being edited. The itemEditor is not seen until the cell to be edited receives input focus. Then the itemRenderer is hidden and the itemEditor is moved to that position, sized to fit the area, and given the data for the record. When editing is finished (by moving focus to another location), the list control copies the new value from the editor to the dataProvider record.

Using the image above as an example, when the user clicks in a cell of the "Part #" column, the dataProvider[row][dataField] value is given to the text property of the itemEditor (TextInput) control. When editing is finished, the text property value from the itemEditor (TextInput) control is copied to the dataProvider[row][dataField]. The dataProvider, being a collection, dispatches an event in response to the update.

While the default TextInput control makes a fine editor, it really only works for the most simple of cases. It works fine for String values, for example, such as a book title, author name, or product number. If you need more control or want to validate the user's input, then you need to take matter into your own hands.

Flex Controls as itemEditors

Here is how you make an itemEditor which only accepts numeric values:

	<mx:DataGrid x="46" y="270" editable="true" dataProvider="{employeeDB}">
		<mx:columns>
			<mx:DataGridColumn headerText="Name" dataField="name"/>
			<mx:DataGridColumn headerText="Position" dataField="position"/>
			<mx:DataGridColumn headerText="Age" dataField="age">
				<mx:itemEditor>
					<mx:Component>
						<mx:TextInput restrict="0-9" maxChars="3" />
					</mx:Component>
				</mx:itemEditor> 
			</mx:DataGridColumn>
		</mx:columns>
	</mx:DataGrid>

A very common control to use for an itemEditor is the CheckBox. This is very useful for editing Boolean values. Here is an example of using the CheckBox to edit the values for an "In Stock" column of an inventory program:

<mx:DataGrid x="531" y="273" editable="true" dataProvider="{inventoryDB}">
		<mx:columns>
			<mx:DataGridColumn headerText="Product" dataField="product"/>
			<mx:DataGridColumn headerText="Part #" dataField="part"/>
			<mx:DataGridColumn headerText="In Stock?" dataField="inStock"
				labelFunction="inStockLabeler"
				itemEditor="mx.controls.CheckBox" editorDataField="selected" /> 
			<mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
		</mx:columns>
	</mx:DataGrid>

In this example the content of the cells in this column are rendered using a labelFunction (inStockLabeler) which could display anything such as "Yes", "No", "In Stock", or "Out of Stock". The itemEditor property is set to the mx.controls.CheckBox class. And there is another, equally important, property set on the DataGridColumn: editorDataField. This field indicates the property of the itemEditor class to use to fetch the value when editing is finished. In this case it is the CheckBox's selected property. When editing is finished, the DataGrid will use the CheckBox's selected property to replace the inStock property in the data record.

You may wonder why the example with the TextInput did not supply the editorDataField property. That is because the default value for editorDataField is "text" which just happens to be name of the property on the TextInput control holding the value.

You can use this same technique with a number of Flex controls. Here is one for an order quantity column using NumericStepper:

	<mx:DataGrid x="531" y="82" editable="true" dataProvider="{inventoryDB}">
		<mx:columns>
			<mx:DataGridColumn headerText="Product" dataField="product"/>
			<mx:DataGridColumn headerText="Part #" dataField="part"/>
			<mx:DataGridColumn headerText="In Stock?" dataField="inStock"/>
			<mx:DataGridColumn headerText="Quantity" dataField="quantity"
				itemEditor="mx.controls.NumericStepper" editorDataField="value"/> 
		</mx:columns>
	</mx:DataGrid>

Notice the editorDataField is "value" - the property of the NumericStepper which holds the current value of the control. Make sure you use the fully-qualified class name for the itemEditor property.

Complex Editor

Now suppose you want to do something a little more complex that doesn't have a ready-made Flex control available. Here is one which allows a credit card number to be entered using 4 separate 4-digit fields:

	<mx:DataGrid x="46" y="463" editable="true" dataProvider="{accountDB}" width="302">
		<mx:columns>
			<mx:DataGridColumn headerText="Account" dataField="account" width="100"/>
			<mx:DataGridColumn headerText="Credit Card" dataField="ccard" editorDataField="value">
				<mx:itemEditor>
					<mx:Component>
						<mx:HBox>
							<mx:Script>
							<![CDATA[
								public function get value() : String
								{
									return part1.text+part2.text+part3.text+part4.text;
								}
								override public function set data(value:Object):void
								{
									super.data = value;
									part1.text = value.ccard.substr(0,4);
									part2.text = value.ccard.substr(4,4);
									part3.text = value.ccard.substr(8,4);
									part4.text = value.ccard.substr(12,4);
								}
							]]>
							</mx:Script>
							<mx:TextInput id="part1" maxChars="4" restrict="[0-9]" width="40" />
							<mx:TextInput id="part2" maxChars="4" restrict="[0-9]" width="40" />
							<mx:TextInput id="part3" maxChars="4" restrict="[0-9]" width="40" />
							<mx:TextInput id="part4" maxChars="4" restrict="[0-9]" width="40" />
						</mx:HBox>
					</mx:Component>
				</mx:itemEditor>
			</mx:DataGridColumn>
		</mx:columns>
	</mx:DataGrid>

This inline itemEditor follows the same rules as other itemEditors and names the editorDataField as "value". The component chosen for the itemEditor is the HBox - which does not have a "value" property. To make this itemEditor work, a getter function named value is created to return the concatenation of the 4 input fields. Now when editing for the cell completes, the DataGrid can call upon the value property of the itemEditor and it will receive the combined fields.

You can also see that I have overridden the data setter function. In that function I split up the credit card number among the four TextInput fields. This is the technique you use to display the data to be edited. The editorDataField is the property used to retrieve the new value.

Conclusion

In this article you've seen how to create an inline itemEditor - from simply naming a class to creating a complex class right within the MXML tags. By naming the property of the editor class which contains the final editor value, the DataGrid can retreive the value from the editor instance and replace the current value in the data.

The next article covers more complex itemEditors and editing events.

Posted by pent at 05:39 PM | Comments (11)

April 02, 2008

itemRenderers: Part 5: Efficiency

,,,

If you are displaying a large number of itemRenderers - either in the DataGrid or AdvancedDataGrid - your application's performance may be adversely affected if you do not code these itemRenderers effeciently. Here are some tips that might help:

Switching Styles

Here's an itemRenderer which switches components depending on the value of the data field.

<mx:Canvas>
    <mx:Script><![CDATA
        private function lessThanZero() : Boolean {
           return data.price < 0;
        }
    ]]></mx:Script>
    <mx:Label text="{data.price}" color="#FF0000" visible="{lessThanZero()}" />
    <mx:Label text="{data.price}" color="#00FF00" visible="{!lessThanZero()}" />
</mx:Canvas>

This will be faster than setting the style. Some other things to keep in mind:

Extending UIComponent

By far the most efficient way to write an itemRenderer is to extend UIComponent using an ActionScript class. You'll have complete control of the code and the renderer will be as efficient as possible.

Let's start with the example above, switching styles, and write a simple itemRenderer extending UIComponent.

package renderers
{
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.UIComponent;

	public class PriceItemRenderer extends UIComponent implements IListItemRenderer
	{
		public function PriceItemRenderer()
		{
			super();
		}
		
	}
}

You'll notice that not only did I write the class to extend UIComponent, I also have it implementing the IListItemRenderer interface. It is necessary to do this because a list control will expect any renderer to implement this interface and if you do not, you'll get a runtime error as the list attempts to cast the renderer to this interface.

If you read the documentation on IListItemRenderer you'll see that is an amalgamation of many other interfaces, most of which UIComponent implements for you. But there is one interface extended by IListItemRenderer that UIComponent does not implement: IDataRenderer. This requires you to add the code to give the itemRenderer class the data property you've been using all along.

If you attempt to use this class without implementing IDataRenderer you'll get these errors when you compile the code:

1044: Interface method get data in namespace mx.core:IDataRenderer not implemented by class renderers:PriceItemRenderer.
1044: Interface method set data in namespace mx.core:IDataRenderer not implemented by class renderers:PriceItemRenderer.

Edit this class and change it to the following:

package renderers
{
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.UIComponent;
	import mx.events.FlexEvent;


	public class PriceItemRenderer extends UIComponent implements IListItemRenderer
	{
		public function PriceItemRenderer()
		{
			super();
		}
		
		// Internal variable for the property value.
	    private var _data:Object;
	    
	    // Make the data property bindable.
	    [Bindable("dataChange")]
	    
	    // Define the getter method.
	    public function get data():Object {
	        return _data;
	    }
	    
	    // Define the setter method, and dispatch an event when the property
	    // changes to support data binding.
	    public function set data(value:Object):void {
	        _data = value;
	    
	        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
	    }
		
	}
}

I took the code directly from the Flex documentation for IDataRenderer, so you don't even have to type it yourself.

With that out of the way we can add in the two labels.

  1. Add variables to hold the two labels.
    		private var posLabel:Label;
    		private var negLabel:Label;
  2. Modify the set data function to call invalidateProperties(). This is important because the change of the data has to make the text in the labels change AND to change their visibility.
    	    public function set data(value:Object):void {
    	        _data = value;
    	    
    	    	invalidateProperties();
    	        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    	    }
    Calling invalidateProperties() tells the Flex framework to call the commitProperties() function at the apppriate time.
  3. Override createChildren() and create the labels, adding them to the display list of the component. Notice that in addition to creating the labels, their styles and visible are also set.
    		override protected function createChildren() : void
    		{
    			super.createChildren();
    			
    			posLabel = new Label();
    			posLabel.visible = false;
    			posLabel.setStyle("color", 0x00FF00);
    			addChild(posLabel);
    			
    			negLabel = new Label();
    			negLabel.visible = false;
    			negLabel.setStyle("color", 0xFF0000);
    			addChild(negLabel);
    		}
  4. Override commitProperties() to set the labels' text and visibility. In the past you've been overriding set data to make this type of change, and you can do that in this class, too, if you prefer.
    		override protected function commitProperties():void
    		{
    			super.commitProperties();
    	        posLabel.text = data.price;
    	        negLabel.text = data.price;
    	        
    	        posLabel.visible = Number(data.price) > 0;
    	        negLabel.visible = Number(data.price) < 0;
    		}
  5. Override updateDisplayList() to size and position the labels. You must size the labels because their default size is 0x0. This is another thing a Container class will do for you. Since this is a pretty simple itemRenderer you can just set the labels' size to match the size of the itemRenderer.
    		override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ) : void
    		{
    			super.updateDisplayList(unscaledWidth, unscaledHeight);
    			
    			posLabel.move(0,0);
    			posLabel.setActualSize(unscaledWidth,unscaledHeight);
    			
    			negLabel.move(0,0);
    			negLabel.setActualSize(unscaledWidth, unscaledHeight);
    		}

All this probably seems a bit complicated just to do this, but keep in mind that using a container will add a lot more code than this.

UIComponent Notes

The UIComponent class is the basis for all visual Flex components - controls and containers. Here are some tips about using UIComponent as your itemRenderer.


Posted by pent at 11:50 AM | Comments (20)

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 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 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 (5)

March 03, 2008

itemRenderers: Part 1: inline renderers

,,

I'm starting a new series of articles on itemRenderers. Our documentation team has great examples so please check that information out first. I'm giving you my distillation of it.

Recycling Renderers

One thing many people try to do is access an itemRenderer from outside of the list. For example, you might want to make the cell in the 4th column of the 5th row in a DataGrid turn green because you've just received new data from the server. Getting that itemRenderer instance and modifying it externally would be a huge breech of the Flex framework and component model.

To understand itemRenderers you have to understand why they are what they are and what our intentions were when we designed them. BTW - when I say 'we' I really mean the Adobe Flex engineering team - I had nothing to do with it. Anyway, suppose you have 1000 records you want to show. If you think the list control creates 1000 itemRenderers you are incorrect. If the list is showing only 10 rows, the list creates about 12 itemRenderers - enough to show every visible row plus a couple for buffering and performance reasons. The list initially shows rows 1 through 10. When the user scrolls the list it may now be showing rows 3 - 12. But those same 12 itemRenderers are still there - no new itemRenderers were created, even after the list scrolled.

Here's what we do. When the list is scrolled, those itemRenderers which will still be showing the same data (rows 3 - 10) are moved upward. Aside from being in a new location, they haven't changed. The itemRenderers that were showing the data for rows 1 and 2 are now moved below the itemRenderer for row 10. Then those itemRenderers are given the data for rows 11 and 12. In other words, unless you resize the list, those same itemRenderers are reused - recycled - to a new location and are now showing new data.

If you want to change the background color of the cell in the 4th column of the 5th row, be aware that the itemRenderer for that cell may now be showing the contents of the 21st row if the user has scrolled the list.

So how do you make changes like this?

The itemRenderers must change themselves based on the data they are given to show. If the itemRenderer for the list is supposed to change its color based on a value of the data, then it must look at the data it is given and change itself.

inline itemRenderers

In this article we'll look at the answer to this problem using inline itemRenderers. An inline itemRenderer is one which is written directly in the MXML file where the list control occurs. In the next article we'll look at writing external itemRenderers. The inline itemRenderers are the least complex and are generally used for very simple renderers or for prototyping a larger application. There's nothing wrong with inline itemRenderers, but when the code becomes complex it is better to extract it into its own class.

In all of the examples we'll use the same data: a collection of information about books: author, title, publication date, thumbnail image, and so forth. Each record is an XML node which looks like this:

<book>
    <author>Peter F. Hamilton</author>
    <title>Pandora's Star</title>
    <image>assets/pandoras_star_.jpg</image>
    <date>Dec 3, 2004</date>
</book>

Let's start with a simple itemRenderer using a <mx:List> control. Here, the author is listed followed by the title of the book.

	<mx:List x="29" y="67" dataProvider="{testData.book}" width="286" height="190">
		<mx:itemRenderer>
			<mx:Component>
				<mx:Label text="{data.author}: {data.title}" />
			</mx:Component>
		</mx:itemRenderer>
	</mx:List>

This itemRenderer is so simple that a labelFunction would probably have been better, but it at least lets you focus on the important parts. First, an inline itemRenderer uses the <mx:itemRenderer> tag to define it. Within this tag is the <mx:Component> tag. This tag must be here as it tells the Flex complier you are defining a component inline. We'll discuss what this really means in a bit.

Within the <mx:Component> tag you define your itemRenderer. For this example it is a single <mx:Label> with its text field set to a data-binding expression: {data.author}: {data.title}. This is very important. The list control gives each itemRenderer instance the record of the dataProvider by setting the itemRenderer's data property. Looking at the code above, it means that for any given row of the list, the itemRenderer instance of its inline itemRenderer will have its data property set to a <book> XML node (such as the one above). As you scroll through the list, the data property is being changed as the itemRenderers are recycled for new rows.

In other words, the itemRenderer instance for row 1 might have its data.author set to "Peter F. Hamilton" now, but when it scrolls out of view, the itemRenderer will be recycled and the data property - for that same itemRenderer - may now have its data.author set to "J.K. Rowling". All of this happens automatically as the list scrolls - you don't worry about it.

Here's a more complex inline itemRenderer using the <mx:List> control again:

	<mx:List x="372" y="67" width="351" height="190" variableRowHeight="true" dataProvider="{testData.book}">
		<mx:itemRenderer>
			<mx:Component>
				<mx:HBox >
					<mx:Image source="{data.image}" width="50" height="50" scaleContent="true" />
					<mx:Label text="{data.author}" width="125" />
					<mx:Text  text="{data.title}" width="100%" />
				</mx:HBox>
			</mx:Component>
		</mx:itemRenderer>
	</mx:List>

This really isn't much different. Instead of a <mx:Label> the itemRenderer is an <mx:HBox> with an <mx:Image>, <mx:Label>, and a <mx:Text> control. Data-binding still relates the visual with the record.

DataGrid

You can use inline itemRenderers on a DataGrid, too. Here's one applied to a column:

	<mx:DataGrid x="29" y="303" width="694" height="190" dataProvider="{testData.book}" variableRowHeight="true">
		<mx:columns>
			<mx:DataGridColumn headerText="Pub Date" dataField="date" width="85" />
			<mx:DataGridColumn headerText="Author" dataField="author" width="125"/>
			<mx:DataGridColumn headerText="Title" dataField="title">
				<mx:itemRenderer>
					<mx:Component>
						<mx:HBox paddingLeft="2">
							<mx:Script>
							<![CDATA[
								override public function set data( value:Object ) : void {
									super.data = value;
									var today:Number = (new Date()).time;
									var pubDate:Number = Date.parse(data.date);
									if( pubDate > today ) setStyle("backgroundColor",0xff99ff);
									else setStyle("backgroundColor",0xffffff);
								}
							]]>
							</mx:Script>
							<mx:Image source="{data.image}" width="50" height="50" scaleContent="true" />
							<mx:Text width="100%" text="{data.title}" />
						</mx:HBox>
					</mx:Component>
				</mx:itemRenderer>
			</mx:DataGridColumn>
		</mx:columns>
	</mx:DataGrid>

As you can see, this is much more complex than the last two, but it has the same structure: <mx:itemRenderer> with <mx:Component> definition inside of it.

The purpose of <mx:Component> is to provide an MXML syntax for creating an ActionScript class right in the code. Picture the code that appears in the <mx:Component> block being cut out and put into a separate file and given a class name. When you look at the inline itemRenderer it does look like a complete MXML file, doesn't it? There's the root tag (<mx:HBox> in this case) and even a <mx:Script> block.

The purpose of the <mx:Script> block in this example is to override the set data function so the background color of the itemRenderer can be changed. In this case, the background is changed from white whenever the publication data for a book is in the future. Remember that itemRenderers are recycled, so the color must also be set back to white if the test fails. Otherwise all of the itemRenderers will eventually turn purple as the user scrolls through the list.

outerDocument

The scope has also changed. What I mean is, variables that you define from within a <mx:Component> are only scoped to that component/inline itemRenderer. Likewise, the content outside of the <mx:Component> is in a different scope, just as if this component were defined in a separate file. For instance, suppose you add a Button to this itemRenderer that allows the user to by the book from an online retailer. Buttons call functions on their click event, so you might define the button like this:

<mx:Button label="Buy" click="buyBook(data)" />

If the buyBook() function were defined in the <mx:Script> block of the file you would get an error saying that buyBook() is an undefined method. That's because buyBook() is defined in the scope of the file, not in the scope of the <mx:Component>. Since this is a typical use case there is a way around that using the outerDocument identifier:

<mx:Button label="Buy" click="outerDocument.buyBook(data)" />

The outerDocument identifier changes the scope to look into the file, or outer document, with reference to the <mx:Component>. Now beware: the function has to be a public function, not a protected or private one. Remember that <mx:Component> is treated as an externally defined class.

Bubbling Events

Let's look at another, even more complex example. This is a TileList using the same data.

	<mx:TileList x="29" y="542" width="694" dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" >
		<mx:itemRenderer>
			<mx:Component>
				<mx:HBox verticalAlign="top">
					<mx:Image source="{data.image}" />
					<mx:VBox height="115" verticalAlign="top" verticalGap="0">
						<mx:Text text="{data.title}" fontWeight="bold" width="100%"/>
						<mx:Spacer height="20" />
						<mx:Label text="{data.author}" />
						<mx:Label text="Available {data.date}" />
						<mx:Spacer height="100%" />
						<mx:HBox width="100%" horizontalAlign="right">
							<mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]">
								<mx:click>
								<![CDATA[
									var e:BuyBookEvent = new BuyBookEvent();
									e.bookData = data;
									dispatchEvent(e);
								]]>
								</mx:click>
							</mx:Button>
						</mx:HBox>
					</mx:VBox>
				</mx:HBox>
			</mx:Component>
		</mx:itemRenderer>
	</mx:TileList>

The itemRenderer looks like this when the application is run:

This itemRenderer is pretty close to the one used in the DataGrid, but the Buy button's click event doesn't use outerDocument to call a function. In this case the click event creates a custom event which bubbles up out of the itemRenderer, through the TileList, and is received by some higher component in the visual chain.

This is a very common problem: you have an itemRenderer which has some interactive control in it, usually a Button, LinkButton, etc. that is supposed to cause some action to take place when clicked. Perhaps it is to delete the row or in this case, buy the book.

It is unreasonable to expect the itemRenderer to do the work. Afterall, the itemRenderer's job is to make the list look good - period. Event bubbling allows the itemRenderer to pass off the work to something else. A custom event is useful here because the event is related to the data on the row - so why not include that data in the event; the receiver of the event won't have to go hunt it down.

Summary

Using inline itemRenderers is a great and quick way to give your lists a custom look. Consider inline itemRenderers as separate ActionScript classes - afterall, they are scoped as if they were. If you must refer to functions or properties in the containing file, use the outerDocument identifier to change the scope. If you need to communicate information as the result of an interaction with the itemRenderer, use a custom, bubbling, event.

And remember: don't try to get hold of itemRenderers - they are recycled for a purpose. Make them responsible only to the data given to them.

In the next article I'll discuss external itemRenderers.

Posted by pent at 08:26 AM | Comments (25)

February 29, 2008

Migrating from Flex 2 to Flex 3

Check this link before migrating your applications from Flex 2 to Flex 3. In my own experience the issues have been very minor and fixed within a few minutes. But your results will varying depending on what controls you've used and how large your application is.

http://learn.adobe.com/wiki/display/Flex/Backwards+Compatibility+Issues


Posted by pent at 12:05 PM

December 31, 2007

Scrolling Text Component

Here's another example of a Flex component. This one scrolls a message within a fixed area. The message can be scrolled vertically or horizontally. You can give it a try right here:

This component shows how to use custom properties, meta data to work with Flex Builder, and overriding functions.

Click here to download the source code. The download is a Flex Builder 3 Beta 3 project - if you do not have Flex Builder 3 Beta 3 from Adobe Labs, you can open the file as a regular archive and use the source code.

Posted by pent at 08:27 AM | Comments (27)

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

There is a beta for you to download on that link as well as links to demos.

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:

You should now be able to make reusable components for all of your Flex projects.


Posted by pent at 02:35 AM | Comments (5)

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 (2)

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:

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?

Download file

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.

Download Files

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:

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 (4)

October 22, 2007

Now Available in Chinese

Several articles have now been translated into Chinese. You can view them at http://www.zhuoqun.net/index.php/archives/tag/peter-ent on Dreamer's Blog.

It is quite an honor to have someone take time to do this work. Thank you.

Posted by pent at 09:55 AM

October 09, 2007

Flex and Hibernate

For the past month or so I've been working on a new example that shows how to use the Hibernate data assembler in LiveCycle Data Services ES. If you aren't familiar with Hibernate, it is a way to easily persist Java objects - either singularly or in collections and with complex relationships. For example, if you have a data base of employees using several tables, you can make a single Java class that represents the employees and tell Hibernate how to bring it all together. You can load and save these objects using Hibernate and all of the tables will be updated properly.

LiveCycle Data Services ES includes Hibernate. Using the AMF protocol between the Flex client and LC DS you can persist ActionScript objects, too. You can create an Employee ActionScript class and point to a LC DS destination which maps to Hibernate and save and load your ActionScript objects. There's a bit more to it than that, of course, which is why I wanted to make an example.

This example is complex enough that I have broken it into two parts. This is the first part and shows how to use Hibernate with LC DS along with a simple Flex application. The next part focuses a bit more on the Flex side with a more complex application but still using the same example code from this part.

Here are the basic steps:


  1. You need a database. I've used MySQL 5 for the example. You need to create tables and foreign key relationships. The schema which accompanies this article includes that.

  2. You need LiveCycle Data Services ES. Once that's installed you follow a few more steps to turn on Hibernate. There is nothing else to download.

  3. You make some Java beans that represent your objects. There is a Hibernate tool and Eclipse plugin which can do this but I did it all by hand. If this were a larger project I would opt for the tool. Hibernate persists Java objects, not ActionScript objects, which is why you need these Java classes. LC DS will take care of mapping your ActionScript classes to the Java classes.

  4. You make Hibernate configuration files which maps the Java classes to the database tables. The example I've written keeps these pretty minimal, which is good the first time out. In reading a Hibernate book I can see that very complex things can be done using these files.

  5. You make corresponding ActionScript classes. When you request data through the remote data service in LC DS you get back ActionScript objects. There are special ActionScript metadata tags which tell the compiler to expect this.

  6. You make a Flex application.

You also might want to visit this blog to get a more rapid introduction to Hibernate and Flex: Mind the Gap

About the Data

Before going further I should explain the data being used. I work in the Flex support group and so I modeled this example on some of the tools we use to open and track questions from our customers.

The tables include one for Accounts (customer companies), AccountContacts (people who have questions), Consultants (people like me), Cases (the questions asked), and CaseNotes (the progress of the Case). Accounts can also have a Consultant assigned to them and a Consultant can have multiple Accounts assigned. This is a one-to-many relationship. The Java and ActionScript classes reflect this: you'll see Sets in the Java classes and ArrayCollections in the ActionScript classes.

Setup and Installation

DownloadArrow.png Click arrow to download sample code
The download contains 2 zip files of Flex Builder 2 projects. If you cannot load the projects, unzip them into a clean directory and use the sources and assets directly.

To begin this example, install your database and create the tables from my schema. Create a database called "support" and if you are using MySQL 5 you should get no errors when you run the SQL statements. If you are using a different database you may have to change the SQL. This is one good reason to use Hibernate as it masks the database peculiarities from you.

Now install LC DS if you haven't already. Install it using the embedded JRun instance. This will make debugging and development simple.

Active the Hibernate components in LC DS as follows: (steps from documentation here).

Copy <install root>/resources/hibernate/*.jar to your web application's WEB-INF/lib directory (for example, on windows, this might be C:\lcds\jrun4\servers\default\samples\WEB-INF\lib\

Create a new Flex Builder Java project. Have the bin directory point to the WEB-INF/classes directory. Copy the contents of the classes directory to the WEB-INF/classes directory in LC DS. You'll see that there are both .class and .hbm.xml files. It is best if the Hibernate configuration files shadow the Java class files.

Modify your data-management-config.xml file that is in WEB-INF/flex directory. Here's a tip: in Flex Builder, add a new folder to your Java project, but click the Advanced button to make it a link to an existing folder. Browse to the WEB-INF/flex directory. Now you can edit the configuration files right from Flex Builder.

Start LC DS. You should see a lot of output, but no errors. If you do get errors, double-check your work.

Create a new Flex Builder Flex project. Indicate that you want to use LC DS, but compile locally in Flex Builder. You can compile on the server, too if you like. I prefer to leave my source files off the server.

Import the Flex sources into this new project. Notice how the bin directory points to the LC DS flex context root directory. Your compiled files will go right out to the server so all you need to do is run it.

In fact, all you need to do is run the TestingApp application. This is what you should see:

TestingAppScreenShot_thumb.jpg Click to enlarge

So how does all this work?

If you open the Flex application you'll see a couple of DataService tags. Note the destination values. Then open the data-management-config.xml file on the LC DS server and find the destination with the same name. Notice how this destination is tied to the Hibernate entity description.

When the Flex application requests all of the Account objects, LC DS uses the Hibernate adapter to get Hibernate to fulfill the query. Hibernate queries the database and assembles a collection of Java Account objects. LC DS then converts the Java classes into an AMF binary stream, including the class name meta data.

The AMF stream arrives at the Flash Player and the Flex code examines the metadata and matches it with ActionScript classes with the same metadata and creates the ActionScript objects.

What you get back in your Flex application is an ArrayCollection of ActionScript Account objects. But there is more than that. Notice how the Account class also defines a collection of AccountContacts. Hibernate has gone and fetches them, too.

I hope this is enough detail to get you going. Leave me some comments if there are parts I could explain better and I'll follow with another article. Once this part is well understood, I'll publish the next part which details a Flex application that makes use of this data.

Hibernate Mapping in Detail

Let's take a closer look at the Hibernate mapping files and how they relate to the application as a whole.

First open the data-management-config.xml file and locate the destination for account.hibernate:


<destination id="account.hibernate">
<adapter ref="java-dao" />
<properties>
<use-transactions>true</use-transactions>
<source>flex.data.assemblers.HibernateAssembler</source>
<scope>application</scope>
2. <metadata>
<identity property="id"/>
<one-to-many property="accountContacts" destination="accountContact.hibernate" read-only="true" lazy="true" />
<many-to-one property="consultant"
destination="consultant.hibernate" lazy="true" />
</metadata>

<server>1.
<hibernate-entity>support.Account</hibernate-entity>

<fill-configuration>
<use-query-cache>false</use-query-cache>
<allow-hql-queries>true</allow-hql-queries>
</fill-configuration>
</server>
</properties>
</destination>

#1: Notice in the <server> section the <hibernate-entity>? The value supplied is the name of the Hibernate entity class to use with this destination. Since it is "support.Account" you'll want to look into the support package for the entity configuration file. Open WEB-INF/classes/support/Account.hbm.xml:

<class name="support.Account" table="accounts"> This names the Java class associated with this Hibernate class entity and the database table it maps to.


<id name="id" column="accountid">
<generator class="native"/>
</id>

If you examine the database table description for "accounts" you'll see that its primary key is the account column. The Hibernate entity element, <id>, shows that the id property of the Java class maps to the accountid column of the "accounts" table.

Look back at the data-management-config.xml destination at #2, the <metadata> for the destination description. See how the <identity> element names the same property.

The data-management-config.xml destination <metadata> also names some associations:


<one-to-many property="accountContacts"
destination="accountContact.hibernate"
read-only="true" lazy="true" />
<many-to-one property="consultant"
destination="consultant.hibernate"
lazy="true" />

Go back to the Account.hbm.xml file and you'll see corresponding relationship mappings:


<set name="accountContacts" inverse="true" >
<key column="accountid" />
<one-to-many class="support.AccountContact" />
</set>

<many-to-one name="consultant"
column="consultantid"
class="support.Consultant"
not-null="false" />

Since an Account can have zero or more AccountContacts, the first association declares a one-to-many relationship (one Account to many AccountContacts). In Hibernate, this is represented as a Set and corresponds to the java.util.Set class in the Account java class. The Set's name (accountContacts) is the name of the property in the Java class; the set names the column to key off of and the one-to-many element identifies which Hibernate entity class to use for those elements.

Back in the data-management-config.xml file, the one-to-many association is repeated to let the Data Services Hibernate Assembler know the relationships should be formed.

The relationship between a Consultant and an Account is the reverse: many Accounts to one Consultant. So a many-to-one association is used. When Hibernate sees the many-to-one element it knows that the Java class property, "consultant" will be of the type, support.Consultant and be a single element, not a collection.

Where's the ActionScript?

When dealing with the server-side there is no ActionScript – that's in the Flex client. Hibernate uses Java classes. For example, support.Account.

The Data Services Hibernate Assembler takes the Java class instances returned by Hibernate (eg, support.Account) and creates a byte stream in AMF format. Part of the byte stream (or serialization) of the class instances contains the name of the Java class.

Back in the Flex client, the receipt of the byte stream causes a look-up of an ActionScript class with metadata that maps the Java class name to an ActionScript class. The Flex client code then instantiates an ActionScript class (eg, support.Account) and fills its properties with the corresponding values from the byte stream.

Posted by pent at 11:21 AM | Comments (12)

September 27, 2007

Sharing Data Between Applications

This is a topic that comes up from time to time and is applicable to both Flash and Flex as the solution lies within the Flash Player.

Let's say you have a customer's profile you'd like to store on their local machine. Perhaps your web site has multiple HTML pages, some with Flash or Flex applications and you would like those applications to use the same customer profile information.

Providing this is not a lot of information, the SharedObject is the best way to go. SharedObject is a class for, well, sharing data. A small data file is stored on the local computer and requires the end-user's consent (which is granted by default). The end-user also has control over how much space to allocate for ALL SharedObjects - not just for your applications. Bear these restrictions in mind when writing your applications - you may need to query the user for the information if it cannot be stored and retrieved.

Download Sample Flex Application

Using SharedObject is pretty easy. Here's how to store a String and a Number:

var so:SharedObject = SharedObject.getLocal( "MyAppData" );
so.data.customerName = customerData.name;
so.data.serialNumber = customerData.serialNumber;
so.flush();

And that's it. A local SharedObject is created with the name "MyAppData". The SharedObject has a public member, data. Noticed that I didn't set data directly, but set properties on data itself. That is very important - you cannot change the value of data, you must add properties to it - otherwise your information will not be saved.

Reading the information is just as easy:

var so:SharedObject = SharedObject.getLocal( "MyAppData" );
if( so.data.customerName ) customerData.name = so.data.customerName;
if( so.data.serialNumber ) customerData.serialNumber = so.data.serialNumber;

Notice that I tested each property to see if it exists before using it.

A common question is if it is possible to save ActionScript class instances. Consider this example: you have a Person class which has members like name and age. The class also has a member address which is typed to be the ActionScript class Address. What you'd like to do is this:

var currentPerson:Person;
...
so.data.person = currentPerson;

This generally works, but what is saved to the SharedObject is NOT an instance of the Person class. Rather, so.data.person is a plain ActionScript Object - all information with respect to the class has been removed.

When you want to read this information, you need to assign each part. For example:

currentPerson.name = so.data.person.name;
currentPerson.age = so.data.person.age;
currentPerson.address.street = so.data.person.address.street;
...

It is also not guaranteed that your class will be successfully (or completely) turned into nested ActionScript Objects.

A better way to serialize your class is by implementing a serialization technique. One way is to use flash.utils.IExternalizable; you could also use XML. You don't really need to implement this interface as SharedObject doesn't know anything about IExternalizable. It is just good practice to do so.

Implementing IExternalizable requires your classes to have two functions: writeExternal and readExternal. Basically you convert your class to a series of bytes which can be stored in the SharedObject and read back later. For example:

class Person implements IExternalizable
{
    public function writeExternal( output:IDataOutput ) : void
    {
      output.writeUTF(name);
      output.writeInt(age);
      address.writeExternal(output);
    }


    public function readExternal( input:IDataInput ) : void
    {
      name = input.readUTF();
      age = input.readInt();
      address.readExternal(input);
    }
}

The Address class would do the same. The IDataOutput and IDataInput interfaces are implemented by several classes. You can use ByteArray for use with SharedObject. Here's how to save your Person class instance:

var bytes:ByteArray = new ByteArray();
currentPerson.writeExternal(bytes);
so.data.person = bytes;
so.flush();

Reading them back and creating your Person record is also simple:

if( so.data.person ) {
    currentPerson.readExternal( so.data.person as ByteArray );
}

If you'd rather use XML than ByteArray, simply write functions to create the XML (eg, toXML) and parse XML (eg, fromXML) in place of the writeExternal and readExternal functions, respectively.

For a simple class this might not be worth the effort. But if you have a complex class it would certainly be better to encapsulate the serialization within the class and store the serialized data in the SharedObject.

Posted by pent at 11:08 AM | Comments (4)

August 20, 2007

Did you know?

Some formatting options may not be so obvious. These work-arounds come from my teammate, Nick Watson, in the U.K. I thought I'd share them with you.

The CurrencyFormatter does not supply leading zeros to values less than 1. For example, when you format the value 0.45 you get $.45 and not $0.45. There is no options on the CurrencyFormatter to add or supress leading zeroes.

The work-around is to change the decimalSeparatorTo property of the CurrencyFormatter. Here's a class which extends CurrencyFormatter and overrides the format function:


import mx.formatters.CurrencyFormatter;

public class LeadingZeroCurrencyFormatter extends CurrencyFormatter
{
public function LeadingZeroCurrencyFormatter()
{
super();
}
override public function format(value:Object):String
{
var n:Number = Number(value);
if( Math.abs(n) < 1 ) decimalSeparatorTo = "0.";
else decimalSeparatorTo = ".";

return super.format(value);
}

}


If you need to format Dates you might want to look at the DateBase class:
http://livedocs.adobe.com/flex/2/langref/mx/formatters/DateBase.html

First of all, if you need to know the names of the months or days of the week, you don't have to code them yourself. DateBase.monthNamesLong is a static property and you can use the values in your application. Having all of the month names in one location makes it that much easier to change them.

Secondly, if you want to change the names to another language, simply replace this Array with new values:


DateBase.monthNamesShort =["Ene","Feb","Mar","Abr","May","Jun","Jul","MyMonth","Sep","Oct","Nov","Dic"];

Now any DateFormatter calls you make will use these new values for the short month names.

Posted by pent at 12:14 PM | Comments (4)

July 02, 2007

New Items on Flex Dev Center

We've posted new articles on the Flex Developer web site. The articles cover topic of interest to the .NET audience, the improved designer/developer workflow in Flex Component Kit for CS3, and the human factors aspects of designing applications.

An interoperability guide to using Flex and .NET data types

Map objects in ActionScript to their server-side counterparts in .NET, bridging client/server domains in your Flex RIAs.

http://www.adobe.com/devnet/flex/articles/net_interop.html

Video interview: Flex Component Kit for Flash CS3

Flex SDK engineer Glenn Ruehle explains how the Flex Component Kit improves the workflow between Flex and Flash developers and designers.


http://www.adobe.com/devnet/flex/videos/flex_3_beta/

BPEL4People overview

Learn more about the specifications and integrate human interactions with the Web Services Business Process Execution Language (WS-BPEL) 2.0.

http://www.adobe.com/devnet/livecycle/articles/bpel4people_overview.html

Posted by pent at 09:11 AM

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:

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)

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.

Download this example

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:

  1. 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).
  2. 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 (30)

April 20, 2007

On Creating Flex Applications

I get a number of questions from people with large Flex applications that have run into a problem. For example, someone might have a TabNavigator and sometimes one of the tabs doesn't show up or a tab remains even if the child component has been removed.

Isolating the problem can be difficult because the Flex application is large and requires data from a server or some other resource that makes it nearly impossible to send to us in Flex Product Support.

If you are involved in a large-scale development project (or think you will be), then STOP. Don't go any further. I've got some advice.

Patterns

There are a number of books and papers about software development and they have fancy names like the waterfall method, or simply unit testing. These are good, sound, tested techniques that might at first appear to be so much academic fluff, but there is a reason people went to the trouble of creating these design and development patterns.

I'm not going to write another book on the subject, but rather distill them into something I hope makes sense and is easy enough for you to implement. This is how I work on a problem.

The Steps (or the Zen of Development)

The first step is research. That is, figuring out how something is going to work before incorporating it into the application. For example, suppose I think I'll want to use a Move effect. I'll write a separate application to explore how the Move effect works. I'll try out its options and may be I'll discover that it doesn't work as I thought it did. Or may be I'll discover that what I really want is a Move + Zoom effect and then I'll figure out how to use Sequence or Parallel effects. The point is, I look at the application and think about the parts that I'm unsure of. I may not know if a DataGrid with 30 columns will perform well or even been easy for the end-user to use. It is this exploration of the possibilities that can give you a better feel for how Flex works and really build up your skill set.

The second step is organization. I try to think of the parts of an application as coming from 3rd party vendors. If I need a DataGrid with special features I think about it as if I could buy it from someone. This is also known as the black box approach. In other words, I don't want any other part of the application to be able to muck with the insides of my special DataGrid. Nor do I want my special DataGrid to know anything about the rest of the application. Thinking about it this way isolates each major part and leads to the next step.

Hint: This is where custom events come into play. A custom event helps you identify what action is happening that relates to the new component classes. A custom event can be as simple as a new event type (eg, "inventoryUpdate") or it can contain additional information (eg, the part number and name of the item being updated). Each component or group of components should have their own custom event(s).

The third step is interfaces. This is how parts mesh or fit together. It involves how one black box component interacts with another one. Obviously there are parts of a component that have to be publically exposed. Think about the Flex componets. The DataGrid does not know a thing about your application. All you can do is interact with its public functions, properties, events, and styles. If you discover that the DataGrid does not do something you want, you can create your own class, extending DataGrid and add those features. But you should create your class with the same intention - expose just those public functions and properties that get the job done.

Hint: Using actual ActionScript interfaces can really help keep parts of the application isolated. An interface provides the contract between a component and its users, but it also allows the component developer to change how the component works (or to fix bugs) without adversely affecting the rest of the application.

The last step is testing. That is, testing each of these units. Write stand-alone applications to try your components out separately and in logical combinations as they might be used in your larger application. You may discover flaws, shortcomings, or eliminate unnecessary features at this stage. If you've done the research step, then you'll already have most of the testing code in place.

Hint: If in the testing phase you run into a problem you cannot solve, you have a self-contained example that you can send to someone for help.

Benefit

The benefit to this approach is that you have a much more reliable application. Plus you may have created reusable components for a future application.

Further, should you need the services of the Flex Support Team, you'll have ready-made test cases that you can send to us so we can quickly help resolve the problem.


Posted by pent at 05:13 PM

April 03, 2007

The Stack Components

Not too long ago I read a question from someone who wanted to know how to make the Accordion control display more than one child at time. The response said to use a VBox.

This question and the response intrigued me, so I set out to create a control which would allow any number of its children to be open simultaneously. After some tinkering I came up with the Stack components: VStack and HStack, show in the following figures.

VStack with 3 children open
VStack with middle child open, rest closed

 

HStack with 3 children open
HStack with middle child closed, rest open

The VStack component looks very much like Accordion, except zero, one, or any number of its children can be visible at once. HStack is the horizontal equivalent.

These might not be the most useful components, but there are some interesting things you can use in your own components. You can download the source (a zip file Flex Builder 2 project) here: Stack Component Source.

How It's Made

I created a base component called StackBase which extends UIComponent. StackBase has a single child: a Box container; VStack extends StackBase and makes the child a VBox and HStack extends StackBase and makes the child an HBox.

My goal was to capitalize on the layout management already built into the Box classes and to use "off-the-shelf" parts when possible, making the component easier write, understand, and maintain.

The DefaultProperty

I wanted the Stack components to behave just like any other Flex navigator component, such as Accordion. Meaning, I wanted to be able to use it like this:

<adobe:VStack ...>
      <mx:Canvas ...>
            <!-- canvas children here -->
      </mx:Canvas>
      <mx:Canvas label="Page 2">
            <!-- canvas children here -->
      </mx:Canvas>
      <!-- etc. -->
</adobe:VStack>

I used the [DefaultProperty] metadata tag to tell the Flex compiler which property should be used if none is specified. Now it might not look obvious, but those Canvas containers within the VStack definition do belong to a property - contents - of the StackBase class. Open StackBase.as and you'll see what I mean:

[DefaultProperty("contents")]
public class StackBase extends UIComponent ...

and further down you'll see the contents property defined:

[ArrayElementType("mx.core.Container")]
public function set contents( value:Array ) : void ...

The contents property is an Array and for the Stack components, it is an Array of mx.core.Container classes (or any class that extends Container, such as Canvas). Just try and put something other than a Container in stack and you'll get the same error as if you were using a ViewStack or Accordion.

Creating the Content

The StackBase createChildren() method creates the Box. The contents - children of the Box - cannot be created at this time in the component life cycle because the contents property may not be set. A better place is in commitProperties().

In case you didn't know, commitProperties() is called once all of the properties have been set and the component children created (or in response to invalidateProperties()). The children may not yet be visualized, but they are available to have their own properties set. This means the contents Array is set with the components to go into the Box and the Box is ready to accept children.

The process of creating the content actually involves creating a couple of additional components. The content given in the MXML file is not made directly the children of the Box. Instead, a Canvas is created and it is given the content children and a control to open and close the child. This is a StackHeaderButton control which is part of this package.

When the content has been created you have:

Box
    Canvas
         StackHeaderButton
         container-specified
    Canvas
         StackHeaderButton
         container-specified
    etc.

Sizing

The trick to this component is sizing the content properly. Suppose for example you have a VStack with 3 children. If all of them are visible, how much room do they take up? Suppose only one of them is visible?

The solution is that all of the open children evenly divide the space available, minus the space taken by the StackHeaderButtons. Using the example, when all three are visible they use approximately 1/3 each of the space. If one is closed, the remaining two occuply 1/2 of the the space.  You are welcome to take this component and modify it to do something differently.

Interactivity

The StackHeaderButtons not only visually separate the content but they also open and close the content. When you click on a header, the child slides either open or closed and the remaining children have their sizes adjusted. This should appear fairly smooth because I used a Resize effect to do this.

My algorithm goes something like this:

First count the number of children in the content which are closed ("collapsed" in the code). Then take the space occupied of the Stack control and divide it evenly among the open children, subtracting the space taken for the StackHeaderButtons, of course.

As this is being done, a Resize effect is created for each child. Afterall, when one child closes the open ones increase their size.

All of the Resize effects are placed into a Parallel effect so that all of the adjustments are done at once. When the calculations have completed the Parallel effect is played and the contents adjust to their new sizes.

VStack vs. HStack

I had originally planned this to be a vertical control, but after I saw how it all came together I decided to add in the HStack control. I changed StackBase to use some protected functions for determining the size and position of its child content and the HStack component overrides these functions and returns width instead of height or x instead of y.

Mostly the controls are the same and StackBase takes care of the majority of the work.

Skins

The StackHeaderButton uses a skin class for its appearance. A skin is a class whose sole job is to provide the visualization for a component. I started off using a simple Button for the header, but decided it was easier to rotate a Label than the label of a Button. You can change the appearance of the headers just by writting your own skin classes.

Summary

Even if you don't find these controls useful themselves, use them as a guide to building your own components. I have to admit that using Box, Canvas, and Resize made the job easier, but if you want to write the whole thing from scratch, go for it. Just pay attention to the Flex framework component life cycle.

Some things of note in this component are:

[DefaultProperty] and [ArrayElementType] meta tags. These tags make it easier for people to use the component.

Resize and Parallel effects. You can make a whole lot happen all at once and make the control appealing to use.

Skins. Think about how your component is visualized and then write those separately as skins. This will make customizing its appearance easier and it separates the function of the component from its presentation.

Embedded Fonts. For the HStack to look correct, the labels on the StackHeaderButtons are rotated 90 degrees. They would be invisible if you didn't use an embedded font for them. To make things speedy, the Flash Player uses system fonts for most of the text. But system fonts do not have vector paths (outlines) that describe the letters, so they cannot be rotated. By embedding a font you can rotate, skew, and scale text controls easily.

 

Posted by pent at 01:31 PM

March 22, 2007

Hotfix 1 for Flex 2.0.1 SDK Released

We've just released a hotfix for Flex 2.0.1. Check out the list of things that are addressed by this hotfix and download it if you are affected by any of these items.

Hotfix 1 for Flex SDK 2.0.1

Posted by pent at 09:58 AM

March 13, 2007

Enhance the User Experience with Effects

May be you haven't tried any of the effects available in Flex or, if you have, found Fade, Wipse, Move, Zoom, etc. to be fine, but a little ho-hum.

You've probably discovered that you can add effects when components are hidden (hideEffect) or shown (showEffect) and may be you've even tried effects in Transitions. If you haven't, experiment a little to see if you can make your Flex app have that little bit o' something that makes the application easier and more enjoyable to use.

You can also enhance the effects by using easing functions. For example, with easing functions you make a child of a ViewStack bounce into position. The easing functions are pretty easy to use, just follow the examples in the documentation.

For some exta pow! check out the blogs from Alex Uhlmann and Ely Greenfield. There are some amazing effects you can use with Flex.

Posted by pent at 09:49 AM

February 21, 2007

Data Binding Tip

Here's a simple way to enable/disable controls based on selection. For example, suppose you have a DataGrid and a number of buttons which operate on the data. Some buttons can only be used when a row is selected. Other buttons can only be used when there are no selections. Still others can only be used on certain selections.

The Problem

To handle this you may be tempted to code a number of if-statements or to use data binding to set up a variable and then set the variable at various times in your code. Something like this:

[Bindable] private var somethingSelected:Boolean = false;

<mx:Button label="Publish" click="publishItem()" enabled="{somethingSelected}" />

This is a good attempt. All the controls which depend on the selection state of the DataGrid are bound to this one variable. Changing the variable changes the controls' enabled state. However, you still have to decide when to change the variable. For example:

private function publishItem() : void {
     // get the selected item
     // publish it
     grid.selectedItem = -1; // clear the selection
     somethingSelected = false;
}

To complicate things, suppose another button should be enabled if the selection contains a specific value. You not only have to worry about the somethingSelected variable, you also have to worry about this other test. In other words, in the publishItem() function you have to include setting the other variable. This gets more complex the more conditions you have.

An Easier Way

There is an easier way. Right now the Publish button has a binding with the somethingSelected variable. What is needed is a binding between the somethingSelected variable and the selection state of the DataGrid. You can set that up with the <mx:Binding> tag:

<mx:Binding source="grid.selectedIndex >= 0" destination="somethingSelected" />

Now the value of somethingSelected is tied to the selection state of the DataGrid. Select a row and somethingSelected turns true and all of the buttons with enabled="{somethingSelected}" are enabled and all the buttons with enabled="{!somethingSelected}" are disabled.

The source of the Binding does not have to be a variable. It can be an expression as shown here. The source is simply the condition of the grid's selectedIndex being greater than or equal to zero.

Here is a slightly more complex example:

<mx:Binding source="grid.selectedItem.code == 1" destination="codeOnePicked" />

Now a record in the DataGrid while a field whose value is 1 will turn the variable codeOnePicked true.

<mx:CheckBox label="Code One?" selected="{codeOnePicked}" />
<mx:Button label="Publish" enabled="{somethingSelected && !codeOnePicked}" />

Here, the CheckBox is selected whenever any record from the DataGrid is selected that as a code field of 1; the Publish button is disabled when that happens.

Conclusion

If you have controls that depend on selections or other conditions in the UI, see if the <mx:Binding> tag can clean up your code and make it easier to read and extend.


Posted by pent at 12:25 PM

February 01, 2007

Coloring the Background of Cells

The Flex DataGrid is probably the most commonly used control (after Labels and Buttons). One question that keeps popping up is, "How do I color the background of the cells?" I'll answer that question plus show you how to color the background of columns and rows, too.

Click here to download the source to the samples shown.

Cell Background Color

You need an itemRenderer to change the background color of specific cells. An itemRenderer either applies to all the cells in a DataGrid (when specified in the <mx:DataGrid> tag) or all the cells in a column (when specified in the <mx:DataGridColumn> tag).

Here's an example of coloring the background of cells in the Year column. Those with a value less than 2000 are blue and those greater than 2000 are green.

Changing the background color of a cell is as simple as overriding the updateDisplayList function and drawing a filled rectangle. If all you want to do is color the background of a cell, then you can make an itemRenderer that extends mx.controls.Label. This code is for a simple itemRenderer, based on Label, that colors the background blue if the value of year is less than 2000 and green otherwise:

<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script><![CDATA[
     override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
     {
          super.updateDisplayList(unscaledWidth,unscaledHeight);
          var g:Graphics = graphics;
          g.clear();
          g.beginFill( data.year < 2000 ? 0x0000FF : 0x00FF00 );
          g.drawRect(0,0,unscaledWidth,unscaledHeight);
          g.endFill();
     }
   ]]></mx:Script>
</mx:Label>

Column Background Color

You do not need to make an itemRenderer just to color the background of every cell in a column. The DataGridColumn class uses the backgroundColor style and applies it uniformly to a column. In fact, the background really is a background - it has nothing to do with cells as the color is applied below the cells.

<mx:DataGridColumn headerText="Make" dataField="col1" backgroundColor="red" />

The Make column will have a solid red background. Cell highlight and selection color are applied on top of the background, so those indicators will be seen. But any row colors, such as the DataGrid's alternating row colors, will not be seen as the column's backgroundColor is solid and above the row color. Here's an example of coloring the columns:


Column Background Alpha

Unfortunately, the DataGridColumn does not use the backgroundAlpha style and the color is always solid. But it is pretty easy to change if you need to have some transparency to a column's background.

The first thing you need to do is create a class that extends DataGrid. You can do this either as an MXML file (with DataGrid as its root tag) or with an ActionScript class extending DataGrid.

In your class, overrride the drawColumnBackground function and adjust the alpha level:

override protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint,
                                                                            column:DataGridColumn):void
{
      super.drawColumnBackground(s,columnIndex,color,column);

      var background:Shape = Shape(s.getChildByName(columnIndex.toString()));
      if( background ) {
          background.alpha = 0.5;
      }
}

The function calls the super class version to do all the hard work, then sets the background's alpha to 0.5. Now the color will be semi-transparent, showing the alternating row colors, as seen in this image:

To make the class more reusable, use a public property for the alpha value. Add to your class:

public var columnBackgroundAlpha:Number = 1;

And change the function:

       backgroundAlpha = columnBackgroundAlpha;

The default behavior is to mimic the way the DataGrid works and apply a solid background color. To make the backgrounds more translucent:

<local:ColoredBackgroundDataGrid columnBackgroundAlpha="0.3" ... >

Assuming you called your class ColoredBackgroundDataGrid, the above MXML tag sets the alpha to 0.3, making it very transparent. Now you have a reusable class.

Row Background Color

Next to coloring the background of specific cells, coloring the background of an entire row is probably the most popular color-related question for the DataGrid. Here's an example of coloring the background of some rows based on the data in the row:

Setting the background color of a row is accomplished by overriding the drawRowBackground function:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                      dataIndex:int):void
{
      color = 0xFF0000;
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}

In this example the row will have a solid red background, ignoring the color value passed to the function. Of course, our aim is to look at the data for the given row and determine the color. So the function changes to:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                       dataIndex:int):void
{
      var dp:ArrayCollection = dataProvider as ArrayCollection;
      var item:Object;
      if( dataIndex < dp.length ) item = dp.getItemAt(dataIndex);
      if( item != null && item.year < 20000 ) color = 0xFF8800;
      else if( item != null && item.year>= 2000 ) color = 0xFFFFFF;
      else color = 0x00CC00;
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
      }

Now the color is determined by the value of the year field for each row: less than 2000 is orange, greater than 2000 is white, and if there is no item for the row it is green.

The dataIndex is may be larger than the number of items in the dataProvider. That's because you may have more rows showing than you have data. For example, if you can see 10 rows in the DataGrid but you have only 6 records in the DataProvider, 4 of those rows have no data, thus the item will be null.

The colors for this example are hard-coded and is determined by a specific data field, also hard-coded. You can make this class re-usable by giving your class a color function, which is like a labelFunction for a column, except this would return a color value instead of a String. To do this, add another public property:

public var rowColorFunction:Function;

Notice the data type is Function. You can use that in the drawRowBackground function:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                       dataIndex:int):void
{
      if( rowColorFunction != null ) {
           var dp:ArrayCollection = dataProvider as ArrayCollection;
           var item:Object;
           if( dataIndex < dp.length ) item = dp.getItemAt(dataIndex);
           color = rowColorFunction( item, rowIndex, dataIndex, color );
      }
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}

Now drawRowBackground looks to see if the rowColorFunction is defined. If it is NOT defined, then the standard color is used. If the rowColorFunction is defined, then it is invoked with some parameters and it is expected to return a color.

Here's an example with the result shown in the image above:

<local:ColoredBackgroundDataGrid rowColorFunction="determineColor" ... >

private function determineColor( item:Object, rowIndex:int, dataIndex:int, oldColor:uint ) : uint
{
      if( item == null ) return 0x00CC00; // green are empty rows
      if( item.year< 2000 ) return 0xFFCC00;
      else if( item.year >= 2000 ) return 0xFFFFFF;
}

The logic used in the hard-coded drawRowFunction is now in a function in the main application. This means you can use the ColoredBackgroundDataGrid anywhere you would normally use the DataGrid.

Advanced Column Background

A function similar to the rowColorFunction can be applied to columns. Here's one possibility:

Consider this columnBackgroundFunction:

private function columnGradient(column:DataGridColumn, columnIndex:int, columnShape:Shape,
                                               x:Number, y:Number, width:Number, height:Number) : void
{
      var m:Matrix = new Matrix();
      m.createGradientBox(width,height,0,x,0);
      columnShape.graphics.clear();
      columnShape.graphics.beginGradientFill(GradientType.LINEAR,[0xFFFF00,0xFF0000],[1,1],[0,255],m);
      columnShape.graphics.drawRect(x,y,width,height);
      columnShape.graphics.endFill();
}

This function draws a gradient-filled rectangle in the columnShape (see image above). Using a Shape allows lightweight graphics to be drawn as the background to the column; you can do the same for rows.

Change the drawColumnBackground function in your DataGrid class as follows:

override protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint,
                                                                           column:DataGridColumn):void
{
      super.drawColumnBackground(s,columnIndex,color,column);

      var background:Shape = Shape(s.getChildByName(columnIndex.toString()));
      if( background ) {
          background.alpha = columnBackgroundAlpha;
      }

      if( columnBackgroundFunction != null ) {
          var columnShape:Shape = Shape(s.getChildByName("lines"+columnIndex.toString()));
          if( columnShape == null ) {
              columnShape = new Shape();
              columnShape.name = "lines"+columnIndex;
              s.addChild(columnShape);
          }
          var lastRow:Object = rowInfo[listItems.length - 1];
          var xx:Number = listItems[0][columnIndex].x;
          var yy:Number = rowInfo[0].y;
          var ww:Number = listItems[0][columnIndex].width;
          if (this.headerHeight > 0)
          yy += rowInfo[0].height;
          var hh:Number = Math.min(lastRow.y + lastRow.height,
                                                  listContent.height - yy);
          columnBackgroundFunction( column, columnIndex, columnShape, xx, yy, ww, hh );
      }
}

As you can see, this is more complex as a Shape must be created, added to the Sprite, and location and dimension calculated.

Conclusion

While the DataGrid does provide the ability to color the background of columns, you cannot change the background alpha of the columns. You also do not need to use an itemRenderer to set the background of a row (or a column). Instead, by creating a class that extends DataGrid and overriding a couple of functions, you can easily color the backgrounds of rows or columns (or both) and make it generic enough to use the new class anywhere.

The advanced columnBackgroundFunction should give you some ideas of how advanced you can get when considering the possibilities for the background to your DataGrids.

 


Posted by pent at 10:28 PM

December 15, 2006

Inline itemRenderer

itemRenderers that change data

I recently had the pleasure of working with one of our customers, Ryan Green of Davita, who agreed to share this bit of code. I like it because it is straightforward, yet contains many interesting bits. I've made some alterations from Ryan's original code for this example.

Download this example here

I've had questions from people who have put Buttons or some other interactive component into a cell of a DataGrid and wanted to know how to get the button to change the DataGrid. A common use is a trashcan to delete the record. Here's one approach which shows a simple order catalog. Clicking on the "add to cart" link on a row decrements the quantity in stock. When the quantity reaches zero the link becomes disabled and changes to "out of stock".

Let's start with the DataGrid definition. You'll see an in-line itemRenderer, which for this case is a neat solution. You can also use a custom component with the same code.

<mx:DataGrid id="grid" dataProvider="{dp}" selectable="false" y="45" horizontalCenter="0"
                     width="390" height="169">
     <mx:columns>
           <mx:DataGridColumn headerText="Product" dataField="product"/>
           <mx:DataGridColumn headerText="Quantity" dataField="quantity"/>
           <mx:DataGridColumn headerText="Order" dataField="action" >
                  <mx:itemRenderer>
                        <mx:Component>
                                  <mx:LinkButton label="{data.action}"
                                                          enabled="{data.quantity > 0}"
                                                          click="sendEvent()">
                                        <mx:Script>
                                        <![CDATA[

                                                private function sendEvent() : void
                                               {
                                                      dispatchEvent( new CustomEvent(data) );
                                                }

                                        ]]>
                                       </mx:Script>
                                </mx:LinkButton>

                        </mx:Component>
                  </mx:itemRenderer>
           </mx:DataGridColumn>
      </mx:columns>
</mx:DataGrid>

This looks like:

The itemRenderer for the last column is a LinkButton. The label for the LinkButton is taken from the value of the record's "action" field. The LinkButton is enabled only when the value in the "quantity" field is greater than zero. When clicked, the LinkButton calls the sendEvent() method in its Script block.

Note: The itemRenderer is a LinkButton but you can also use a Container, such as Canvas, and place the LinkButton inside it plus any other components that are relevant to your data.

The click event handler, sendEvent, dispatches ChartEvent (a custom event). The CartEvent is given the data record from the row.

Note: In case itemRenderers are new for you, most Flex components, including those used for itemRenderers, have a data property. This property is set with the current record of the dataProvider corresponding to the row of the itemRenderer instance.

This is the CartEvent class:

public class CartEvent extends Event
{
      static public const ADD_TO_CART:String = "addToChart";

      public function CartEvent(data:Object, bubbles:Boolean=true, cancelable:Boolean=false)
      {
           super(ADD_TO_CART, bubbles, cancelable);
           this.data = data;
      }
      public var data:Object;
}

Not much to it really. The event type is "addToCart". This is not an event the DataGrid dispatches. So how do you listen for this event and use it? The key is the bubbles value of the event: it is set to true by default in the CartEvent constructor function. This means you can set an event listener on any component "above" the itemRenderer - the DataGrid or the Application - whichever makes sense for you.

Since the DataGrid does not it offically support "addToCart" you cannot put it as an attribute on the <mx:DataGrid> tag. But you can assign an event handler for it in ActionScript:

grid.addEventListener(CartEvent.ADD_TO_CART, customHandler);

with the customHander() function:

private function customHandler( event:CartEvent ) : void
{
      // the data record is included in the event; it can be modified and
      // put back into the dataProvider where it will be picked up by the
      // DataGrid

      event.data.quantity -= 1;
      dp.itemUpdated(event.data,"quantity");
      if( event.data.quantity <= 0 ) {
           event.data.action = "out of stock";
           dp.itemUpdated(event.data,"action");
      }
}

When the LinkButton in a row is clicked, it dispatches the CartEvent which is handled by the customHandler() function. The function decrements the value in "quantity" of the given data record and the dataProvider is updated. Should the value hit zero, the text in the "action" field is changed to "out of stock" and also updated in the dataProvider.

Clicking on the "add to cart" link for the Sprokets product enough times reduces its count to zero, changing the LinkButton to "out of stock" and disabling it.

Notice that when the data is being updated the property name is also given to the itemUpdated() method. By doing this you ensure that only that field is changed. Should the dataProvider collection have a Sort on it, and that field is not part of the sort, updating the dataProvider will not cause a sort to occur.

There are a number of variations on this example. For instance, the CartEvent could pass the row index instead of the data itself. Then the customHandler has to extract the data from the dataProvider using the index. In some circumstances the index will probably be more useful than the data. Or maybe you'll need both.

What about itemClick?

Now you might think that you can just do all this with the itemClick event. And you are right. Using itemClick you can get which row and which field was clicked. You can update the data and the change will be reflected in the DataGrid.

But the beauty of the approach shown here is that you have an interactive cell - a LinkButton in this case, but it could be more complex. Consider this approach if you have the need for a custom itemRenderer.


Posted by pent at 10:43 AM

March 02, 2006

Update to IE May Affect Flex Application

Microsoft has released an optional update to IE which will cause ALL ActiveX based controls (such as the Flash Player) to require a user to either click on the Flash Player or press Tab or Enter before the Flash Player will become interactive.

Adobe has responded with the hotfix. Please see this technical for the full information: http://www.macromedia.com/go/ace0407

Posted by pent at 07:29 AM

November 29, 2005

Flex 1.5 Flickr PhotoSearch Application

If you were present at MAX 2005 and/or have begun particpation in the Flex 2 Alpha project at Macromedia Labs, you will have seen a demonstration of Flex 2 that uses the Flickr service to display images.

I was intrigued by this because I saw an opportunity to build my own Flickr application in Flex. I decided to use Flex 1.5 first because I have not spent much time with Flex 2. That will come shortly.

I wanted to build a 'real world' application that any of Macromedia's customers might build. It is one thing to support a product and while I was an avid user of Flex before joining Macromedia, I haven't built an application in a long time. Flickr provided an excellent excuse because I did not have to build the back-end.

Flickr PhotoSearch with Macromedia Flex Launch the application


If you are interested in my application, you can download it from the link below. You will need your own Flickr account however, so set that up first. And you will need to get a Flickr API key as well as I have removed mine from the source code.

My goals for developing this application were:

I believe I achieved these goals, but you can be the judge of the last one when you look at my code. Remember, I'm a "support engineer" not an "application developer". So be kind.

One thing you will notice as you look through the code is that it is loosely coupled. That is, there are a lot of events being dispatched and no child component knows anything about its parent. For example, when you click on an image to see its larger version, the Thumbnail component dispatches a "click" event like a button. The Thumbnail does not invoke a method in its parent. This proves useful because the Thumbnail is used not only in the main Gallery, but in the Favorites panel as well. The approach left me a great deal of freedom to change components without affecting the rest of the application.

How to use PhotoSearch

Here's a quick overview of how you use the program:

When the program first starts you have to log into Flickr. A window is presented with instructions. Once you pass the login process, the window disappears and the application becomes active.

On the left side of the screen is the Search Panel and the Favorites Panel. At the bottom is the Trashcan where you can drag items from the Favorites or Search History to delete them. In the Search Panel you can enter a tag, such as a "dog" and press the Go (magnifying glass) button.

When Flickr returns the results of the search, the search term is placed into the search history DataGrid and the Photo Gallery displays the Thumbnails of the first (or only) page of images found. If the search results in more than one page, the bottom right corner allows you to select a new page along with a convenient Go button.

If you see a image you like, you can drag it from the Photo Gallery to the Favorites area. The list of images, as well as the search history, are remembered between sessions using the Flash SharedObject. So the next time you run the application on the same computer you will see your favorite images and past searches. At any time you can drag them to the Trashcan.

The Search Panel also has a small triangular button next to the search TextInput field. This exposes "advanced" search options. You can enter multiple tags and search for images that match all of the words (the default) or any of words. You can also enter a Flickr user name into the User TextInput field and display all that user's images. The Clear History button does what it says: it erases your entire search history.

When you want to see a large version of an image - whether it is in the Photo Gallery or the Favorites Panel - just click it with the mouse. The ImageView displays and dims the Photo Gallery. You can continue to select images from Favorites or the Photo Gallery and they will change in the Image Viewer. By the way, I lifted the ImageMixer component the ImageViewer uses right from the Flex samples that come with Flex.

The ImageView also has some controls. You can see what they are with their toolTips, but briefly that are print, info, dim, add to favorites, previous image, and next image. On the far right of the ImageViewer is the name of the user who uploaded the picture. If you click their name you can search for all of their photos as if you entered their name into the User TextInput field of the Search Panel.

The info button will display more details about the image. This includes the description (if any), the date the photo was taken (if given), who took the picture (if provided) and any tags associated with the photo. You can click on any of the tags and start a whole new search this way.

You should have noticed that when the ImageViewer appeared, the Photo Gallery got more transparent. I called this feature "dim" for lack of an imagination. I did this because I found it difficult to see the enlarged image on top of the often colorful Photo Gallery. But you may not like that, so the dim control on the ImageViewer will toggle that on and off, then remember your preference in the SharedObject along with your favorite images and search history.

Finally, you can move the ImageViewer by dragging its title bar and dismiss the ImageViewer by clicking the small X in its upper right corner.

That's the brief overview of the application. I've placed a lot of comments (but are there ever enough?) in the code to guide you. I've also made some extra notes below. I hope you find this to helpful as another example of a Flex application.

Download the Code

Disclaimer: Macromedia and Flickr do not support this program. I supply this as-is and may choose to change or replace it as I see fit. Please report any egregious bugs to me if you feel so inclined. You may use the source code for your own projects without the need for copyright; neither I, nor Macromedia, nor Flickr, are responsible for any data lost or corrupted through the use of this code.

Download the code here


Notes

Flickr

One of the most challenging aspects was logging into Flickr. You will find this all taken care of for you in the com.macromedia.flickr.Flickr class. Flickr also requires you to use an MD5 encoder. I found a Javascript version (sorry, I have already forgotten where I got it), but it was very easy to turn it into the com.macromedia.flickr.MD5 class.

Drag and Drop

I make use of the Flex DragManager by allowing you to drag Thumbnails from the Photo Gallery to the Favorites panel. You can also drag Thumbnails from the Favorites panel to the Trashcan to delete it. But you will notice that you cannot drag a Thumbnail from the Photo Gallery to the Trashcan. If you want to use drag and drop, the code in the Trashcan will help you understand it better.

Effects

There are not many effects in this program, but I think they are used, well, effectively. For example, when you have the ImageViewer displayed and pick another image, the first image fades out while the new one fades in. I picked up this code right out of the examples that ship with Flex. Another example if the ImageInfo component that displays details about an image in the ImageViewer. This component is zoomed up and down. You can also find examples in the Search Panel and the Gallery.

Components

If you have already written Flex applications, you will notice that I did not go all out making everything into a component. I decided to be practical and am still mulling over have complex the main application (PhotoSearch.mxml) component turned out. I can see a couple of new components could come of it, but I'm pretty happy with things as they are.

My rule of thumb was that anything I wanted to hide or make look like it was self-contained, would become a component. Here are some examples:

The Trashcan appears as an icon. But it has a lot of code that makes it work. If that code where in the main application file, it would become very messy. It is certainly possible to keep the code in the main file, but I wanted the Trashcan to handle its own affairs, so it became a component.

The Favorites component extends Tile, but is placed within a Panel which is declared in the main application file. I decided that even if Favorites + Panel became its own component, the management and display of the Favorite images required its own component. The same is true for the Gallery.

Once I started getting results back from Flickr, I knew I wanted to show a ProgressBar. One way to do that is to tell the progress bar, manually, when to advance itself. But the ProgressBar can be given a dataProvider object as long as that object has a bytesLoaded and bytesTotal method and dispatches "complete" and "progress" events. Since the ProgressBar would track the loading of the images from Flickr, I used the fact that the Gallery was already a component and just added the missing pieces. Now the Gallery can be a dataProvider to a ProgressBar. This is a really good demonstration of the loose coupling technique.

The SearchHistory is just an extension of the DataGrid but with some added functions. It has the ability to load and save its contents to the SharedObject (see the com.macromedia.flickr.UserData class). There is also a function that makes sure only unique criteria are added (and then saved).

Posted by pent at 09:47 AM | Comments (20)

November 22, 2005

Coding Advice

If you have been following along with the Flex 2 announcements, you have noticed that Flex 2 will be using ActionScript 3. Flex 1.5 uses ActionScript 2. The benefits of ActionScript 3 are too numerous to name here, but I thought I'd give a couple of pieces of advice for coding Flex 1.5 in anticpation of moving to Flex 2.

This advice should get you used to coding in AS 3 "mode" and reduce the number of changes you will have to make.

Data Type Everything

Make sure everything has a data type. For example, change:

var n = 4;
to
var n:Number = 4;

If you are unsure of what type something should be, make it an Object.

The downside is that not everything can be easily typed. For example, while there is an Event type, once you identify a variable as being of that type you cannot add your own properties to it. Flex 2 will overcome that by providing many more pre-defined classes.

If you run into a problem, then just leave the data type off for now and fix it if you port the code to Flex 2.

Events

In Flex 1.5 you can conveniently dispatch events by writing Objects inline:

dispatchEvent( {type:"select", code:"Something" } );

This will not be allowed in Flex 2. Instead, start writing the code like this:

var event:Object = new Object();
event.type = "select";
event.code = "Something;
dispatchEvent(event);

Public and Private

In Flex 2 you will be required to identify the scope of variables and functions. Right now you can get away with:

var dp;
function initApp() {
dp = new Array();
}

In Flex 2, this should become:

private var dp;
private function initApp() : Void {
dp = new Array();
}

Bindings

In Flex 1.5 you can bind to variables without any special syntax. For example:

var dp:Array = [{name:"George",age:18},{name:"Holly",age:23},...];
<mx:DataGrid dataProvider="{dp}" />

In Flex 2 you need to identify those variables which will be used in data binding by using the [Binding] directive.

But you can use [Binding] now and just get used to making the identification:

[Binding]
var dp:Array = ... ;

The [Binding] directive is harmless in Flex 1.5.

Conclusion

I know these seem minor, but if you get into the habit now, you'll be better off if you try and port your application to Flex 2.

Posted by pent at 06:41 AM | Comments (5)

October 31, 2005

Hilighting Bars in Charts

This quick blog entry shows how you can write a custom renderer for a Chart that highlights a bar when the mouse is dragged over a DataGrid. You can adapt the bits of this code to your needs.

Download file

id='ChartSample'
>


flashvars=''
src='http://weblogs.macromedia.com/pent/SampleApp.swf'
name='ChartSample'
/>

Posted by pent at 11:43 AM | Comments (1)

September 20, 2005

creationPolicy vs Form Data

This is a topic that occurs from time to time. Say you have a lot of data to present to a user. Perhaps this is a set of preferences, options, or a profile. There is either too much information to present on a single page, or the information is logically separated into categories. For example, a user's profile might contain their contact information, their login information, and settings. You want to present this information using one of the Flex navigation containers, such as Accordion.

The trouble comes when it is time to set the controls. Usually you have retrieved the user's current settings from the server. When the user picks any of the Accordion headers, you want the information from the server to set the controls (radio buttons, text input fields, etc.).

There are three ways to approach this. First, the wrong way. When you set up an Accordion you may be under the assumption that each of the panels' controls are available to set whenever you like. Here for instance, this code handles the result of retrieving the information from the server and attempts to set the controls in the Accordion:

function handleResult(result) {
	firstName.text = result.firstName;
	...
	userName.text = result.userName;
	...
	color.text = result.color;
	...
}
...
<mx:Accordion ...>
	<mx:Form id="contact">
		<mx:FormItem label="First Name:">
			<mx:TextInput id="firstName" />
		</mx:FormItem>
		...
	</mx:Form>
	...
</mx:Accordion>

This won't work. The problem is that by default, the Accordion creates all of its immediate children (the three Forms), but none of the Forms' children except for the first form (contact) because that one is initially visible. The handleResult() code for setting the firstName field will work, but the other fields will not get set because they won't exist until the user navigates to them.

<mx:Accordion creationPolicy="all">

One way to handle this is to set the creationPolicy for the Accordion to "all". This will instruct the Flex framework to not only create the Accordion's immediate children, but to create their children as well. Now the handleResult function will work because all of the components will exist. The problem with this is that it causes the application to take longer to start up as all of the controls need to be created. That's why the default creationPolicy is to set "auto".

<mx:Model id="prefs">
     <firstName>{firstName.text}</firstName>
	...
     <userName>{userName.text}</userName>
	...
     <color>{color.text}</color>
	...
</mx:Model>

The other way, and best way, to handle this, is to use a Model and leave the creationPolicy set to "auto". This way, the controls are populated from the Model and the resultHandler can fill the model when the results come back:

function resultHandler(result) {
	prefs.firstName = result.firstName;
	...
	prefs.userName = result.userName;
	...
	prefs.color = result.color;
	...
}
...
<mx:Accordion ...>
	<mx:Form id="contact">
		<mx:FormItem label="First Name:">
			<mx:TextInput id="firstName" text="{prefs.firstName}" />
		</mx:FormItem>
		...
	</mx:Form>
	...
</mx:Accordion>

Now when the user picks the "Settings" heading, the Accordion will create the controls and fill them from the Model. The creationPolicy of "auto" takes into account that a user might never pick a heading or a tab. Suppose for example, one of the panels of the Accordion has a DataGrid filled with a lot of data. Creating that can take time. If the user never decided to go there, you made them wait for no reason.

Using a Model also has the added benefit of being able to use Data Validation. For example, if the userName field is required and must be at least 8 letters long, you can specify this information and validate it against a <mx:StringValidator>:

	<mx:FormItem label="User Name:" required="true'>
		<mx:TextInput id="userName" text="{prefs.userName}" />
	</mx:FormItem>
	...
<mx:StringValidator field="prefs.userName" minLength="8" />

Summary

The moral of this story is that you should not try to manipulate the controls too much. Let Flex make your work easier. Use a Model to store the information being sent to or retrieved from the server. Let the navigation containers create their children as they need them, getting their values from a Model. Finally, if you are going to be sending data to the server, the Model makes it easier to use Flex's data validation tags.

Posted by pent at 10:34 AM | Comments (5)

August 12, 2005

File Upload is Here!

At long last, it is now easy to upload files from Flash and Flex applications. Thanks to a new feature of Flash Player 8, you can abandon your clunky, quirky, javascript and invisible HTML forms.

Follow this link to learn more about file upload as well as new features in Flash Player 8 that benefit Flex.

I have to tell you, as a vetern of adding file upload capability to Flash applications, this is truely a godsend. The Flash engineers have listened and really came through.

Oh yeah, your Flex apps run much faster and use less memory, too!

Posted by pent at 09:15 AM | Comments (6)

August 09, 2005

Dialing for Components

A while ago I wrote a DevNote article and a blog entry describing how you can build a component. What started out as a simple component, got increasingly more complex. To the basic component I showed how you can style and skin it and add events. All in all, it wasn't a bad bit of writing. I hope it was helpful.

I want to offer you another look at building components. This time using Flash to make the graphic skins and MXML for the structure. This technique does use ActionScript, but it is kept to a minimum and does not use ActionScript classes.

DialDetails.gif
The Dial Component

Click here to download the components and examples: Download file

The component up for inspection is the Dial component: a round disk with a programmatically positioned needle. See the first figure. The Dial is very plain, but that is a result of my lack as an artist. I created the visual elements of the Dial - the skins - in Flash. I chose Flash for two reasons: all of the assets are stored in 1 file; the assets are scalable since they are vector graphics and not bitmaps. You can however, use any image editor and make PNGs, JPEGs, or GIFs as your assets.

To begin, I created a new Flash document and made three symbols: the Dial face, the needle, and the cap which covers the base of the needle. You could do away with the cap or even make the cap part of the needle. I made my symbols to specific sizes: the Dial face is 150x150 pixels. The cap is 25x25 pixels. The needle is 63x3 pixels and was drawn pointing to the right, so it can easily be rotated (this is the 0 degree position).

Once the assets were drawn and saved as symbols within the Flash document, I published the document to a SWF file and placed it into the same directory as the Flex component source code.

The Dial component is an MXML file. The root tag is a Canvas. I chose Canvas because I can position its children using (x,y) coordinates and because I can easily position one child on top of another; the cap has to be above the needle which is above the face.

You can make the Dial component by creating a new MXML file and set the root to be a Canvas. For its children, add the following controls:

<mx:Image id="face" x="0" y="0" source="{dialFaceSkin}" />
<mx:Image id="needle" x="75" y="75" source="{dialNeedleSkin}" />
<mx:Image id="cap" x="62.5" y="62.5" source="{dialCapSkin}" />
<mx:Text />

Notice how I explicity position the components based on their sizes. The cap for example, is at (62.5,62.5) - the center of the dial is at (75,75) and the cap is 25x25 with its center being (12.5,12.5). So the position of the cap's upper left corner is (75-12.5,75-12.5) or (62.5,62.5). Notice that the source property for the Image tags are data bindings to the skins. You bring in the skins with a Script block:

[Embed(source="DialAssets.swf",symbol="dialFace")]
public var dialFaceSkin:String;
The Embed compile directive will cause the compiler to locate the symbol in the asset file and bring it into the final SWF. The variable below the Embed directive will be associated with the asset. Since the asset is dynamically bound to the Image, you see the dial face in the canvas.

Now you can save the Dial.mxml file and use it in a test application.


<mx:Application xmlns:mx="" xmlns:local="*">
<local:Dial id="myDial" />
</mx:Application>

Now you can write the code to position the needle. We want the Dial to have a minimum and maximum value just like other similar Flex controls (sliders and NumericStepper, for example). I wrote set and get methods for these properties. This follows the design pattern for Flex components and allows the properties to work with data binding since they will emit ChangeEvents when set.

This is the code snippet for the minimum property; the maximum and value properties are similar.


[ChangeEvent("minimumChanged")]
private var _minimum:Number;
public function set minimum(n:Number) : Void
{
_minimum = n;
dispatchEvent({type:"minimumChanged"});
invalidate();
}
public function get minimum() : Number
{
return _minimum;
}

The metadata ChangeEvent tells the compiler that this value can be used with data binding. Internally, we use _minimum to hold the minimum value as "minimum" is the name of the set function. This means you can do:

<local:Dial id="myDial" minimum="32" />

When that happens, the minimum set function is called, _minimum is set, and the change event is dispatched. So

<mx:Text text="{myDial.minimum}" />

will change with the new value.

The invalidate() call tells the Flex framework to re-draw the control. This may result in the position of the needle changing. For instance, if the previous value of minimum was 0 and is now 32, the needle will need move further clockwise.


function positionNeedle() : Void
{
var n:Number = (_value - _minimum)/(_maximum - _minimum);
var pos:Number = 135 + n*270;
needle._rotation = pos;
}

The positionNeedle method handles the draw event. Make sure you change the root tag to include this event handler: draw="positionNeedle()".

This method uses the current values of _minimum, _maximum, and _value to position the needle. You can see that since these values contribute to the rotation of the needle skin, you need to invoke invalidate() whenever any of these properties are changed.

That's all there is to making this component: you create your graphic skins in Flash, use a Canvas to position the elements of the component, and use a little ActionScript to set the properties and initialize the component.

Going Further

To liven things up a little, I created a couple more skins in another Flash document (I could have used the same Flash document, but I wanted to keep the assets for different components separate).

In your test application, add another instance of the Dial, but this time change the skins:


[Embed(source="TempAssets.swf",symbol="dialFaceSkin")]
var tempFace:String;
[Embed(source="TempAssets.swf",symbol="dialNeedleSkin")]
var tempNeedle:String;

<local:Dial id="tempDial" dialFaceSkin="{tempFace}" dialNeedleSkin="{tempNeedle}" />


When you run the application, you'll see this more colorful dial in addition to the original dial. If you think you would use these skins more than once, create a new component (see TemperatureDial.mxml), but use Dial as its root tag. You will find that all you need to do is embed the skin assets and assign them properly. By basing your new component on an existing component, you get all of that component's features and code, plus your extensions.

One thing you should have noticed right away is that I hard-coded the positions of the children (Images and Text) based on my knowledge of the size of the graphic skins. That's perfectly OK. But if you want real flexibility, you'll have to calculate those values.

Conclusion

Writing specialized components for Flex does not have to be difficult. You also do not have to follow the rules precisely, but use the framework as intended and make it work for you and use as much of it as you need. While I did not make my component so it can be any size, I did make it do what I wanted and I allowed it to be customized further.

Using a graphic editor, such as Flash, allowed me to concentrate on the function of the component, rather than on its looks.

Posted by pent at 01:29 PM | Comments (4)

July 21, 2005

Flex RemoteObject and ColdFusion Components

I am not a CF expert. So when I was asked to provide a sample program showing how to use ColdFusion Components (CFCs) from Flex, I decided it was time to learn more about Flex's big brother.

I'm sure a lot of you will roll your eyes at the simplicity of this, but if are just getting started combining the technologies, this should be a big help.

First, define your CFC. I won't go into the details about that, there are plenty of books on the subject. Here is a simple CFC to illustrate this example:

<cfcomponent>
 <cffunction name="getdata" access="remote" returnType="any">
    <cfset myArray = ArrayNew(1)>
    <cfset myStruct = StructNew()>
    <cfset myStruct.fruit = "Apples">
    <cfset myStruct.quantity = 1245>
    <cfset myArray[1] = myStruct>

    <cfset myStruct = StructNew()>
    <cfset myStruct.fruit = "Oranges">
    <cfset myStruct.quantity = 5601>
    <cfset MyArray[2] = myStruct>

    <cfreturn myArray>
  </cffunction>
</cfcomponent>

Know where your ColdFusion server is installed. For me, it is at http://localhost:8101/cf6 with /cf6 being the context root.

My CFC is in the file: cfdocs/petersamples/cfc/flextest.cfc

Know where your Flex server is installed. For me, it is at http://localhost:8700/flex with /flex being the context root.

In my Flex application I defined the following tag:

<mx:RemoteObject id="ro"
    endPoint="http://localhost:8101/cf6/flashservices/gateway"
    source="cfdocs/petersamples/cfc/flextest">
      <mx:method name="getdata" />
</mx:RemoteObject>

The endPoint is set to the Flash Remoting gateway that comes built into ColdFusion. The source points to the CFC starting just below the context root (/cf6).

The method names the cffunction(s) in the CFC.

At this point, you treat the CFC as you would any other RemoteObject: invoke the function (eg, ro.getdata(parameters)) and handle the result (eg, {ro.getdata.result}).

Posted by pent at 09:55 AM | Comments (2)

July 08, 2005

More Flex and Flash Interaction Tips

I recently helped a customer who had a Flash movie loaded into his Flex application that was not working as expected. The Flash movie had a MovieClip symbol which could be dragged with the mouse. This is pretty common in Flash movies: you detect that the mouse has been pressed over the MovieClip instance and then startDrag() to let the user move the MovieClip.

This was working sporadically in Flex. I determined that the problem happened when mouse event handlers were present in the Flex application: mouseOut, mouseOver, even ToolTips prevents the MovieClip from being dragged.

I showed this to a Flex engineer and he told me what was happening. When mouse event handling code was present, the DragManager was enabled and superseded the standard mouse handling. In other words, Flex took over mouse control.

There is a work-around however. You must remove startDrag (and stopDrag) and handle the mouse tracking yourself. Here's how to do that.

Suppose you have MovieClip mc which are allowing the user to drag via startDrag. You probably have an onPress event handler for it which initiates the drag and a onRelease handler which stops the drag:


mc.onPress = function() {
   this.startDrag();
}

mc.onRelease = function() {
   this.stopDrag();
}

To make this work in Flex, change to:

mc.onPress = function() {
   this.onMouseMove = function() {
      var point = {x:_level0._xmouse, y:_level0._ymouse};
      this._parent.globalToLocal(point);
      this._x = point.x;
      this._y = point.y;
      updateAfterEvent();
   }
}
mc.onRelease = function() {
   this.onMouseMove = undefined;
}

The mouse tracking method is set only when the user presses down on the MovieClip and is discarded when the mouse is released.

First, a point is created where the (x,y) values are global position of the mouse. _level0 is the Flex application level. The point is then translated into the co-ordinate space of the MovieClip's parent and then positioned. Finally, updateAfterEvent is called to insure smooth tracking of the object.

This not only works in Flex, but will work in Flash as well.

Posted by pent at 10:02 AM | Comments (3)

May 16, 2005

doLater vs setInterval

Here's a brief explanation of how you can delay the execution of a function and why you would want to do that.

Most programming languages have a way for you to execute code after a delay. These are timer functions - you set a delay and after that time period expires the code you specify executes. Java for example, has the Thread.sleep() method.

ActionScript is no different. If you want to make a clock for example, you'd like to have the second hand move every, well, second. Since the computer is capable of executing hundreds of instructions per second, you need to set a timer so that the code to position the hands of clock executes every second or 1,000 milliseconds.

setInterval

setInterval( obj:Object, fn:String, delay:Number [,arg1, arg2, ...] ) : Number

The setInterval method has three required parameters:

obj: This is the object in whose context the function is found. Often this is used.
fn: This is the name of the function defined on obj which is executed every delay milliseconds.
delay: This the amount of time between executions of the function.

You can also pass extra arguments to setInterval if the fn function requires them.

setInterval returns a unique Number identify the interval created.

Keep in mind that the function is executed repeatedly until told to stop using clearInterval:

clearInterval( intervalID:Number ) : Void

Pass the value returned by setInterval to clearInterval.

doLater

Unlike traditional programming languages, ActionScript is executed within the Flash Player. If you are at all familiar with Flash, you know that Flash is based on the movie concept of a timeline. You place your symbols and code in various frames. The Flash movies are run at specified frame rates. Flex movies are executed at 24 fps.

What you may not know, is that Flash updates the visuals (color, object placement, size, etc.) at the end of the frame. This gives the ActionScript in the frame a chance to execute and set up the visual changes. For instance, if you use ActionScript to change a line from blue, to black, to red within the code for a single frame, you will see only a red line. That's because each change the line's color is not changing the visual aspect of the line - it is merely changing the line's property. So changing the property from blue to black to red is overwriting the property. When the frame code has finished, Flash updates the visual aspect of the line to last value of the property - red in this case.

So what does this have to do with doLater? Everything. The doLater method allows you to execute code once the UI has been updated.

The doLater method is defined for the Flex mx.core.UIObject, so any class that extends a control, container, or chart can use doLater.

When to use doLater

It is not always obvious when to use doLater. Typically, if you try to do something and the UI has not updated, put the last thing you want to do into a doLater method.

For example: suppose you want to change the dataProvider for a List and then have the list positioned to show a particular item in the new list. You might try this first:

myList.dataProvider = new_data;
myList.vPosition = 45;

A couple of things could have: it could work as you expect (unlikely) or the new data will appear, but the list will not be positioned correctly (most likely).

When you change the dataProvider for a list, the UI is normally updated at the end of the frame in which the dataProvider was assigned. Since the instruction to change the position of the list will have happened before the UI changed, position 45 may not exist (or may not yield the correct location once the data has been formatted for the display).

You make this work properly by putting the change to vPosition in a doLater method:

function scrollLater() {
myList.vPosition = 45;
}

Now write the code to change the dataProvider and scroll the list:

myList.dataProvider = new_data;
doLater( this, "scrollLater" );

The doLater method's signature is similar to that of setInterval:

doLater( obj:Object, fn:String [,args:Array] ) : Void

obj: This is the object on which the function is to operate. The object is often this.
fn: This is the name of the function defined on obj.
args: This is an optional Array of arguments. For example: ["cats",345]. The function itself would be defined to take two arguments: fn( animal:String, quantity:Number ).

doLater has several important differences with setInterval:

  • The doLater function executes once; the setInterval function executes repeatedly until cleared.
  • The doLater function executes at the end of the current frame; the setInterval function executes after a specified delay.
  • The doLater function does not return any arguments; the setInterval function returns an interval ID.

An example of using doLater can be found in this blog under the Ticker
application. I use doLater to move text across the screen.

Summary

Use doLater if you do not need to specify an exact delay for the task and you need to do something once. Use setInterval if you want to repeatedly do something or if you want to delay task for a specific amount of time.

Posted by pent at 02:28 PM | Comments (11) | TrackBack

March 31, 2005

Efficient Component Layout

I recently worked on a project where there were problems getting the components to fit properly, align, and stretch were necessary. Here are some tips to making it easier to layout your application.

First, diagram what you what. Here is a layout that could be more optimized:

<mx:Application>
	<mx:VBox>
		<mx:HBox>
			<custom component 1>
		</mx:HBox>
		<mx:HBox>
			<custom component 2>
			<mx:VBox>
				<mx:ViewStack>
					.. stack children ..
				</mx:ViewStack>
			</mx:VBox>
			<custom component 3>
	</mx:VBox>
	<mx:HBox>
		<custom component 4>
		<custom component 5 >
		<custom component 6>
	</mx:HBox>
</mx:Application>

This layout can be made more efficient in a couple of ways. First, remember that <mx:Application> lays its children out vertically, so using a VBox inside of it is redudant. The same holds true for <mx:Panel> and <mx:TitleWindow>.

Second, any container with one child is unnecessary. All that you are doing is causing the Flex LayoutManager to do more work.

Here is the same application with a more efficient layout:

<mx:Application>
	<custom component 1>
	<mx:HBox>
		<custom component 2>
		<mx:ViewStack>
			.. stack children ..
		</mx:ViewStack>
		<custom component 3>
	</mx:HBox>
	<mx:HBox>
		<custom component 4>
		<custom component 5>
		<custom component 6>
	</mx:HBox>
</mx:Application>

The first <mx:VBox> was eliminated because it was redudant. The first <mx:HBox> was eliminated because it was unnecessary (it had only 1 child). The same is true for the inner <mx:VBox> that contained the <mx:ViewStack>.

Another layout concern is when and where to place width= and height= specifications. As a rule of thumb, I do not place those values in the component definition. For example, in the definition for CustomComponent.mxml:

<mx:VBox xmlns:mx="http://www.macromedia.com/2003/mxml" width="100%" height="100%">
   .. contents ..
</mx:VBox>

Placing the width= and height= parameters here means that this component will always try to take up all of the space in whatever container it is occupying. My rule is to let the parent component decide how its children should be positioned and sized:

<mx:HBox width="500" height="400">
   <CustomComponent width="100%" height="50%" />
   <OtherComponent width="100%" height="50%" />
</mx:HBox>

Placing the size attributes in the parent lets you easily reuse the component.

Another problem I see is when to use the width= and height= attributes. Take this MXML snippet:

<mx:HBox>
   <CustomComponent width="100%" />
   <OtherComponent width="100%" />
</mx:HBox>

This will have a layout problem because the <mx:HBox> has been given no size. You may think that the <mx:HBox> will take its size from its children, but that is not always the case. You may see scrollbars appearing where you don't want them or components not occupying all of the space.

Suppose the <mx:HBox> above were meant to fill the remaining space of its parent? Without specify width= and height= attributes on the <mx:HBox>, you will get unexpected results.

This is better:

<mx:HBox width="100%" height="100%">
   <CustomComponent width="100%" height="100%" />
   <OtherComponent width="100%" height="100%" />
</mx:HBox>

My other rule of thumb is to always specify width= and height= attributes. This helps the Flex LayoutManager be more efficient and it gives you more control.

Always specify sizes, even for GridItem and GridRow tags.

You can also use <mx:Spacer> to help you out. On the project I mentioned above, the <mx:Grid> was used to create a control bar. There were two parts: a right side and a left side. So the developer used a <mx:Grid> with a single <mx:GridRow> which contained two items: one aligned left and the other aligned right. This worked, but the <mx:Grid> is pretty ineffcient. A better way to lay this out is:

<mx:HBox width="100%" height="26" horizontalGap="2" marginLeft="3">
   <control 1>
   <control 2>
   <mx:Spacer width="100%" />
   <control 3>
   <control 4>
</mx:HBox>

This uses an <mx:HBox> and a <mx:Spacer> in the middle taking up the remaining amount of space. So the Flex Layout Manager places the first two controls in the HBox on the left, then places the other two controls on the right, and fills the remaining space with the spacer.

I threw in some other attributes on the <mx:HBox> in case you weren't aware you could use them. The horizontalGap= attribute will make sure there are only 2 pixels between the items and the marginLeft attribute makes sure the first control is 3 pixels from the edge on the left.

From a performance perspective, it is best to use absolute pixel sizes and positions. But this isn't always possible and certainly, it doesn't make applications resizable. So compromises can be made. If for example, you know that a text field will be 200 pixels wide and 26 pixels height, specify that. If want the text field to occupy the width of its container, then use 100%.

In conclusion, when you are creating a component or application, follow these rules:

First, diagram your layout.

Second, remove controls that are redudant, especially <mx:VBox> containers as immediate children of <mx:Application>, <mx:Panel>, and <mx:TitleWindow> as they place their children vertically by default.

Third, identify containers holding only a single child. All Flex components can be sized and most have the same attributes. Do not think you need a container just to a particular attribute, especially when the child of the container is itself extending a container.

Finally, make sure you specify width= and height= attributes on all components. Use pixel sizes if possible. And use the <mx:Spacer> to take up room between components rather than introducing extra containers.

Posted by pent at 11:57 AM | Comments (5) | TrackBack

March 22, 2005

USA Map

A while ago I wrote about how to use a Flash SWF in a Flex application. This is another take on it, and one I hope you will find useful.

Flex comes with a great set of charts, but no maps. The example uses a map of the USA (a Flash movie) that is controlled by Flex. The map is wrapped in a Flex component so it behaves like other Flex components. For example, there is a dataProvider to color the states; there are also click and rollOver/rollOut events.

This sample demonstrates the following:

  • You can combine Flash and Flex to make an effective application.
  • Flex can easily control a Flash SWF and the SWF can report events back to the Flex application.
  • Using an MXML wrapper around the SWF makes the SWF fit better with Flex applications and provides for great reusability.

Download the zip file and extract the contents into an empty directory. The application file is USADemoApp.mxml. The Flash movie which is the map, is wrapped in USAMap.mxml and used by the application as any component.

Download Flex application

Download Flash source for the USA map

Click to view sample

Posted by pent at 04:28 PM | Comments (25) | TrackBack

February 17, 2005

Operating in Parallel with RemoteObject

You probably know that data services in Flex are asynchronous. That is, when you make a data service request, the result does not come back immediately. You set up handlers for the results when they are returned.

As Flex applications become more complex, there is more reliance on communication with servers via the Flex data services.

One of those services, RemoteObject, is especially useful if your server-side code is running on the same application server as the Flex engine. RemoteObject calls are faster that WebServices and HTTPServices because the information is exchanged in binary.

But RemoteObject has a secret. Well, it has a not-so-well advertised behavior. While it is true that requests made with RemoteObject data services are asynchronous, they are not parallel.

That is, if you make 3 requests using HTTPService you get the results returned when each request completes. For example, if one request takes 2 seconds, another takes 10 seconds, and another takes 30 seconds, you get the first results back in 2 seconds, the next comes about 8 seconds later, and last comes some 20 seconds after that.

If make 3 requests using RemoteObject, you get the results when they ALL complete. That is, your results come back in 40 seconds.

The reason for this seemingly odd behavior is that all of the RemoteObject requests go to a single point in the server: a servlet known as amfgateway.

Amfgateway takes in all of the requests and queues them. It processes each in turn and when all have been run, returns the results back.

This is by design. Most applications are fine with this behavior. It is only when you are making several or more requests simultaneously AND those requests take an extraordinary amount of time to complete, do you have a problem.

However, there is a work-around should you need it. If you read the RemoteObject tag documentation, you'll see there is an attribute called endpoint. This names the URL to handle the remote request. The default is the amfgateway servlet.

It turns out, that if you explicity give the endpoint and you make each URL unique by adding a bogus attribute, a new connection will be opened to service the request. Thus your simultaneous requests get executed in parallel and your results come back when each is finished.

Here is an example:

<mx:RemoteObject endpoint="/flex/amfgateway?a=1" ... >
<mx:RemoteObject endpoint="/flex/amfgateway?a=2" ... >
and so forth.

In the example "/flex" is the web root of your Flex installation.

Posted by pent at 10:02 AM | TrackBack

December 31, 2004

Sorting by Date in the DataGrid

The DataGrid's built-in sorting capabilities do not extend to Date objects. The DataGrid can easily sort Strings and Numbers, but it converts Dates to Strings before sorting them - which is not typically what you want to happen.

This article shows you one way to get the best performance sorting Date objects.

Download sample files

Behind the Scenes

The DataGrid's sort algorithm is a good one. But it works best with numbers, something which can be compared in byte-code in a few instructions. Comparisons between Strings for example, require looking at the length of the Strings, then comparing them character by character until a difference is found.

Dates are represented internally as the number of milliseconds since January 1, 1970. Unfortunately, the DataGrid has no way to extract that information from a Date object, so the Date's default String representation is used.

A way to circumvent this is to represent the Date information as a Number in the DataGrid's dataProvider. Beginning with the server-side representation of the data, the information is transferred to the Flex application (using any of the remote data services) as a Number, stored in the DataGrid as a Number, sorted as a Number, and finally displayed as a Date in your choice of format.

Supply and Demand

If your application is supplying the DataGrid from a remote object, send the data not as a formatted String (e.g., "May 17, 2004" or "17-05-04"), but as pseudo-date number: 20040517. This is simply the 4-digit year, followed by the 2-digit month, followed by the 2-digit day. This number is easily handled by the DataGrid's sort algorithm.

If you send Dates as pre-formatted Strings with the intention of sorting them as Numbers, you have to convert those Strings into Numbers in ActionScript. Parsing Strings is a slow process, even if you do it when the data arrives in the Flex application. Not only is it slow, you also violate the separation of model from presentation when the data is already in presentation form.

Note: you can send the internal Date representation only if you are using Java on the server-side (using java.util.Date.getTime). This is because only Java stores Dates using the same representation as ActionScript. If you fail to send the correct value, the Dates will appear incorrectly in your Flex application.

With the coded value stored in the DataGrid's dataProvider, the sort can take place. But the value stored is not what you want someone to see. You need a cell renderer to format the number as a date.

Presentation

The cell renderer can be written in ActionScript or MXML. In the code sample, open PseudoDateRenderer.as to see how the pseudo-date is converted into a Date for display.

Define a DataGridColumn for your DataGrid to associate the cell renderer with the column.

<mx:DataGridColumn columnName="dateasnumber" headerText="Date"
cellRenderer="PseudoDateRenderer" />

You may think that the cell renderer is too complex or that because of the calculations, will take away from any performance gained by the numeric sort. Not true. The cell renderer is applied only to those cells visible. If you have 2000 records in your dataProvider, but are only showing a single column of Dates with only 30 rows visible, then you will have only 30 instances of the cell renderer. The DataGrid has a very clever algorithm for recycling the cell renderer instances as the user scrolls through the data. By setting up a reusable Date (theDate in the cell renderer) and using the setFullYear function, you simply change the date being displayed.

Conclusion

You can make the DataGrid as efficient as possible when it comes to handing Dates if you do a little work on the back end and format the display on the front end. This keeps the presentation of the data separate from the data representation and gives a huge performance boost.

Posted by pent at 04:08 PM | Comments (3) | TrackBack

December 23, 2004

Gauge Component

I recently had the privilege of attending a customer-training session given by one of our Flex architects. The session covered component creation in Flex. I volunteered a component I wrote a while back as a test case to have our expert help us write better components.

This gauge component is one outcome of that session (another is an article I wrote which will appear on the Macromedia site early next year). Feel free to download the component. It includes some skin files, too. I placed comments throughout to help guide you when building your own components.

GaugeSamples.jpg

Download file

Posted by pent at 03:25 PM | Comments (7) | TrackBack

Cell Renderers

Cell Renderers in Flex is always a hot topic. I was recently involved with several cell renderer issues. The download contains several interesting examples.

Download file

Note: The solutions to some of these were a team effort.

CheckBoxCellRenderer is your typical cell renderer which replaces boolean values with a check box.

DateChooserCellRenderer pops up a DateChooser when the user clicks the cell or tabs into it. This seems simple enough, but the problem was that you had to click the date twice to select it. The fix was to pop up the DateChooser in response to a mouseDown event and not in the setValue method.

ComboBoxCellRenderer places a ComboBox into a cell. That isn't too hard, but the difference with this one is that each row item can have its own unique values. Plus this item shows you how you can include validation and get those nifty red borders and pop-up error messages!

MultilineListRenderer is a renderer for a List - but within a ComboBox. That is, when the ComboBox (actually, MultlineComboBox) is clicked, the dropdown doesn't have 1 line, but 2. You can modify this to have as many lines as you need.

Tip: Your cell renderer gets its data from a field in each row item. If the there is no matching field when your cell renderer updates the item, you may get random information displayed in the cell. For example, if the CheckCellRenderer is put onto a column with a columnName="pickme" and the dataProvider has no "pickme" fields, the check boxes will be selected at random!

Posted by pent at 11:49 AM | Comments (3) | TrackBack

December 22, 2004

Ticker

Here is a simple ticker, or marque, that moves text across the screen. The key is the doLater method which just advances the text by a specific amount.

The sample test file shows how you can change the text on the fly and adjust its speed, too.

Download file

Posted by pent at 05:05 PM | Comments (1) | TrackBack

December 16, 2004

Accessing SWFs from Flex and Vice-Versa

I've seen a number of questions regarding the interaction between a SWF and Flex. The idea is that you create some Flash content, perhaps a navigation bar, and you want to have events that happen within the SWF processed by the Flex app in which the SWF is embedded. Or you have the reverse: controls in the Flex app modify the behavior of an embedded SWF.

Both types of interactions are possible. You will be able to cook up more elaborate and elegant solutions, but this should get you started.

Download samples

The sample file contains a Flash document and Flex application. The Flash document contains a spinning ball and a few functions. The Flex application controls the color of the ball and can pause or resume its spin. The ball in the Flash document is covered by an invisible button which when click, invokes a method in the Flex application.

The key here is the Flex Loader's content property. Once the SWF has loaded and the Loader fires its "complete" event, the content property is the SWF.

You can pass a reference to the Flex application (or any component) to the SWF in the complete event handler:

<mx:Loader source="Ball.swf" complete="event.target.content.flexApp=this" />

Within the SWF, you can now reference "flexApp" and invoke the "onBallClick" function.

Once the SWF has been loaded into the Flex application, you can invoke methods or set values in the SWF from the Flex application. For example, when you press the Pause (Flex) button, the SWF's pause() method is invoked:

loader.content.pause();

Use the sample files as a guide to building your own hybrid applications.

Tip: Try not to use any of the Halo (or V2) components in your Flash document. The Flex components and Flex components while similar are not 100% compatable. Besides, why would you use buttons, labels, and datagrids in Flash when you have Flex?!?

Posted by pent at 03:30 PM | Comments (9) | TrackBack