When you define a view state, you specify the changes, or overrides, from the base state to the new view state, or from any other view state to the new view state. As part of the view state, you can define the following overrides:
If you require overrides in addition to the ones supplied by Flex, you can define your own custom override class. For more information, see Creating your own override classes.
You use the SetProperty class to set a property value that is in effect during a specific view state. For example, the following code sets properties of a Panel container and of a Button control when you switch to the Register view state:
<?xml version="1.0"?>
<!-- states\StatesSetProp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="Register">
<mx:SetProperty
target="{loginPanel}"
name="title" value="Register"/>
<mx:SetProperty
target="{loginButton}"
name="label" value="Register"/>
</mx:State>
</mx:states>
<mx:Panel id="loginPanel"
title="Login"
horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<mx:Form id="loginForm">
<mx:Button label="Login" id="loginButton"/>
</mx:Form>
<mx:ControlBar width="100%">
<mx:Button label="Change State"
click="currentState =
currentState=='Register' ? '':'Register';"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
You can use data binding to specify information to the value property, as the following example shows:
<?xml version="1.0"?>
<!-- states\StatesSetProp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
// Define a variable in ActionScript.
[Bindable]
public var registerValue:String="Register";
]]>
</mx:Script>
<mx:states>
<mx:State name="Register">
<mx:SetProperty
target="{loginPanel}"
name="title" value="{registerValue}"/>
<mx:SetProperty
target="{loginButton}"
name="label" value="{registerValue}"/>
</mx:State>
</mx:states>
<mx:Panel id="loginPanel"
title="Login"
horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<mx:Form id="loginForm">
<mx:Button label="Login" id="loginButton"/>
</mx:Form>
<mx:ControlBar width="100%">
<mx:Button label="Change State"
click="currentState =
currentState=='Register' ? '':'Register';"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
When you switch to the Register state, the value property of the SetProperty class is determined by the current value of the registerValue variable. However, this binding occurs only when you switch to the Register view state; if you modify the value of the registerValue variable after switching to the Register view state, the title property of the target component is not updated.
You use the SetStyle class to set a style property value that is in effect only during a specific view state. In the following example, you change the background color of the Panel container, and the text color of the Button control as part of changing to the Register view state:
<?xml version="1.0"?>
<!-- states\StatesSetStyle.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="Register">
<mx:SetProperty
target="{loginPanel}"
name="title" value="Register"/>
<mx:SetStyle
target="{loginPanel}"
name="backgroundColor" value="0x00FFFF"/>
<mx:SetProperty
target="{loginButton}"
name="label" value="Register"/>
<mx:SetStyle
target="{loginButton}"
name="color" value="0xFFFF00"/>
</mx:State>
</mx:states>
<mx:Panel id="loginPanel"
title="Login"
horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<mx:Form id="loginForm">
<mx:Button label="Login" id="loginButton"/>
</mx:Form>
<mx:ControlBar width="100%">
<mx:Button label="Change State"
click="currentState =
currentState=='Register' ? '':'Register';"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
You use the AddChild and RemoveChild classes to add and remove components as part of a switch of view state. When you remove a component by using the RemoveChild class, you remove the component from the application's display list, which means that it no longer appears on the screen. Even though the component is no longer visible, the component still exists and you can access it from within your application. For more information, see Removing a child component.
When you add a component as part of a change of view state by using the AddChild class, you can either create the component before the first switch to the view state, or create it at the time of the first switch. If you create the component before the first switch, you can access the component from within your application even though you have not yet switched view states. If you create the component when you perform the first switch to the view state, you cannot access it until you perform that first switch.
Regardless of when you create a component by using the AddChild class, the component remains in memory after you switch out of the view state that creates it. Therefore, after the first switch to a view state, you can always access the component even if that view state is no longer the current view state. For more information, see Controlling when to create added children.
You use the RemoveChild class to remove a child component from a container as part of a change of view state. When you remove a child, you can use the target property to specify the component to remove. Removing a child does not delete it, so you can redisplay it later without recreating it.
For an example using the RemoveChild class, see Example: Login form application.
You use the AddChild class to add a child component to a container as part of a change of view state. When you add a child, you can use the relativeTo property to specify the container relative to which you are placing the new child; the default value is the application or custom component that defines the view state. For example, to add a Button control to an HBox container, you specify the container as the value of the relativeTo property.
You can use the position property to specify the child's location within the container. Valid values are before, after, firstChild, and lastChild. The default value is lastChild.
The following example adds a Button control as the first child of an HBox container named h1:
<?xml version="1.0"?>
<!-- states\StatesAddRelative.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="NewButton">
<mx:AddChild relativeTo="{h1}" position="firstChild">
<mx:Button id="buttonNew" label="New Button"/>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:HBox id="h1">
<mx:Button label="Change State"
click=
"currentState = currentState=='NewButton' ? '':'NewButton';"/>
</mx:HBox>
</mx:Application>
The executing SWF file for the previous example is shown below:
You can add multiple child components, or a container that contains multiple child components, as the following example shows:
<?xml version="1.0"?>
<!-- states\StatesAddRelativeMultiple.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="NewVBox">
<mx:AddChild relativeTo="{v1}">
<mx:VBox id="v2">
<mx:Button id="buttonNew1" label="New Button"/>
<mx:Button id="buttonNew2" label="New Button"/>
<mx:Button id="buttonNew3" label="New Button"/>
</mx:VBox>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:VBox id="v1">
<mx:Button label="Change State"
click=
"currentState = currentState=='NewVBox' ? '':'NewVBox';"/>
</mx:VBox>
</mx:Application>
The executing SWF file for the previous example is shown below:
In the previous example, you use a single <mx:AddChild> tag to add the VBox container and its children. The following example performs the same action, but uses multiple <mx:AddChild> tags:
<?xml version="1.0"?>
<!-- states\StatesAddRelativeMultipleAlt.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="NewVBox">
<mx:AddChild relativeTo="{v1}">
<mx:VBox id="v2"/>
</mx:AddChild>
<mx:AddChild relativeTo="{v2}">
<mx:Button id="buttonNew1" label="New Button"/>
</mx:AddChild>
<mx:AddChild relativeTo="{v2}">
<mx:Button id="buttonNew2" label="New Button"/>
</mx:AddChild>
<mx:AddChild relativeTo="{v2}">
<mx:Button id="buttonNew3" label="New Button"/>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:VBox id="v1">
<mx:Button label="Change State"
click=
"currentState = currentState=='NewVBox' ? '':'NewVBox';"/>
</mx:VBox>
</mx:Application>
The executing SWF file for the previous example is shown below:
Controlling when to create added children
By default Flex creates container children when they are first required as part of a change of view state. However, if a child requires a long time to create, users might see a delay when the view state changes. Therefore, you can choose to create the child before the state changes to improve your application's apparent speed and responsiveness.
Flex lets you choose among three ways of creating a child component added by a view state:
The specification of when the child is created is called its creation policy. For more general information on creation policies and controlling how children are created, see Improving Startup Performance.
The AddChild class has two mutually exclusive properties that you use to specify the child to add and the child's creation policy:
target Always creates the child when the applications starts. You cannot change the creation policy when using this property. When you use this property, you can access the component before the first change to the view state that defines it.
targetFactory Lets you control the creation policy of the child.
The target property specifies the component to add. When you use this property, Flex creates the child when the applications starts. You cannot use any other creation policy with this property.
The child does not dispatch the creationComplete event until it is added to a container. Therefore, in the previous example, the Button control does not dispatch the creationComplete event until you change state. If the AddChild class defines both the Button and a container, such as a Canvas container, then the Button control dispatches the creationComplete event at startup.
One use of the target property is to modify components that already exist in your application. For example, if you want to move a component from one parent container to another, you use the RemoveChild class to remove the child from its parent container, and then use the AddChild class to add the component to a different container. Since the component already exists in the application, you do not have to worry about its creation policy, as the following example shows to move a Button control from one VBox container to another:
<?xml version="1.0"?>
<!-- states\StatesAddRemove.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="NewParent">
<mx:RemoveChild target="{button1}"/>
<mx:AddChild target="{button1}" relativeTo="{v2}"/>
</mx:State>
</mx:states>
<mx:VBox id="v1" borderStyle="solid">
<mx:Label text = "VBox v1"/>
<mx:Button id="button1" label="Click Me"/>
</mx:VBox>
<mx:VBox id="v2" borderStyle="solid">
<mx:Label text = "VBox v2"/>
<mx:Button label="Change Parent"
click="currentState =
currentState=='NewParent' ? '':'NewParent';"/>
</mx:VBox>
</mx:Application>
The executing SWF file for the previous example is shown below:
About the targetFactory property
The targetFactory property is the default property of the AddChild class, so if you specify the child by using an MXML tag in the <mx:AddChild> tag body, as shown in the following code example, Flex automatically uses the targetFactory property and creates the required factory class:
<mx:AddChild relativeTo="{v1}">
<mx:Button id="b0" label="New Button"/>
</mx:AddChild>
Because targetFactory property is the default property of the AddChild class, the previous example is equivalent to the following:
<mx:AddChild relativeTo="{v1}">
<mx:targetFactory>
<mx:Button id="b0" label="New Button"/>
</mx:targetFactory>
</mx:AddChild>
You can specify either of the following items to the targetFactory property:
When you use the targetFactory property to specify the child, you can optionally use the creationPolicy property to specify the creation policy. The creationPolicy property supports the following values:
auto Creates the child instance when it is first added by a change to the view state. This is the default value. In ActionScript, specify this property by using the mx.core.ContainerCreationPolicy.AUTO constant.
all Creates the child instance at application startup. In ActionScript, specify this property by using the ContainerCreationPolicy.ALL constant. This is equivelant to using the target property.
none Requires your application to call the AddChild.createInstance() method to create an instance of the child. In ActionScript, specify this property by using the mx.core.ContainerCreationPolicy.NONE constant.
You can call the createInstance() method with any creation policy, but if the child has already been created, it does nothing. For example, if you have a creationPolicy value of auto, but need to access a child before the view state is entered, you can call the createInstance() method to ensure that the child has been created.
The following example adds a child and has Flex create a child when the application first starts. The application does not display the child until the view state with the AddChild tag is activated.
<mx:AddChild relativeTo="{v1}" position="lastChild" creationPolicy="all">
<mx:Button id="b0" label="New Button"/>
</mx:AddChild>
Example: Using different creation policies
The following example lets you change between a base view state, a view state that adds a Button control created at application startup, and a view state that adds a Button control created using the AddChild.createInstance() method:
<?xml version="1.0"?>
<!-- states\StatesCreationPolicy.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initButton();">
<mx:Script>
<![CDATA[
// Because the cpAll view state creates the Button control
// at application startup, you can access the control to
// set the label before the first switch
// to the cpAll view state.
public function initButton():void {
newButton.label="cpAll Button";
}
]]>
</mx:Script>
<mx:states>
<!-- Create the Button control at application startup. -->
<mx:State name="cpAll">
<mx:AddChild relativeTo="{myPanel}" creationPolicy="all">
<mx:Button id="newButton"/>
</mx:AddChild>
</mx:State>
<!-- Create the Button control when you want to create it. -->
<mx:State name="cpNone">
<mx:AddChild id="noCP"
relativeTo="{myPanel}" creationPolicy="none">
<mx:Button label="cpNone button"/>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:Panel id="myPanel"
title="Static and dynamic states"
width="300" height="150">
<!-- Change to the cpAll view state. -->
<mx:Button label="Change to cpAll state"
click="currentState = currentState == 'cpAll' ? '' : 'cpAll';"/>
<!-- Create the Button control for the noCP state that
uses creationPolicy=none.
If you do not click this button before changing to the
cpNone view state, the view state does nothing
because the Button control does not exist. -->
<mx:Button label="Explicitly create a button control"
click="noCP.createInstance();"/>
<!-- Change to the cpNone view state. -->
<mx:Button label="Change to noCP state"
click="currentState = currentState == 'cpNone' ? '' : 'cpNone';"/>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
Example: Adding a Button control using the IDeferredInstance interface
The following example uses DeferredInstanceFromFunction, a subclass of DeferredInstance, to create the child instance for the AddChild class:
<?xml version="1.0"?>
<!-- states\StatesDefInstan.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.core.*;
import mx.states.*;
[Bindable]
public var defInst:DeferredInstanceFromFunction =
new DeferredInstanceFromFunction(createMyButton);
// Function to create a new Button control.
public function createMyButton():Object {
var newButton:Button = new Button();
newButton.label = "New Dynamic Button";
return newButton;
}
]]>
</mx:Script>
<mx:states>
<mx:State name="deferredButton">
<mx:AddChild relativeTo="{myPanel}" targetFactory="{defInst}"/>
</mx:State>
</mx:states>
<mx:Panel id="myPanel"
title="Static and dynamic states"
width="300" height="150">
<mx:Button id="myButton2"
label="Toggle Deferred Button"
click="currentState =
currentState == 'deferredButton' ? '' : 'deferredButton';"/>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
Flex lets you define state-specific event handlers in your application. A state-specific event handler is only active during a specific view state. For example, you might define a Button control that uses one event handler in the base view state, but uses a different event handler when you change view state.
You can set state-specific event handlers in either of two ways:
When using the SetEventHandler class, any existing event handler that is defined in MXML is removed from the target component. The following example defines a view state that adds an event handler to a Button control:
<mx:states>
<mx:State name="State1">
<mx:SetEventHandler target="{b1}"
name="click" handler="trace('goodbye');"/>
</mx:states>
...
<mx:Button id="b1" click="trace('hello');"/>
In this example, the trace('hello'); handler is removed when the trace('goodbye'); handler is added as part of the State 1 view state. However, if you add an event handler in ActionScript by calling the addEventListener() method, the event handler is not removed by the SetEventHandler class.
Using the handlerFunction property
You can use the handlerFunction property in ActionScript or MXML. It lets you specify an event handler that must take a single argument of type flash.events.Event. The following example code sets an event handler as part of a view state:
<mx:SetEventHandler target="{myButton1}"
name="click" handlerFunction="handler2"/>
You use the SetEventHandler.name property to specify the event name for the associated event handler. Notice that you only specify the name of the event handler to the handlerFunction property because that event handler is required to take a single argument of type flash.events.Event. Therefore, the function handler2 must have the following declaration:
private function handler2(event:Event):void
{
...
}
The handler event has the following advantages over the handlerFunction property:
You can use the handler event in MXML only.
You use the SetEventHandler.name property to specify the event name for the associated event handler. The following example shows how to use the handler property to specify the event handler code directly in the <mx:SetEventHandler> tag for the click event of a Button control:
<mx:SetEventHandler target="{myButton1}"
name="click" handler="Alert.show('Hello World.')"/>
The following example shows how you specify a handler function that takes two arguments; the Event object and a user ID String variable:
<mx:SetEventHandler target="{myButton1}"
name="click" handler="myAlert(event, userID)"/>
The function myAlert must have the following declaration:
private function myAlert(eventObj:Event, idString:String):void
{
...
}
Example: Setting event handlers
The following example creates the base view state and three additional view states. Each state uses the SetEventHandler class to define a different event handler for the click event for the Button control named b1:
<?xml version="1.0"?>
<!-- states\StatesEventHandlersSimple.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import flash.events.Event;
// Base view state click event handler.
private function handlerBase():void {
ta1.text = "Default click handler for the base view state";
}
// View state 1 click event handler.
private function handlerState1(event:Event):void {
ta1.text =
"Use the handlerFunction property to specify the function.";
}
// View state 3 click event handler.
private function handlerState3(theString:String):void {
ta1.text =
"Use the handler property to specify the function" +
"\nArgument String: " + theString;
}
]]>
</mx:Script>
<mx:states>
<mx:State name="State1">
<mx:SetEventHandler target="{b1}"
name="click"
handlerFunction="handlerState1"/>
<mx:SetProperty target="{b1}"
name="label" value="Click Me: State 1"/>
</mx:State>
<mx:State name="State2">
<mx:SetEventHandler target="{b1}"
name="click"
handler="ta1.text='Specify an inline event listener';"/>
<mx:SetProperty target="{b1}"
name="label" value="Click Me: State 2"/>
</mx:State>
<mx:State name="State3">
<mx:SetEventHandler target="{b1}"
name="click"
handler="handlerState3('Event listener arg');"/>
<mx:SetProperty target="{b1}"
name="label" value="Click Me: State 3"/>
</mx:State>
</mx:states>
<mx:Button id="b1"
label="Click Me: Base State"
click="handlerBase();"/>
<mx:TextArea id="ta1" height="100" width ="50%"/>
<mx:VBox>
<mx:Button
label="Set state 1, use handlerFunction property"
click="currentState='State1';"/>
<mx:Button
label="Set state 2, specify inline handler"
click="currentState='State2';"/>
<mx:Button
label="Set state 3, specify handler function"
click="currentState='State3';"/>
<mx:Button
label="Set base state"
click="currentState='';"/>
</mx:VBox>
</mx:Application>
The executing SWF file for the previous example is shown below: