Wizards & Skinning

Back when I was first looking at Flex, I knocked up a little prototype for work to show some people a possible solution to a problem they were having. They need to produce custom catalogs of products for clients that differ in the elements they require and the format of the file produced. Some may want XML, some fixed length fields, others delimited text (, | etc).

I wrote a wizard in Flex that would take you through the process of specifying a file and then use a Java method via BlazeDS to create the file (the Java code just constructed a demo file from the field data you provided for the prototype). In the end, it turned out that getting all the source data for the elements was the real sticking point so it never went any further.

I hadn’t done anything with it since but I started thinking about skinning apps (as a developer, I’m a terrible designer) and figured this wouldn’t be a bad app to try it on. If you go to Scale nine and check out the gallery, you’ll see a number of themes you can download for free. If you can’t find one you like, there should be enough there to show you how to modify one that’s close or write your own.

I downloaded the “Undefined” skin and dumped it all in my wizard project. The skin came with a main mxml file so it was pretty obvious to see how to use it in my own files.  All I had to do was use an mx:Style tag to import the css and specify the styleName property for my components.

I went from a screen that looked like this:

old wizard screen

to one that looked like this:

new wizard screen

While you might not like the look of either of them, you’ve got to admit, it’s pretty nice to be able to change the look of your app so easily.

The app itself makes use of states to change the layout of the screen and walk the user through the steps they have to take.  START is the first state and requests the customer id the catalog is for. There’s no back button in this state as we’ve got nothing to go back to. The next state is the one shown above, PICK_FIELDS. This displays the available fields in the left list from an ArrayCollection (fromAC) defined in the app and the right list is bound to another ArrayCollection (toAC). Drag/drop is enabled to allow the user to specifiy the fields they need and the order they want them in. The next state is SPEC_FIELDS, this contains text fields for a document header and footer (in the case of XML files) and a datagrid which has columns for the field name ( taken from toAC), max length, pad character, pre string and post string. This allows the user to specify the format of each of the elements they have selected. The results are stored in another ArrayCollection (fieldAC). The final state is GENFILE which in this case just displays some debug and removes the buttons but for a final version would display some messaging about the file created.

It’s worth noting also how the states use mx:SetEventHandler to change the behavior of the back and next buttons in the various states. Hopefully it’s fairly obvious what’s going on with that explanation and the source provided below and maybe it’ll provide you with some ideas for your own “wizard” style apps.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" borderColor="#B7BABC" themeColor="#9999CC" currentState="START">
<mx:Style source="/css/skinPreview.css/"/>
<mx:Script>
<![CDATA[

import mx.controls.DataGrid;
import mx.collections.ArrayCollection;

[Bindable]
public var toAC:ArrayCollection = new ArrayCollection();

[Bindable]
public var fromAC:ArrayCollection = new ArrayCollection(["Product Id", "Price", "Description", "Long Description", "UOM", "ECO", "Minority"]);

[Bindable]
public var fieldAC:ArrayCollection=new ArrayCollection();

private function next():void
{
if (currentState==”SPEC_FIELDS”)
{
currentState=”GENFILE”;
}else
{
currentState=(currentState==”START”) ? currentState=”PICK_FIELDS” : currentState=”SPEC_FIELDS”;
}
}

private function back():void
{
currentState=(currentState==”SPEC_FIELDS”) ? currentState=”PICK_FIELDS” : currentState=”START”;
}

private function drawDetails(event:Event):void
{
next();
var toArray:Array=toAC.toArray();
for(var i:String in toArray)
{
var field:FieldData=new FieldData();
field.name=toArray[i];
fieldAC.addItem(field);
}
}

private function wipeDetails(event:Event):void
{
fieldAC.removeAll();
back();
}

private function createFile(event:Event):void
{
next();
ro.createFile(custText.text,docHead.text,fieldAC.toArray(),docFoot.text);
}

]]>
</mx:Script>

<mx:RemoteObject id=”ro” destination=”cif-creator”/>

<mx:states>
<mx:State name=”START”>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:HBox id=”custInput”>
<mx:Label id=”custLabel” text=”Customer Id” styleName=”myText”/>
<mx:TextInput id=”custText” />
</mx:HBox>
</mx:AddChild>

<mx:RemoveChild target=”{backBtn}” />

</mx:State>

