Prerequisite knowledge

A good understanding of Flex 3 and basic understanding of Flex 4.5 is recommended. You should also complete Part 1 and Part 2 of this series before beginning this one.


User level


Required products

Flash Builder 4.7 Premium (Download trial)

This article is Part 3 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.
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 (this article) continues the migration and describes changes required to have the application's ViewStack use the new Flex 4.5 Spark NavigatorContent container.
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-Part3-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 2 in this series.

Changes to use NavigatorContent for the ViewStack Views

Flex 4.5 does not include Spark navigator containers, so you still need to use the MX ViewStack. Moreover, you cannot use Spark containers directly as views of a ViewStack, so you need to use the Spark NavigatorContent container or wrap Spark containers in an MX container before using them for the views of a ViewStack.
The Flex 3 Dashboard application uses the MX Canvas container for the ViewStack views, which are initialized in the onResultHttpService() function in main.mxml. You'll replace the Canvas container with a NavigatorContent container.
Changes within the main.mxml file's onResultHttpService() function
  1. Open main.mxml.
  2. Locate the following line in the onResultHttpService() function:
var canvas:Canvas = new Canvas();
  1. Replace it with this line:
var canvas:NavigatorContent = new NavigatorContent();
Keep the instance name as canvas even though you are now using a NavigatorContent container; this is to avoid trivial changes related to using instance names.
  1. Add this line to the imports at the top of main.mxml:
import spark.components.NavigatorContent;
  1. Remove this line, as your code no longer references the Canvas class:
import mx.containers.Canvas;
  1. Back in onResultHttpService() , remove these two lines, because Flex 4.5 Spark containers do not have scrollbars unless you explicitly add them, so the lines are unnecessary:
canvas.horizontalScrollPolicy = "off"; canvas.verticalScrollPolicy = "off";
  1. Save your changes.
Changes within the PodLayoutManager Class to use NavigatorContent
Because you are now using NavigatorContent for the views, if you build the project you will get an error in main.mxml. This occurs because the PodLayoutManager class is still expecting a Canvas. To fix this error you'll need to make changes to use NavigatorContent instead of Canvas.
  1. Open the file src\com.esria.samples.dashboard\managers\
  2. Locate the following line:
private var _container:Canvas;
  1. Replace it with this:
private var _container:NavigatorContent;
  1. Add this line to the imports at the top of
import spark.components.NavigatorContent;
  1. Remove this line, as your code no longer references the Canvas class:
import mx.containers.Canvas;
  1. Change the container setter and getter functions to use NavigatorContent:
public function set container(canvas: NavigatorContent):void { _container = canvas; } public function get container():NavigatorContent { return _container; }
To avoid runtime errors you need to make changes because of differences between the NavigatorContent and Canvas container API methods and variables (see Table 1).
Table 1. Relevant differences between NavigatorContent and Canvas containers APIs.
add an item to the container
set the index of an item in the container
the number of items in the container
  1. In change container.addChild(pod) to container.addElement(pod) .
  2. Change container.addChild(dragHighlight) to container.addElement(dragHighlight) .
  3. Change container.setChildIndex(pod, container.numChildren - 1) to container.setElementIndex(pod, container.numElements - 1) .
  4. Change container.setChildIndex(dragHighlight, i) to container.setElementIndex(dragHighlight, i) .
  5. Save your changes.
Changes within the Pod class to use NavigatorContent
You also need to make a change in the Pod class due to the API differences between the NavigatorContent and Canvas classes.
  1. Open src\com.esria.samples.dashboard\view\ and locate the onMouseDown() function.
  2. Change parent.setChildIndex(this, parent.numChildren - 1); to Group(parent).setElementIndex(this, Group(parent).numElements - 1) ;.
Note that you cast the parent to Group and not NavigatorContent, because the actual Pod parent is the NavigatorContent contentGroup skin part, which is of type Group.
  1. Add this line to the imports at the top of
import spark.components.Group;
  1. Save your changes and build the project.
At this point you should have no errors, and you should be able to launch the Dashboard application with no compile time or runtime errors.
Note that if you click a ComboBox in a pod, it might open and then immediately close. This problem will be resolved by using the Flex 4.5 Spark ComboBox. It is happening because the ChartContent and FormContent components' onMouseDown() event handler is responding to the mouseDown event, preventing the ComboBox from receiving the event.

Changes to the Pod class to extend the Spark Panel

The ViewStack views, now represented using the Spark NavigatorContent, contain the actual pods of the Dashboard application. This section modifies the Pod class to extend the Spark Panel, instead of the MX Panel container, which it extends in the Flex 3 Dashboard application.
  1. Open
  2. To import the Spark Panel rather than the MX Panel, replace import mx.containers.Panel; with import spark.components.Panel; .
This causes a number of errors (see Figure 1) due to differences in the implementation of the MX and Spark Panel, which you'll resolve as you proceed.
Figure 1. Changing the Pod class to extend the Spark Panel results in errors.
Figure 1. Changing the Pod class to extend the Spark Panel results in errors.
Most of these errors occur because the Spark Panel class does not implement the titleBar property. Instead, you use a custom skin class to implement the title bar area. The Spark Panel also does not implement the titleTextField property, also customized through the skin class.
The error related to the rawChildren property occurs due to changes in the Spark component architecture. The MX Container class, from which the MX Panel inherits, provides the rawChildren property as a way to access the container chrome, such as the container border, for example. This is accomplished in the Pod class using a custom skin.
  1. Remove the following line from the Pod constructor function. Flex 4.5 Spark containers do not have scrollbars unless you explicitly add them, so this line is unnecessary:
horizontalScrollPolicy = "off";

Changes in the Pod class to support a custom skin

Before creating a custom skin class for the Pod class, you'll add support for the custom skin in the Pod class. You start by declaring skin parts, which are visual components declared in the Pod class and implemented in its skin.
The first skin part is a container for the minimize and maximize buttons. The Flex 3 Dashboard uses an HBox container, but you will use the new Spark HGroup container. Notice the use of the public access modifier. All skin parts must be public.
  1. Locate the following line:
private var controlsHolder:HBox;
  1. Replace it with these lines:
[SkinPart(required="false")] public var controlsHolder:HGroup;
  1. Locate this line:
import mx.containers.HBox;
  1. Replace it with this line:
import spark.components.HGroup;
The next two skin parts are for the minimize and maximize buttons.
  1. Locate these lines:
private var minimizeButton:Button; private var maximizeRestoreButton:Button;
  1. Replace them with these lines:
[SkinPart(required="false")] public var minimizeButton:Button; [SkinPart(required="false")] public var maximizeRestoreButton:ToggleButton;
  1. Locate this line:
import mx.controls.Button;
  1. Replace it with this line:
import spark.components.Button;
  1. Add this line:
import spark.components.ToggleButton;
You use the ToggleButton control because the Spark Button control does not support the selected property as the MX Button does, and the selected property is necessary to switch between the maximized and restored states for the button display.
The next skin part is for the line dividing the title bar area and the main content area.
  1. Locate this line:
private var headerDivider:Sprite;
  1. Replace it with these lines:
[SkinPart(required="false")] public var headerDivider:Rect;
  1. Add this line:
import spark.primitives.Rect;
Notice that you changed the type for the headerDivider skin part from Sprite to Rect.
The next skin part is for an empty rectangle that responds to mouse clicks and to the user dragging the Pod around the Dashboard.
  1. Add these lines:
[SkinPart(required="false")] public var titleBar:Group;
Now that the skin parts have been declared, several Pod class code changes are necessary.
  1. Delete the following text from the createChildren() function; it will be replaced with code in the custom skin class:
if (!headerDivider) { headerDivider = new Sprite(); titleBar.addChild(headerDivider); } if (!controlsHolder) { controlsHolder = new HBox(); controlsHolder.setStyle("paddingRight", getStyle("paddingRight")); controlsHolder.setStyle("horizontalAlign", "right"); controlsHolder.setStyle("verticalAlign", "middle"); controlsHolder.setStyle("horizontalGap", 3); rawChildren.addChild(controlsHolder); } if(!minimizeButton) { minimizeButton = new Button(); minimizeButton.width = 14; minimizeButton.height = 14; minimizeButton.styleName = "minimizeButton"; controlsHolder.addChild(minimizeButton); } if (!maximizeRestoreButton) { maximizeRestoreButton = new Button(); maximizeRestoreButton.width = 14; maximizeRestoreButton.height = 14; maximizeRestoreButton.styleName = "maximizeRestoreButton"; controlsHolder.addChild(maximizeRestoreButton); }
The createChildren() function should now be similar to the following:
override protected function createChildren():void { super.createChildren(); addEventListeners(); }
  1. Next, delete the entire updateDisplayList() function, as its code is unnecessary after porting the Dashboard application to Flex 4.5.
  2. Delete the following line from the minimize() function, as it will be unnecessary:
setStyle("borderSides", "left top right");
  1. Delete this line from the onClickTitleBar() function, as it will be unnecessary:
setStyle("borderSides", "left top right bottom");
  1. Delete this line from the Pod constructor function, as it will be unnecessary:
setStyle("titleStyleName", "podTitle");
The next few changes are necessary to support displaying pods in their minimized state, where only the title bar area is visible, and the pod content is hidden.
  1. First, add the following immediately before the Pod class declaration:
The Pod class declaration should now be as follows:
[SkinState("minimized")]; public class Pod extends Panel
  1. Add the following function to
override protected function getCurrentSkinState():String { var returnState:String = "normal"; if (windowState == WINDOW_STATE_MINIMIZED) { returnState = "minimized"; } return returnState; }
  1. Next, add a call to invalidateSkinState() in the minimize() function, so the function is similar to the following:
public function minimize():void { windowState = WINDOW_STATE_MINIMIZED; invalidateSkinState(); height = MINIMIZED_HEIGHT; showControls = false; }
  1. Also add a call to invalidateSkinState() at the end of the onClickMaximizeRestoreButton() function.
Before implementing the custom pod skin class, you need to make one more change to avoid a runtime error due to differences between the MX and Spark Panel API methods.
  1. In main.mxml, locate the addPods() method and find the following line:
  1. Replace it with this line:
  1. Save all your changes and build the project.
You should now have no compile time errors, but if you launch the application the pods will not be fully functional because the Pod class references some skin parts that have not been created yet.

Implementing a custom skin for the Pod class

Now that the Pod class has been modified to support the custom skin, the actual skin class can be implemented.
To begin, you first create the custom skin class file.
  1. Right-click the skins folder and select New > MXML Skin.
  2. In the New MXML Skin dialog box, type CustomPanelSkin for the Name.
  3. Type com.esria.samples.dashboard.view.Pod as the Host Component.
  4. Ensure the Create As Copy Of option is set to spark.skins.spark.PanelSkin.
  5. Deselect the Remove ActionScript Styling Code option.
  6. Click Finish.
  7. To use this custom skin class, add the following line to the top of file
import skins.CustomPanelSkin;
  1. Then add this line to the end of the Pod class constructor:
setStyle("skinClass", Class(CustomPanelSkin));
Now you are ready to add skin parts in the custom skin and implement the Pod styles defined in the Flex 3 Dashboard. Each skin part must have the same id in the custom skin that was defined in the Pod class.
  1. In CustomPanelSkin.mxml, insert the following code directly after the <s:Label id="titleDisplay"…> tag, and inside the same Group containing the titleDisplay Label:
