7 November 2011
You do not need extensive knowledge of Flex 3 or Flex 4.5 to complete this tutorial, but if you intend to incorporate aspects of the Flex Dashboard application into your own projects, then having a good understanding of Flex 3, and at least a basic understanding of what's new in Flex 4.5, is essential. You should also complete Part 1, Part 2, and Part 3 of this series before beginning this one.
Beginning
This article is Part 4 of a four-part series in which you import the Flex 3 Dashboard demonstration application available on the Flex Developer Center into Flash Builder 4.5, and then migrate the application to Flex 4.5 to take advantage of the Flex 4.5 Spark architecture and components.
Although a key feature of Flex 4.5 is its support for creating mobile applications in Flex, this article series does not cover converting the Dashboard application to a mobile application. A future article series will cover creating a mobile Dashboard application. Also, although Flex 4.5 introduces locale-aware formatters, they will not be used in this article series because the Dashboard application does not involve localization.
Part 1 of this series described how to import the Flex 3 Dashboard into Flash Builder 4.5 make just enough changes to get it to compile and run. Part 2 began the process of migrating the Dashboard code to Flex 4.5 by modifying the application background and TabBar to make use of the new Flex 4.5 Spark components. Part 3 continued the migration and described changes required to have the application's ViewStack use the new Flex 4.5 Spark NavigatorContent container. Part 4.5 (this article) completes the migration by adding Spark Panel, List, DataGrid, and VGroup components as well as custom skins and states.
If it has been a while since you completed Parts 1 and 2, you may want to quickly refer to the opening sections of those articles for a review of the Dashboard application, how the conversion is organized, and some tips for following the steps.
The sample files for this article include Dashboard-Part4-Start.fxp, which you can use as a starting point for the steps that follow. This Flex project file includes all the steps through Part 3 in this series.
In this section you'll create two custom button skin classes for the maximize and minimize buttons located in the Pod title bar area. These skins will implement the maximizeRestoreButton and minimizeButton styles from styles.css.
Note: Recall that the maximize button uses the ToggleButton control because the Spark Button control does not support the selected property as the MX Button does. This property is necessary to switch between the maximized and restored states for the button display.
skinClass properties for the buttons in CustomPanelSkin.mxml in the controlsHolder HGroup: <s:HGroup id="controlsHolder" right="9" top="4"
horizontalAlign="right" verticalAlign="middle" gap="3">
<s:Button id="minimizeButton"
skinClass="skins.CustomMinimizeButtonSkin"/>
<s:ToggleButton id="maximizeRestoreButton"
skinClass="skins.CustomMaximizeButtonSkin"/>
</s:HGroup>
minWidth="21" minHeight="21" from the opening <s:SparkButtonSkin> tag.</s:states> tag until just before the ending </s:SparkButtonSkin> tag.</s:states> tag: <s:BitmapImage includeIn="up, down" width="14" height="14"
source="@Embed(source='/assets/minimize_up.png')"/>
<s:BitmapImage includeIn="over" width="14" height="14"
source="@Embed(source='/assets/minimize_over.png')"/>
</s:states> tag: <s:BitmapImage includeIn="up, down" width="14" height="14"
source="@Embed(source='/assets/maximize_up.png')"/>
<s:BitmapImage includeIn="over" width="14" height="14"
source="@Embed(source='/assets/maximize_over.png')"/>
<s:BitmapImage includeIn="upAndSelected, downAndSelected" width="14"
height="14" source="@Embed(source='/assets/restore_up.png')"/>
<s:BitmapImage includeIn="overAndSelected" width="14" height="14"
source="@Embed(source='/assets/restore_over.png')"/>
When you launch the Dashboard you'll see that the Pod title bar area looks very much like the corresponding area in the Flex 3 Dashboard application.
Changes are necessary in CustomPanelSkin.mxml to support displaying pods in a minimized state. Some of these steps are intricate, so follow along carefully.
minimized state by adding this line to the <s:states> tag: <s:State name="minimized"/>
excludeFrom="minimized" to the following three visual elements, which should not display in the minimized state: <s:RectangularDropShadow id="dropShadow" blurX="10" blurY="10"
alpha="1" distance="5" angle="0" color="#999999" left="0"
top="5" right="5" bottom="0" excludeFrom="minimized"/>
<s:Rect id="background" left="1" top="1" right="1" bottom="1"
excludeFrom="minimized">
<s:Group id="contentGroup" width="100%" height="100%"
minWidth="0" minHeight="0" excludeFrom="minimized">
You'll need to modify the updateDisplayList() function to check for null objects, because in the minimized state, excluded visual elements will not exist and will cause null object runtime errors.
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
if (getStyle("borderVisible") == true)
{
border.visible = true;
contents.left = contents.top = contents.right = contents.bottom = 1;
if(background != null){
background.left = background.top = background.right =
background.bottom = 1;
}
}
else
{
border.visible = false;
contents.left = contents.top = contents.right = contents.bottom = 0;
if(background != null){
background.left = background.top = background.right =
background.bottom = 0;
}
}
if(dropShadow != null){
dropShadow.visible = getStyle("dropShadowVisible");
}
var cr:Number = getStyle("cornerRadius");
var withControls:Boolean = (currentState == "disabledWithControlBar"
|| currentState == "normalWithControlBar");
if (cornerRadius != cr)
{
cornerRadius = cr;
if(dropShadow != null){
dropShadow.tlRadius = cornerRadius;
dropShadow.trRadius = cornerRadius;
dropShadow.blRadius = withControls ? cornerRadius : 0;
dropShadow.brRadius = withControls ? cornerRadius : 0;
}
setPartCornerRadii(topMaskRect, withControls);
setPartCornerRadii(border, withControls);
if(background != null){
setPartCornerRadii(background, withControls);
}
}
if (bottomMaskRect) setPartCornerRadii(bottomMaskRect, withControls);
borderStroke.color = getStyle("borderColor");
borderStroke.alpha = getStyle("borderAlpha");
if(backgroundFill != null){
backgroundFill.color = getStyle("backgroundColor");
backgroundFill.alpha = getStyle("backgroundAlpha");
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
Now you can minimize, maximize, and restore the Dashboard pods, in the same manner as the Flex 3 application. However, dragging the pods does not work, which will be fixed when you convert the DragHighlight component next.
The next step is to convert the DragHighlight component, which displays a highlight box when pods are dragged, to Flex 4.5. You will use a Spark BorderContainer to define the visual aspects of the drag highlight defined in the DragHighlight style in styles.css.
<mx:Box> tag with the following:<s:BorderContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
borderColor="#CCCCCC" borderWeight="3"
borderStyle="solid" backgroundColor="#F3F3F3"/>
This is the only change necessary for the DragHighlight component. If you run the application now, you'll be able to drag Pods without a runtime error.
In the Flex 3 Dashboard application, the PodContentBase class extends the Flex 3 MX VBox container. To migrate to Flex 4.5, it will need to extend the Spark VGroup instead. Because it does not use a skin, the VGroup container has a lighter weight than the VBox container. The PodContentBase class is the base class of the ChartContent, FormContent, ListContent, and PieChartContent components, which represent the actual data for the pods.
public class PodContentBase extends VBox
public class PodContentBase extends VGroup
import mx.containers.VBox;
import spark.components.VGroup;
These are the only two changes necessary to the PodContentBase class. Note that launching and using the application now might result in empty pods or runtime errors, because although you have modified the PodContentBase class, you have not yet modified its subclasses.
This is the first of four sections in which you make changes to the ChartContent, PieChartContent, FormContent, and ListContent components, which extend the PodContentBase class and represent the actual data for the pods. All of these components are located in src/com/esria/samples/dashboard/view/.
Several steps in these sections are the same, but you will make changes to each component separately, because making changes to all four files at the same time would make it impossible to launch the application for an extended period of time due to multiple errors.
xmlns:mx=http://www.adobe.com/2006/mxml with the following lines: xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
mx to fx for the <mx:Script> tag.Note: When changing a tag from mx to fx (or in some cases, from mx to s ) here and throughout these steps, remember to change the associated closing tag as well; for example, change </mx:Script> to </fx:Script> .
mx to fx for all <mx:Component> tags.<fx:Declarations> tag, so non-visual elements can be distinguished from visual elements: <fx:Declarations>
<effects:DrillDownEffect id="drillDownEffect" duration="1500"
effectEnd="chart.showDataTips=true" />
<effects:DrillUpEffect id="drillUpEffect" duration="1500"
effectEnd="chart.showDataTips=true"/>
</fx:Declarations>
<s:DropDownList selectedIndex="{_selectedViewIndex}"
change="onChangeComboBox(event)" width="100"
fontSize="10" fontWeight="bold" fontFamily="verdana">
<s:ArrayList>
<fx:String>Bar Chart</fx:String>
<fx:String>Line Chart</fx:String>
</s:ArrayList>
</s:DropDownList>
import mx.controls.ComboBox; . It is no longer needed.A Spark DropDownList is used instead of a Spark ComboBox because the Spark ComboBox items are editable by default, and the Dashboard items should not be editable. Also, notice that the DropDownList width is specified because the default behavior of the Spark DropDownList in the Dashboard does not respect the size of the largest item.
onChangeComboBox() function and update the event type. Change ListEvent to IndexChangeEvent .import mx.events.ListEvent; and add import spark.events.IndexChangeEvent; .onChangeComboBox() function locate the following line:var index:Number = ComboBox(e.target).selectedIndex;
var index:Number = DropDownList(e.target).selectedIndex;
fontFamily="verdana" to the <mx:ColumnChart> and <mx:LineChart> tags.<mx:HBox> tag with <s:HGroup> .<mx:Canvas> tags with <s:NavigatorContent> .In this section, you'll update the PieChartContent component.
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:mx="http://www.adobe.com/2006/mxml"
mx to fx for the <mx:Script> tag and for the <mx:Component> tag.<fx:Declarations> tag.fontFamily="verdana" to the <mx:PieChart> tag.In this section, you'll update the FormContent component. Several steps in this section are identical to steps in the section on modifying the ChartContent component.
<mx:Script> tags to fx .gap="7" to the <PodContentBase> root tag near the top of the file. This adds vertical space between elements in the PodContentBase container. <s:DropDownList selectedIndex="{selectedViewIndex}"
change="onChangeComboBox(event)" width="74"
fontSize="10" fontWeight="bold" fontFamily="verdana">
<s:ArrayList>
<fx:String>Form</fx:String>
<fx:String>Grid</fx:String>
</s:ArrayList>
</s:DropDownList>
onChangeComboBox() method, locate this line:var index:Number = ComboBox(e.target).selectedIndex
var index:Number = DropDownList(e.target).selectedIndex;
onChangeComboBox() function from ListEvent to IndexChangeEvent .import mx.events.ListEvent; to import spark.events.IndexChangeEvent; .<mx:HBox> tags with <s:HGroup> tags.horizontalGap="0" property from the <s:HGroup> tag that has it.gap="10" property to each <s:HGroup> tag.<s:HGroup> containing the newButton and deleteButton buttons, set gap="2" .<mx:VBox> tag with a <s:NavigatorContent> tag.<s:NavigatorContent> tag, because it defaults to BasicLayout, and you want its children to have vertical layout: <s:layout>
<s:VerticalLayout horizontalAlign="center" gap="7"/>
</s:layout>
<mx:Canvas> tags with a <s:NavigatorContent> tag.Note: Do not add the layout tag inside this instance of <s:NavigatorContent> , because in this case it is replacing a Canvas, which uses BasicLayout.
<mx:Label> tags with <s:Label> .Note: If you build the project now, you will see three warnings because the Spark Label control does not support data binding to its text property. You can ignore these warnings.
<s:Label> tags:fontSize="10" paddingTop="4" fontFamily="verdana"
These attributes ensure the application's Label components look like their counterparts in the Flex 3 version.
<mx:TextInput> tags with <s:TextInput> tags.<s:TextInput> tags:fontSize="10" paddingTop="4" fontFamily="verdana"
<mx:TextArea> tag with a <s:TextArea> tag.<s:TextArea> tag: fontSize="10" paddingTop="4" fontFamily="verdana"
<mx:Button> tags with <s:Button> tags.fontSize="10" fontWeight="bold" fontFamily="verdana" only to <s:Button> tags that have the label property defined.fontSize="10" fontFamily="verdana" to the <mx:DateField> tag.The Spark DataGrid, new to Flex 4.5, requires several changes when migrating the Dashboard application.
<mx:DataGrid> tag with a <s:DataGrid> tag.fontSize="10" fontFamily="verdana" to the <s:DataGrid> tag.<mx:columns> tag to s .<mx:DataGridColumn> tags with <s:GridColumn> tags.<s:DataGrid> tag, change the itemClick event to a gridClick event. itemClick="selectedIndex=DataGrid(event.currentTarget).selectedIndex;"
With this:
gridClick="selectedIndex=DataGrid(event.currentTarget).selectedIndex;"
itemDoubleClick event to a gridDoubleClick event.itemDoubleClick="selectedViewIndex=0;"
With this:
gridDoubleClick="selectedViewIndex=0;"
<s:ArrayList> tag just after the opening <s:columns> tag, and add an </s:ArrayList> tag just before the closing </s:columns> tag.<s:GridColumn> tag, change rendererIsEditor="true" to rendererIsEditable="true" .width="80" to the first and second <s:GridColumn> tags to set the initial width of the columns.formatDate() method, change the data type of the second parameter from DataGridColumn to GridColumn :import mx.controls.dataGridClasses.DataGridColumn;
Next you'll create two custom button skin classes for the left and right buttons, which enable the user to move through the data items. These skins will implement the leftArrowButton and rightArrowButton styles in file styles.css.
styleName="leftArrowButton" and styleName="rightArrowButton" respectively. Remove the width , height , and styleName properties, and set the skinClass properties for the buttons as follows: <s:Button
enabled="{dataProvider.length > 0}"
skinClass="skins.CustomLeftButtonSkin"
click="saveData();selectedIndex-=1;"/>
<s:Button
enabled="{dataProvider.length > 0}"
skinClass ="skins.CustomRightButtonSkin"
click="saveData();selectedIndex+=1;"/>
<s:BitmapImage includeIn="up, down" width="9" height="12"
source="@Embed(source='/assets/left_arrow_up.gif')"/>
<s:BitmapImage includeIn="over" width="9" height="12"
source="@Embed(source='/assets/left_arrow_over.gif')"/>
<s:BitmapImage includeIn="disabled" width="9" height="12"
source="@Embed(source='/assets/left_arrow_disabled.gif')"/>
<s:BitmapImage includeIn="up, down" width="9" height="12"
source="@Embed(source='/assets/right_arrow_up.gif')"/>
<s:BitmapImage includeIn="over" width="9" height="12"
source="@Embed(source='/assets/right_arrow_over.gif')"/>
<s:BitmapImage includeIn="disabled" width="9" height="12"
source="@Embed(source='/assets/right_arrow_disabled.gif')"/>
In this section you'll update the ListContent component.
<mx:Script> tag to fx .mx.controls.Label; with import spark.components.Label; .<mx:List … /> with <s:List … > . (Remember to remove the slash before the closing angle bracket.)borderStyle="none" with borderVisible="false" in the <s:List> tag.fontSize="11" in the <s:List> tag.</s:List> tag.<s:List> tag and the closing </s:List> tag: <s:layout>
<s:VerticalLayout gap="16" paddingTop="6" paddingLeft="5"/>
</s:layout>
Your <s:List> tag should now be similar to the following:
<s:List
id="list"
width="100%" height="100%"
dataProvider="{dataProvider}"
click="onClickList(event)"
borderVisible="false"
itemRenderer="com.esria.samples.dashboard.renderers.ListPodRenderer"
fontSize="11">
<s:layout>
<s:VerticalLayout gap="16" paddingTop="6" paddingLeft="5"/>
</s:layout>
</s:List>
The Flex 3 Dashboard ListContent component displays only a vertical scrollbar, and does not display a horizontal scrollbar. It achieves this by setting the horizontalScrollPolicy property in the custom item renderer for the ListContent List subcomponent to "off" . To remove the horizontal scrollbar in the Flex 4.5 it is necessary to create a custom skin class for the ListContent List subcomponent.
The custom skin will also be used to remove the border from the List control.
skinClass property for the <s:List> tag in ListContent.mxml: skinClass="skins.CustomListSkin"
alpha property of the borderStroke SolidColorStroke to a value of 0 , to remove the List border:<s:SolidColorStroke id="borderStroke" weight="1" alpha="0"/>
horizontalScrollPolicy property of the Scroller subcomponent in the skin class to "off" to remove the horizontal scroll bar:<s:Scroller left="0" top="0" right="0" bottom="0" id="scroller" minViewportInset="1" hasFocusableChildren="false" horizontalScrollPolicy="off">
In this section, you'll update the ListPodRenderer, which is used to render list items in the ListContent component.
<mx:Script> tag to fx .<mx:HBox> with <s:ItemRenderer> .backgroundColor and horizontalScrollPolicy properties from the ItemRenderer tag, as they are not supported by the ItemRenderer class.<s:ItemRenderer> tag: <s:layout>
<s:HorizontalLayout gap="12"/>
</s:layout>
</s:layout> tag: <s:states>
<s:State name="normal"/>
<s:State name="over"/>
</s:states>
onInitialize() function: setStyle("rollOverColor", "#FFFFFF");
setStyle("selectionColor", "#FFFFFF");
<mx:Label> with <s:Label> throughout.truncateToFit="true" property from the final Label, as it is unnecessary.<s:Label> tags:fontSize="10" fontFamily="verdana" kerning="off"
nameLabel <s:Label> , replace rollOver="Label(event.currentTarget).setStyle('textDecoration', 'none');" with textDecoration.over="none" .<s:Label> tag, remove this:rollOut="Label(event.currentTarget).setStyle('textDecoration', 'underline');"
If you launch the Dashboard and click the links in the Company News, a new browser window opens but the page fails to load, because www.esria.com no longer exists.
Follow these steps to fix this:
That's it! You've finished porting the Adobe Flex 3 example Dashboard application to use the Flex 4.5 Spark controls and architecture.
It's important to note that the resulting application is not necessarily coded as it would be if you were to develop the Dashboard from scratch using Flex 4.5. Rather, the goal of the exercise was to migrate an existing application from Flex 3 to Flex 4.5, while changing as little as possible. Now that that has been achieved, you may want to look for opportunities to improve the overall quality of the Flex 4.5 Dashboard and its application architecture.
In fact, the best way to learn even more about the Dashboard application is to think of things it does not do, or limitations or problems you see in the Dashboard, and then try to understand and modify the code. Just remember to periodically export your project to an archive folder just in case you need to revert to an earlier version.
For more information on Flex 4.5—and in particular features relevant to the Dashboard application—see the following resources:
For more information on Flex 4.5—and in particular features relevant to the Dashboard application—see the following resources:
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.
Note: You may use the sample code in your products, commercial or not.