<mx:State name=”PICK_FIELDS”>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:HDividedBox id=”divBox” horizontalAlign=”center” horizontalGap=”10″ verticalAlign=”top” label=”Field Chooser” borderStyle=”inset” width=”100%”>
<mx:List id=”fromList” dataProvider=”{fromAC}” allowMultipleSelection=”true”
dragEnabled=”true” dropEnabled=”true”
dragMoveEnabled=”true” styleName=”myText” width=”50%” height=”100%” fontSize=”12″ backgroundColor=”#E2E2E2″/>
<mx:List id=”toList” dataProvider=”{toAC}” allowMultipleSelection=”true”
dragEnabled=”true” dropEnabled=”true”
dragMoveEnabled=”true” styleName=”myText” width=”50%” height=”100%” fontSize=”12″ backgroundColor=”#E2E2E2″/>
</mx:HDividedBox>
</mx:AddChild>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:Label id=”pickHelp” text=”Drag the fields you need from the left column over to the right. You can also re-order by dragging if you need to” styleName=”myText” fontSize=”10″ textAlign=”center”/>
</mx:AddChild>

<mx:SetEventHandler target=”{nextBtn}” name=”click” handlerFunction=”drawDetails”/>
</mx:State>

<mx:State name=”SPEC_FIELDS”>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:HBox id=”headerInput”>
<mx:Label id=”headLabel” text=”Document Header” styleName=”myText” />
<mx:TextInput id=”docHead”/>
</mx:HBox>
</mx:AddChild>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:DataGrid id=”fieldDG” dataProvider=”{fieldAC}” editable=”true”>
<mx:columns>
<mx:DataGridColumn headerText=”Field Name” dataField=”name” editable=”false” backgroundColor=”#E2E2E2″ />
<mx:DataGridColumn headerText=”Max Length” dataField=”maxLength” editable=”true”/>
<mx:DataGridColumn headerText=”Pad Char” dataField=”padChar” editable=”true”/>
<mx:DataGridColumn headerText=”Pre String” dataField=”preString” editable=”true”/>
<mx:DataGridColumn headerText=”Post String” dataField=”postString” editable=”true”/>
</mx:columns>
</mx:DataGrid>
</mx:AddChild>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:HBox id=”footerInput”>
<mx:Label id=”footLabel” text=”Document Footer” styleName=”myText” />
<mx:TextInput id=”docFoot”/>
</mx:HBox>
</mx:AddChild>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:Label id=”specHelp” text=”Only enter data for fields you require. A Max Length of 0 means no limit will be applied” styleName=”myText” fontSize=”10″ textAlign=”center”/>
</mx:AddChild>

<mx:SetEventHandler target=”{backBtn}” name=”click” handlerFunction=”wipeDetails”/>

<mx:SetEventHandler target=”{nextBtn}” name=”click” handlerFunction=”createFile”/>
</mx:State>

<mx:State name=”GENFILE”>

<mx:AddChild relativeTo=”{changeBox}”>
<mx:Label id=”debug” text=”{docHead.text}” textAlign=”center”/>
</mx:AddChild>

<mx:RemoveChild target=”{btnBox}” />
</mx:State>
</mx:states>

<mx:Panel id=”mainPanel” width=”60%” height=”60%” backgroundColor=”#B9B9B9″ backgroundAlpha=”0.51″ alpha=”1.0″ color=”#000000″ layout=”vertical” cornerRadius=”20″ title=”CIF File Creator” borderColor=”#E2E2E2″ themeColor=”#4B4B4B” >

<mx:VBox id=”changeBox” width=”100%” height=”70%” horizontalAlign=”center” verticalAlign=”middle”/>

<mx:HBox id= “btnBox” width=”100%” verticalAlign=”bottom” horizontalAlign=”right” paddingRight=”10″>

<mx:Button id=”backBtn” label=”&lt;Back” click=”back()” styleName=”buttonOfficial”/>

<mx:Button id=”nextBtn” label=”Next&gt;” click=”next()” styleName=”buttonOfficial”/>

</mx:HBox>

</mx:Panel>

</mx:Application>

Posted in Flex. Tags: , . 2 Comments »

Thoughts on Catalyst

I haven’t played too much with this tool yet and it is still in beta so it’s hard to be too definitive with any opinions right now but there are some things I can say about it.

As a tool for creating prototypes and demos, I think it has a lot of promise. It doesn’t seem to take too long to create something that will give you an idea of the user experience you’re developing.

To use it for full on development projects, it has a few drawbacks. Not all components have a Spark version yet and it won’t do round-trips right now so if you try and take a project you went to FlashBuilder with back into Catalyst, it complains. It’s still beta and from Adobe’s marketing material, it seems like they intend to have all that working at some point so those  may be moot points later. A bigger potential problem in my view is how do you deal with an existing code base of lovingly crafted custom Flex4 components? If your designer uses Catalyst, it will be pumping out nicely skinned standard components and as a developer, you’re going to have to tease all of those out and replace them with your custom work where necessary.  It’s going to get tough if you’ve got skinning involved in those components too. You’re also going to get one big monolithic app that probably doesn’t match up too well with any design patterns.

This is not to say that with planning and careful consideration of what you’re doing, you couldn’t make it work but for bigger apps it’s not going to be a trivial task to take something from Catalyst and make it work effectively.

I think  if you were to use it to create the starting point for your reusable components and then still continue to do your main construction within Flasbuilder, you’re probably going to get the best results.

I’ll be interested to see if I can find anything on using it for larger projects and see what people come up with.

Wiring up a Catalyst Project

A designer buddy of mine (Paul Ramos) has been taking a look at Catalyst and put together a quick little app to try out the tool from a design perspective. He let me have the resulting project to try wiring it up and produce a working app.

The app was a basic Flickr search with an initial search box and a transition to the pictures returned for it. Catalyst created a project with some placeholder images so when I imported it to FlashBuilder, it looked like this:

Catalyst produced Flickr app

Catalyst produced Flickr app

The main MXML file is under src in the default package and it contains andArrayCollection of objects created to provide the placeholders you see above. I basically followed the steps outlined in Lee Brimelow’s tutorial I linked to in my prior Catalyst post to delete this and replace the data binding for the list with an ArrayCollection I declared to contain the results of the Flickr search.  The HTTPService needs to be declared within an fx:Declarations block for Flex4 but is otherwise the same.  There’s a Flickr app on the Adobe site so I used that as the basis for how to talk to Flickr. I found the relevant button click handler and put the HTTP service  send call in that, added a mouseOver handler for the text input to clear the text prompt (more on that later) and that was pretty much it for the main file. The end result looks like this (well with better formatting):

<?xml version=’1.0′ encoding=’UTF-8’?>
<s:Application xmlns:mx=”library://ns.adobe.com/flex/halo”
xmlns:d=”http://ns.adobe.com/fxg/2008/dt&#8221;
xmlns:fx=”http://ns.adobe.com/mxml/2009&#8243;
xmlns:s=”library://ns.adobe.com/flex/spark”
xmlns:th=”http://ns.adobe.com/thermo/2009&#8243;
width=”1000″ height=”600″ backgroundColor=”#ffffff”>

<s:transitions>
<s:Transition fromState=”Page1″ toState=”Page2″ autoReverse=”true”>
<s:Parallel>
<s:Parallel target=”{list1}”>
<s:Fade duration=”750″/>
</s:Parallel>
<s:Parallel target=”{button2}”>
<s:Fade duration=”750″/>
</s:Parallel>
</s:Parallel>
</s:Transition>

<s:Transition fromState=”Page2″ toState=”Page1″ autoReverse=”true”>
<s:Parallel>
<s:Parallel target=”{list1}”>
<s:Fade/>
</s:Parallel>
<s:Parallel target=”{button2}”>
<s:Fade/>
</s:Parallel>
</s:Parallel>
</s:Transition>
</s:transitions>

<fx:Script>
<![CDATA[

import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;

[Bindable]
private var ac:ArrayCollection;

private var firstTimeIn:Boolean=true;

protected function Button_click():void
{
photoService.cancel();
var params:Object = new Object();
params.format = ‘rss_200_enc';
params.tags = textInput1.text;
photoService.send(params);
currentState=’Page2′;
}

protected function Button_click_1():void
{
var state:String = currentState;
if ( state == ‘Page2′ ) {
currentState=’Page1′;
}
}

protected function photoService_resultHandler(event:ResultEvent):void
{
ac=photoService.lastResult.rss.channel.item as ArrayCollection;
}

private function photoFaultHandler(event:FaultEvent):void
{
Alert.show(“Oops! Can’t seem to get photos”,”Error”);
}

private function clearTextInput():void
{
if (firstTimeIn){
textInput1.text=” “;
firstTimeIn=false;
}
}

]]>
</fx:Script>