<s:Group id="titleBar" left="0" right="0" top="0" bottom="0"/> <s:HGroup id="controlsHolder" right="9" top="4" horizontalAlign="right" verticalAlign="middle" gap="3"> <s:Button id="minimizeButton"/> <s:ToggleButton id="maximizeRestoreButton"/> </s:HGroup>
  1. Locate the <s:Rect id="tbDiv" …> tag and change the id to "headerDivider" .
Next, you need to modify the skin class to implement styles from two style definitions in styles.css, the podTitle and Pod styles.
  1. Locate the titleDisplay Label control again in the skin class, and add fontSize="11" fontFamily="arial" as properties.
  2. Change the left property's value to 12 .
  3. Add kerning="off" to achieve the same Panel title string position and rendering as the Flex 3 Dashboard.
When you are done, the Label tag should be similar to the following:
<s:Label id="titleDisplay" maxDisplayedLines="1" left="12" right="3" top="1" bottom="0" minHeight="30" verticalAlign="middle" textAlign="start" fontWeight="bold" fontSize="11" fontFamily="arial" kerning="off">
  1. Implement the padding style properties in the contentGroup Group container by adding the following layout code directly after the opening contentGroup <s:Group …> tag. The padding properties have been adjusted for a layout similar to that of the Flex 3 Dashboard.
<s:layout> <s:VerticalLayout paddingTop="32" paddingLeft="20" paddingRight="20" paddingBottom="20"/> </s:layout>
  1. The Spark Panel has square bottom corners by default, so you do not need to implement the rounded-bottom-corners style. The Spark Panel also has a default border that is a 1px solid line, and a default background color of white, so you also do not need to implement the border-thickness , border-style , and background-color Pod styles.
  2. For a border color like the Flex 3 Dashboard, add the following two lines inside the initializationComplete() method, after the line that sets the useChromeColor property:
setStyle("borderColor", 0x999999); setStyle("borderAlpha", 1);
You cannot simply set the color of the borderStroke SolidColorStroke object, because to support runtime border styling the updateDisplayList() method includes these lines:
borderStroke.color = getStyle("borderColor"); borderStroke.alpha = getStyle("borderAlpha");
  1. This is also the case for the corner-radius style, so add the following line in the initializationComplete() method for the Pod top corner radius:
setStyle("cornerRadius", 6);
  1. Replace the entire RectangularDropShadow tag in the file with the following:
<s:RectangularDropShadow id="dropShadow" blurX="10" blurY="10" alpha="1" distance="5" angle="0" color="#999999" left="0" top="5" right="5" bottom="0"/>
  1. Adjust the color of the divider that separates the Pod title bar area from the content area by setting the color of the SolidColor fill for the headerDivider Rect to 0x999999 .
  2. Implement the Pod header-height style by setting the titleDisplay Label's minHeight property to 22 .
  3. Implement the Pod header-colors style by replacing the two GradientEntry lines in the tbFill Rect with the following:
<s:GradientEntry color="0xFFFFFF" ratio=".1"/> <s:GradientEntry color="0xCCCCCC" ratio=".8"/>
The Spark Panel skin does not define a title bar highlight fill, so the Pod highlight-alphas style property does not need to be set.
  1. Save your changes and build the project.
There should be no compiler errors or warnings.
  1. Run the application.
Note: If you encounter a runtime error when you run the application, you may have left one of the Pods in the minimized state the last time you tested the Dashboard application. Because the minimized state has not yet been implemented, this causes an error. To resolve this issue, follow the instructions for Resetting The Dashboard To Its Initial State in Part 2 of this series and then run the application again.
Except for the minimize and maximize buttons, which you implement in Part 4, the Pod title bar area is now looking very similar to the Flex 3 Dashboard (see Figure 2).
Figure 2. A partial view of the Dashboard application before the minimize and maximize buttons have been implemented.

Where to go from here

You've now finished Part 3 of the series. In Part 4, you'll implement the minimize and maximize buttons for the Pods and complete the remaining steps needed to migrate the Dashboard application from Flex 3 to Flex 4.5.
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.