<fx:Declarations>
<s:HTTPService id=”photoService” result=”photoService_resultHandler(event)”
fault=”photoFaultHandler(event)”
url=”http://api.flickr.com/services/feeds/photos_public.gne”/&gt;
</fx:Declarations>

<s:states>
<s:State name=”Page1″ th:color=”0xcc0000″/>
<s:State name=”Page2″/>
</s:states>

<s:BitmapImage source=”@Embed(‘/assets/flickr_app_template/Background.png’)” resizeMode=”scale” d:userLabel=”Background” id=”bitmapimage1″/>

<fx:DesignLayer d:userLabel=”Input Field”>
<s:TextInput x=”145″ y=”43″ skinClass=”components.TextInput1″
text=”Input Your Search Text Here” id=”textInput1″ mouseOver=”clearTextInput()”/>
</fx:DesignLayer>

<s:Button x=”656″ y=”32″ skinClass=”components.Button2″ id=”button1″ click=”Button_click()”/>

<s:List x=”144″ skinClass=”components.DataList1″ id=”list1″ y=”152″ visible.Page1=”false” dataProvider=”{ac}”/>

<s:Button includeIn=”Page2″ x=”587″ y=”124″ skinClass=”components.Button3″ label=”Click here to return to front” id=”button2″ click=”Button_click_1()”/>

</s:Application>

The next part to work on is the image rendering which is under the components package in the RepeatedItem1 file. Here I switched out the s:BitmapImage for a standard mx:Image that would deal with the data returned by Flickr and change the data binding to point to the relevant element of the data. The S:RichText binding was updated for it’s data element too.  The user label is something Catalyst uses so we can leave that alone. The end result for that (again with better formatting) is:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" xmlns:mx="library://ns.adobe.com/flex/halo">
<s:states>
<s:State name="normal"/>
<s:State name="hovered"/>
<s:State name="selected"/>
</s:states>
<s:Rect d:userLabel="Item Highlight" width="115" height="128" alpha.normal="0" alpha.hovered="0.4" alpha.selected="0.8">
<s:fill>
<s:SolidColor color="0xCED7EE"/>
</s:fill>
</s:Rect>
<mx:Image source="{data.thumbnail.url}" d:userLabel="Shape 4" id="bitmapimage3"/>
<s:RichText d:userLabel="This is where the
Descriptive link goes" fontFamily="Calibri" color="0x000000" fontSize="12" trackingRight="0.5" kerning="off" textAlign="center" whiteSpaceCollapse="preserve" id="richtext1" x="3" y="101" text="{data.credit}">
</s:RichText>
</s:ItemRenderer>

That was it – not too much to it really. I initially tried to use a click handler to clear the prompt text but that would clear it and lose the text cursor so you couldn’t  enter anything. I tried it back in a Flex3 Halo component and it worked fine there so this may be a Flex 4 “feature”. The final working app looks like this:

Fully wired up Flickr app

Fully wired up Flickr app

RMAUG Flash Camp

I was at Flash camp here in Denver on Monday. I went in thinking $40 and a day off wasn’t too much to risk and was very happy the way things turned out. The conference was well organized with good speakers who were more than happy to take questions and chat after their talks. If there was another one going, I’d sign up for sure.

There’s actually another Adobe event tonight that I won’t be able to make unfortunately it’s the Adobe Flex4/ColdFusion9 tour at the Magnolia hotel in downtown Denver. You can sign up and get more info here

Learning Flex – Lesson 8, part II

Item Renderers

There are several ways to define item renderers, the simplest is to use a ‘drop in’ item renderer. Classes that implement the IDropInListItemRenderer can be specified for the itemRenderer property of a list. Button, CheckBox, DateField, Image, Label, NumericStopper, Text, TextArea and TextInput all can be used in this way (just specify the class name for the item renderer property – eg mx.controls.Image). The disadvantage to this method is that drop in renderers cannot pre-process the data they use. If your data contained image filenames but not their common path, you couldn’t use a straight Image drop in renderer as it wouldn’t know about the path to the image.

You can define a renderer inline to your component using the mx:ItemRenderer tag but you can’t really re-use that code. By defining your renderer within an mx:Component tag and supplying the id of this as your renderer you gain some re-use but the most flexible way is to define a separate renderer class in a different file and provide this class name to the list’s itemRenderer property.

The itemRenderer will be called for each of the items in the dataProvider and within the renderer, the individual item being rendered is available through a variable called data.

Using Repeaters

The mx:Repeater tag allows you to iterate through a dataset and define components using it’s items. This will create one component for each item in the dataset.

A repeater has two properties particularly useful when constructing components, currentItem which provides access to the the item the repeater is currently positioned at and itemIndex which is a zero based indicator of that position. An example repeater would be:

<mx:Repeater id="myRepeater" dataProvider="{myDataset}">
<mx:Button label="{myRepeater.currentItem.title}" id="repButton"/>
</mx:Repeater>

If myDataset contained items with titles “The Beginning”, “The Middle”, “The End” I’d now have created 3 buttons with these labels.

Components created with a repeater have a getRepeaterItem() method to allow you to determine which of them is being used. You can refer to event.target.getRepeaterItem() to access the properties of the component that fired the event.

To address a specific instance of a repeated component, you use array syntax with the id specified in the repeated component so if I wanted to access my “The Beginning” button, I would use repButton[0].

Learning Flex – Lesson 8, Using Controls and Repeaters with Datasets

Flex has a group of list based controls that inherit from ListBase and allow the user to scroll through a list of items and select one or more of them. These include DataGrid, HorezontalList, List, ComboBox, TileList and Tree.

All of these use a dataProvider property to define the source of their data. This allows a level of abstraction between the data provider (or model) and the component (or view). This facilitates things like populating multiple components from the same data, switching data providers at runtime and having changes to the data reflected in all components that use it.

Horizontal and Tile List Components

Both of these list components display a list of items, the specific elements of which are defined by the developer. Horizontal lists will display a scroll bar along the bottom if required to view all items. A tile list displays items in a dynamically created set of rows and columns of equal sized tiles. The direction property can be used to force horizontal or vertical layout and scroll bars can be added to one axis.

Displaying data elements in a list control

If you just want to display a single text element from a data provider, this can be achieved by specifiying the element name in the labelField property of the list.

To combine data from several fields, you need to set the labelFunction property instead. This should be the name of a function you write that accepts a parameter of type Object and returns a String. An example would be -

private function listMultiString(item:object):String{
return item.name+" $"+item.price;
}

To display items in other ways, you’ll need to define an item renderer.

Learning Flex – Lesson 7, Creating Components with MXML

Components let you modularize your application. This allows you to build and maintain components separately and hopefully re-use them in later endeavors. To allow this, they should be self contained and loosely coupled.

All Flex components are ActionScript classes. The base class for visual components is UIComponent. Below this, there are general groupings of classes based on their behavior.

To build a component, create an MXML file with the name of your component. As it’s a class, it should follow convention and start with an upper case letter.

A component looks like an ordinary MXML file but instead of the root tag being mx:Application, you use the component type you wish to extend or group functionality under.

To use the new component in a different file, you must add an XML namespace to be able to access it (similar to the way you’d use an import statement to access a pure ActionScript class). Normally you would group similar components together in subdirectories so you’d end up with something like charts/lineCharts/MySuperChart.mxml The namespace is declared alongside the mx namespace definition and for our example, would be xmlns:c="charts.lineCharts.*" the component can then be instantiated in the body just like a regular mx component - <c:MySuperChart/>

A custom component does not have to be a visual component. It could be a purely data related or computational class. In this case, we would use UIComponent as our root tag as this is the base class, it’s the lightest weight component we can use.

Pop up windows

The TitleWindow class can show a close button so it’s often used for pop ups. The close event is used to process this action. The PopUpManager class has a static method removePopUp() which can be called for the close event. The remove method requires a reference to the window to close which would normally be this to indicate that you are telling the TitleWindow to close itself.

We can create  a TitleWindow custom component to define our pop up and then use PopUpManager.createPopUp() to display it. The createPopUp method takes three parameters:

  • parent – a reference to the window to pop over
  • class – a reference to the class of object you want to create
  • modal – a boolean that indicates if the window is modal (consumes all input) or not (defaults to false)

In the file where we’re using our custom TitleWindow class, it could look like:

var popup:IFlexDisplayObject=PopUpManager.createPopUp(this,MyCustomTitleWindow,true)

If you need to pass data to your custom pop up, you would declare the popup variable to be the type of your custom class and then use a class cast to convert the output of the create function into your custom class.

Follow

Get every new post delivered to your Inbox.