Showing posts with label FLEX. Show all posts

Adobe Flex

Adobe Flex is an open source framework that runs in Adobe Flash Player to create RIAs.

· It combines the functionality of desktop applications with the availability of web applications.
· Flex, as a framework, extends the regular Flash application programming interface (API) to provide the benefits of Flash components integrated with Flex.
· Flex has a large selection of out-of-the-box components—such as a DataGrid, Panel, and Tree—which are used to render the view of a Flex RIA.
· Flex is wired together with a standards-based programming model that supports design patterns with development, debug, and software maintainability.
· The programming model is made up of MXML and ActionScript.
· Flex applications are compiled into Flash bytecode (SWF files) and run in Flash Player.


MXML
· MXML is an XML-based language that developers use to lay out components in the view of Flex applications. It provides a declarative way of controlling an application’s visual appearance.
· MXML was first introduced by Macromedia, which was acquired in December 2005 by Adobe Systems.


ActionScript


· ActionScript, as well as JavaScript, is an ECMAScript-compliant scripting language, based on object-oriented principles, for handling client-side logic.
· ActionScript facilitates rapid development for large applications and handling large data sets.
· Java developers will find the transition to Flex easy, thanks to the standards-based MXML and ActionScript languages.


Benefits of Adobe Flex


· Flex allows for the rapid prototyping of complex web applications.
· Flash Player eliminates the need to write browser-specific code.
· Flex has many options to run the application under, including any application server compatible with Java Platform, Enterprise Edition (Java EE, formerly J2EE), such as Tomcat, Internet Information Server (IIS), or WebLogic.


Flex Application Framework


· The Flex framework
o is free to download from Adobe. The core Flex SDK allows developers to compile Flex from the command line. You can start with the Flex framework and begin to build applications using the editor or IDE of your choice.
· BlazeDS
o BlazeDS server-side technology to communicate with backend sources such as Spring through Java remoting and messaging. (BlazeDS was previously only available as part of Adobe LiveCycle, which was not free.)
· Adobe AIR
o Adobe AIR is a runtime application environment that runs on your desktop instead of inside a browser. AIR applications have access to your file system; Flex applications do not have this access. A great example of an AIR application that was built for eBay can be found athttp://desktop.ebay.com/.


Three tools are included as part of Flex


· Flex Builder IDE: Flex Builder can be downloaded and installed either as a stand-alone IDE built in Eclipse or as an Eclipse plug-in
· Flex Charting: This is a set of charting and graphing components, such as bar chart, line chart, and combinations of the two. These charts enhance data visualization with visual cues that represent data and raw forms.
· Adobe LiveCycle Data Services (LCDS): Formerly Flex Data Services, LCDS aids in data synchronization for Flex and Adobe AIR through remoting, messaging, data management services, and PDF document generation.

Flex, Spring, Cairngorm, Hibernate :Bringing It All Together

Spring Configuration for Flex


Then follow these steps to configure a new Spring project:

1. Download the Spring Framework from http://www.springframework.org/download.


2. Copy the spring.jar file that you downloaded to the AppFoundation/WebRoot/WEB-INF/
lib directory in the af_Central project which contains the BlazeDS server, and add it to
the Java Build Path in the project properties. In the root/libs directory for the project,
you should keep only the files needed for compilation, not those for deployment. That
way, you are not maintaining multiple locations for JAR libraries.


3. Modify the web.xml file on the af_Central BlazeDS server and add the security listener,
filter, filter-mapping, and context-param definitions, as follows:


4. In the Spring project in Eclipse, register the SBI front controller in AppFoundation/
WebRoot/WEB-INF/flex/services-config.xml


The web.xml Configuration for Spring Security and SBI Server


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.// ➥
DTD Web Application 2.3//EN" ➥
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>AF Central WS</display-name>
<description>AF Central WS</description>
<!-- Http Flex Session attribute and binding listener support -->
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<!-- The front controller of this Spring Web application,
responsible for handling all application requests -->
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Map all *.spring requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/springSecurityContext.xml
</param-value>
</context-param>
</web-app>

We are going to use the security modules

The springSecurityContext.xml File


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="index.html" filters="none"/>
<security:intercept-url pattern="*.swf" filters="none"/>
<security:intercept-url pattern="*.html" access="ROLE_USER"/>
</security:http>
<security:authentication-provider>
<security:user-service>
<security:user name="katiebug" password="test"
authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="colie" password="test"
authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>
</beans>


Hibernate Configuration for Spring


AF – Client Manager application, we will use Hibernate 3 with annotations
to deliver the payload to and from our database. To make this happen, we need to configure applicationContext.xml to implement Hibernate, annotated classes, and transact ion management.

The hibApplication.properties File


# JDBC Connection information
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/apress
jdbc.username=af

jdbc.password=afpass
# Hibernate 3 configuration
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.transaction.factory_class=org.hibernate.transaction.JDBCTransactionFactory
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50



The applicationContext.xml File Using Hibernate 3 with Annotations and
Transaction Management


<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-lazy-init="true">
<!-- START Load application properties -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>WEB-INF/hibApplication.properties</value>
</property>
</bean>
<!-- END Load application properties -->
<!-- START HIBERNATE CONFIG -->
<!-- Configure SessionFactory -->
<bean id="sessionFactory" class= ➥
"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<!-- Add annotated domain objects here-->
</list>
</property>

<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.transaction.factory_class">
${hibernate.transaction.factory_class}
</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.c3p0.min_size">
${hibernate.c3p0.min_size}
</prop>
<prop key="hibernate.c3p0.max_size">
${hibernate.c3p0.max_size}
</prop>
<prop key="hibernate.c3p0.timeout">
${hibernate.c3p0.timeout}
</prop>
<prop key="hibernate.c3p0.max_statements">
${hibernate.c3p0.max_statements}
</prop>
<prop key="hibernate.connection.driver_class">
${jdbc.driverClassName}
</prop>
<prop key="hibernate.connection.url">${jdbc.url}</prop>
<prop key="hibernate.connection.username">
${jdbc.username}
</prop>
<prop key="hibernate.connection.password">
${jdbc.password}
</prop>
</props>
</property>
</bean>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean class="org.springframework.beans.factory.annotation. ➥
RequiredAnnotationBeanPostProcessor"/>
<!-- END HIBERNATE CONFIG -->

<!-- Define data access beans -->
<!-- Add Services -->
</beans>

Cairngorm Configuration for Flex

Value Objects


value objects are used in Flex to map to object data from Spring domain
objects. A value object is nothing more than a class implementation of a custom data type built

in ActionScript that contains properties to support data visualization in views.









The ClientVO.as Value Object


package com.af.clientmanager.model.vo
{
[Bindable]
[RemoteClass(alias="com.af.core.domain.Client")]
public class ClientVO
{
public var objectIdentifier:Number;
public var assocobjectID:Number;
public var clientName:String;

public var clientID:int;
public var link:String;
public var description:String;
public var notes:String;
public var phone:String;
public var addressLine1:String;
public var addressLine2:String;
public var city:String;
public var state:String;
public var zip:String;
}
}

Cairngorm Delegates


The delegates we create here will eventually house all of the methods that relate to Spring bean operations

The Client Delegate (ClientDelegate.as)


package com.af.clientmanager.business
{
import com.adobe.cairngorm.business.ServiceLocator;
import com.af.model.vo.*;
import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
public class ClientDelegate
{
private var responder : IResponder;
private var service : Object;
public function ClientDelegate( responder : IResponder )
{
this.service = ServiceLocator.getInstance().getService(
"clientService" );
this.responder = responder;
}


// Method calls...
public function insertClient(clientVO : ClientVO ): void
{
var call:AsyncToken = service.insertLoreCategory(ClientVO);
call.addResponder( responder );
}
}
}

Model Locator


The ModelLocator in Cairngorm serves as a singleton class responsible for holding stateful
application information and data returned from data sources. These stateful global classes are accessible from any components in the application that access an instance of the model. This allows you to hold data on the model and bind views to the data. If the data is updated, the view is updated automatically using data binding. You may also elect to create submodels for large applications.









The AF – Client Manager Model Locator (ModelLocator.as)


package com.af.clientmanager.model
{
import com.adobe.cairngorm.model.ModelLocator;
[Bindable]
public class ModelLocator implements com.adobe.cairngorm.model.ModelLocator
{
private static var modelLocator :
com.af.clientmanager.model.ModelLocator;
public static function getInstance() :
com.af.clientmanager.model.ModelLocator
{
if ( modelLocator == null )
modelLocator = new com.af.clientmanager.model.ModelLocator();
return modelLocator;
}

{
if (com.af.clientmanager.model.ModelLocator.modelLocator != null)
throw new Error( "Only one ModelLocator instance should " +
"be instantiated" );
}
// Add all global constants, variables, and submodels here
public const SERVICE_ENDPOINT =
"http://localhost:8080/af_Central/spring/messagebroker/amf";
}
}


Services Definition Class

The services class is a Cairngorm ServiceLocator component that defines all endpoints for
RemoteObject calls to the Spring service layer. Since we are planning to have a separate service layer from the Flex project (with a different web context in Tomcat), we need to define an endpoint on the RemoteObjects. The endpoint tag takes the fully qualified URL of the location the AMF gateway is running on our BlazeDS server, such as the following:

http://localhost:8080/af_Central/spring/messagebroker/amf.

The Services.mxml File with RemoteObject Endpoints



<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator = ModelLocator.getInstance();
]]>
</mx:Script>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="clientService"
destination="clientService"
showBusyCursor="true">
</mx:RemoteObject>

<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="dashboardService"
destination="dashboardService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="projectService"
destination="projectService"
showBusyCursor="true">
</mx:RemoteObject>
</cairngorm:ServiceLocator>


Controller Class Definition

The controller class in Cairngorm will map events to commands. Flex is an event-driven framework, and Cairngorm extends the base event. Every database interaction will require an event and command to execute a transaction based on a user gesture in Flex. The command is responsible for processing the result or fault and moving the data to the appropriate model for a view to be updated

The Main Controller Class (MainController.as)


package com.af.clientmanager.control
{
import com.adobe.cairngorm.control.FrontController;
import com.af.clientmanager.control.commands.events.*;
public class MainController extends FrontController
{
public function MainController():void
{
addCommand(GerClientsEvent.EVENT_GET_CLIENT,
GetClientCommand );
addCommand(DeleteClientEvent.EVENT_DELETE_CLIENT,
DeleteClientCommand );
// Add all command-to-event mappings here...
}
}
}


Service Locator and Front Controller in the Flex Application

In the root MXML component that contains the <mx:Application> tag, we need to set the
Services and MainController instances. These will be loaded into memory when the Flex
application is started and will set listeners for service and CairngormEvent calls






The af_ClientManager.mxml File


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
pageTitle="AF Client Manager"
layout="absolute"
xmlns:view="com.af.view.*"
xmlns:services="com.af.clientmanager.services.*"
xmlns:control="com.af.clientmanager.control.*">
<mx:Style source="assets/css/style.css"/>
<!-- ===================================================================== -->
<!-- the ServiceLocator where we specify the remote services -->
<services:Services id="services"/>
<!-- the FrontController, containing Commands-to-Event mappings -->
<control:MainController id="mainController" />
<!-- ===================================================================== -->
</mx:Application>

Building the Flex User Interface - Part 1

The AF – Client Manager Model


we set up the Cairngorm configuration for Flex by creating the base classes for the delegates, model locator, services, and controller. We also instantiated the controller and services as singletons in the <mx:Application> MXML.


To properly wire the application, we need to use the model for binding <mx:ViewStack selectedIndex="{model.MainViewStackState}">,

ModelLocator.mxml


package com.af.clientmanager.model
{
import com.adobe.cairngorm.model.ModelLocator;
import com.af.clientmanager.model.vo.*;
import mx.collections.ArrayCollection;
[Bindable]
public class ModelLocator implements com.adobe.cairngorm.model.ModelLocator
{
private static var modelLocator :
com.af.clientmanager.model.ModelLocator;
public static function getInstance() :
com.af.clientmanager.model.ModelLocator
{
if ( modelLocator == null )
modelLocator = new com.af.clientmanager.model.ModelLocator();
return modelLocator;
}
public function ModelLocator()
{
if(
com.af.clientmanager.model.ModelLocator.modelLocator !=
null )
throw new Error( "Only one ModelLocator" +
"instance should be instantiated" );
}
public const SERVICE_ENDPOINT:String =
"http://localhost:8080/af_Central/spring/messagebroker/amf";
public const YAHOO_APP_ID:String = "YOUR YAHOO APP ID";
public const UPLOAD_URL:String = "";
public const DIRECTORY_NAME:String = "af_Central";
//Global Menu Constants
public const CLIENT_MANAGER:int = 0;
public const PROJECT_ADMIN:int = 1;
public const DASHBOARD:int = 2;
public var MainViewStackState:int = 0;

// Data Providers
public var clientsDP:ArrayCollection = new ArrayCollection();
public var contactsDP:ArrayCollection = new ArrayCollection();
public var issuesDP:ArrayCollection = new ArrayCollection();
public var documentsDP:ArrayCollection = new ArrayCollection();
public var selectedClient:ClientVO = new ClientVO;
public var selectedContact:ClientContactsVO = new ClientContactsVO;
}
}

We also need to build the base value object for the client object

ClientVO.as


package com.af.clientmanager.model.vo
{
[Bindable]
[RemoteClass(alias="com.af.core.domain.Client")]
public class ClientVO
{
public var objectIdentifier:Number;
public var assocobjectID:Number;
public var clientName:String;
public var clientID:int;
public var link:String;
public var description:String;
public var notes:String;
public var phone:String;
public var addressLine1:String;
public var addressLine2:String;
public var city:String;
public var state:String;
public var zip:String;
}
}

AF – Client Manager Application Wiring


Application View We’ll start with the top of the component model: af_ClientManager.mxml. This contains the <mx:Application> tag

af_ClientManager.mxml



<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
pageTitle="AF Client Manager"
layout="absolute"
xmlns:view="com.af.view.*"
xmlns:services="com.af.clientmanager.services.*"
xmlns:control="com.af.clientmanager.control.*"
xmlns:Main="com.af.clientmanager.view.Main.*"
xmlns:model="com.af.clientmanager.model.*"
initialize="initApp()" >
<mx:Style source="assets/css/style.css"/>
<mx:Script>
<![CDATA[
import mx.events.ListEvent;
import mx.controls.Alert;
private function initApp():void
{
// Currently set to applicationView to bypass
// the login process.
vsAppLevel.selectedChild = applicationView;
}
public function loginSuccess():void
{
vsAppLevel.selectedChild = applicationView;
}
]]>
</mx:Script>

<!-- ===================================================================== -->
<!-- the ServiceLocator where we specify the remote services -->
<services:Services id="services"/>
<!-- the FrontController, containing Commands-to-Event mappings -->
<control:MainController id="mainController" />
<!-- ===================================================================== -->
<mx:Fade id="fadeInApp" alphaFrom="0" alphaTo="1" duration="500" />
<mx:VBox width="100%" height="100%" backgroundAlpha=".88"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
verticalAlign="top" horizontalAlign="center">
<mx:ViewStack id="vsAppLevel" width="100%" height="100%" paddingTop="0"
creationPolicy="all">
<mx:Canvas id="loginView" showEffect="Fade" hideEffect="Fade">
<Main:Login width="100%" height="100%" />
</mx:Canvas>
<mx:VBox width="100%" height="100%"
id="applicationView" backgroundAlpha="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
paddingTop="0"
verticalAlign="top" horizontalAlign="center"
showEffect="Fade" hideEffect="Fade">
<mx:VBox width="985" height="735"
paddingTop="0" verticalGap="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
borderStyle="solid" borderThickness="2"
backgroundColor="#FFFFFF"
backgroundAlpha=".75">
<Main:Header backgroundColor="red" />
<mx:VBox width="100%" height="100%">
<Main:MainView />
</mx:VBox>
<Main:Footer backgroundColor="blue"/>
</mx:VBox>
</mx:VBox>
</mx:ViewStack>
</mx:VBox>
</mx:Application>


The footer is trivial and will have no function other than to display company information.
The header will have navigation components that change the view state of the application.

Header.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="75">
<mx:Script>
<![CDATA[
import com.af.clientmanager.view. ➥
dialogs.DeleteClientConfirmationDialog;
import mx.managers.PopUpManager;
import com.af.clientmanager.control. ➥
commands.events.DeleteClientEvent;
import com.af.clientmanager.control. ➥
commands.events.InsertClientEvent;
import mx.events.ItemClickEvent;
import com.af.clientmanager.model.vo.ClientVO;
import com.adobe.cairngorm. ➥
control.CairngormEventDispatcher;
import com.af.clientmanager.control. ➥
commands.events.UpdateClientEvent;
import mx.controls.Alert;
import com.af.clientmanager.model.ModelLocator;

[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
private function clearClientDetails():void
{
// remove all information from
// the panels after pop-up
model.selectedClient = new ClientVO();
}
private function saveClient():void
{
if(model.selectedClient.objectIdentifier > 0)
CairngormEventDispatcher.getInstance().dispatchEvent(new
UpdateClientEvent(model.selectedClient));
else
{
CairngormEventDispatcher.getInstance().dispatchEvent(new
InsertClientEvent(model.selectedClient));
}
}
private function deleteClient():void
{
// Don't try to delete unless a client is selected
if(model.selectedClient.objectIdentifier > 0)
{
var pop1:DeleteClientConfirmationDialog =
DeleteClientConfirmationDialog(
PopUpManager.createPopUp(this,
DeleteClientConfirmationDialog, true));
pop1.confirmationMessageTitle =
"Delete Confirmation";
pop1.confirmationMessageBody =
"Are you sure you want to delete: " +
model.selectedClient.clientName;
PopUpManager.centerPopUp(pop1);
}
}
private function ➥
functionsBBClick(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:
clearClientDetails();
break;

case 1:
saveClient();
break;
case 2:
deleteClient();
break;
}
}
private function ➥
componentsBBClick(event:ItemClickEvent):void
{
switch (event.index)
{
case 0:
model.MainViewStackState=
model.CLIENT_MANAGER;
break;
case 1:
model.MainViewStackState=
model.PROJECT_ADMIN;
break;
case 2:
model.MainViewStackState=
model.DASHBOARD;
break;
}
}
</mx:Script>
<mx:HBox width="100%" height="100%">
<mx:Image width="190" height="74"/>
<mx:VBox width="100%" height="100%" verticalAlign="bottom">
<mx:HBox width="100%">
<mx:Button label="N" click="clearClientDetails()"/>
<mx:Button label="S" click="saveClient()"/>
<mx:Button label="D" click="deleteClient()"/>
<mx:Spacer width="100%" />
<mx:Button label="Client Manager"
click="{model.MainViewStackState=model.CLIENT_MANAGER}"/>
<mx:Button label="Project Administrator"
click="{model.MainViewStackState=model.PROJECT_ADMIN}"/>
<mx:Button label="Dashboard"
click="{model.MainViewStackState=model.DASHBOARD}"/>
</mx:HBox>
</mx:VBox>
</mx:HBox>
</mx:Canvas>

Footer.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="25">
<mx:Label text="Footer" fontSize="16" color="white" fontWeight="bold" />
</mx:Canvas>


Main View

The MainView component holds the major aspects of the application and drives which part of the application is visible based on its ViewStack state.

MainView.mxml (com.af.clientmanager.view.Main)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
backgroundColor="#888888"
xmlns:ClientDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientDetailViews.*"
xmlns:ClientFinancialsDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientFinancialsDetailViews.*"
xmlns:ClientProjectsDetailViews=
"com.af.clientmanager.view.ClientManager. ➥
ClientProjectsDetailViews.*"
xmlns:components="com.af.clientmanager.view. ➥
ClientManager.components.*"
xmlns:ClientManager="com.af.clientmanager.view. ➥
ClientManager.*"
xmlns:ProjectManager="com.af.clientmanager.view. ➥
ProjectManager.*"
xmlns:Dashboard="com.af.clientmanager.view.Dashboard.*">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>


<mx:HBox width="100%" height="100%">
<components:ClientList />
<mx:ViewStack id="vsMain" width="100%" height="100%"
paddingTop="0"
creationPolicy="all"
selectedIndex="{model.MainViewStackState}">
<mx:Canvas id="clientManagerView"
showEffect="Fade" hideEffect="Fade">
<ClientManager:ClientManagerView />
</mx:Canvas>
<mx:Canvas id="projectManagerView"
showEffect="Fade" hideEffect="Fade">
<ProjectManager:ProjectManagerView />
</mx:Canvas>
<mx:Canvas id="dashboardView" showEffect="Fade"
hideEffect="Fade">
<Dashboard:DashboardView />
</mx:Canvas>
</mx:ViewStack>
</mx:HBox>
</mx:Canvas>


The main view contains several namespace definitions. Those alias definitions will grant
you access to use components in their locations. This view contains and maintains ClientList, ClientManagerView, ProjectManagerView, and DashboardView.


Client Manager View

Another view of interest is the client manager view.

ClientManagerView.mxml (com.af.clientmanager.view)


<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
xmlns:ClientDetailViews="com.af.clientmanager.view. ➥
ClientManager.ClientDetailViews.*"
xmlns:ClientProjectsDetailViews="com.af.clientmanager.view. ➥
ClientManager.ClientProjectsDetailViews.*"
xmlns:ClientFinancialsDetailViews="com.af.clientmanager. ➥
view.ClientManager.ClientFinancialsDetailViews.*"
xmlns:components="com.af.clientmanager.view. ➥
ClientManager.components.*">

<mx:VBox width="100%" height="100%">
<mx:HBox>
<mx:Button label="Overview"
click="{vsAppLevel.selectedChild=
clientOverview}"/>
<mx:Button label="Projects"
click="{vsAppLevel.selectedChild=
clientProjects}"/>
<mx:Button label="Financials"
click="{vsAppLevel.selectedChild=
clientFinancials}"/>
</mx:HBox>
<mx:ViewStack id="vsAppLevel" width="100%" height="100%"
paddingTop="0"
creationPolicy="all">
<mx:Canvas id="clientOverview"
showEffect="Fade" hideEffect="Fade">
<ClientDetailViews:ClientOverview />
</mx:Canvas>
<mx:Canvas id="clientProjects"
showEffect="Fade" hideEffect="Fade">
<ClientProjectsDetailViews:ClientProjects />
</mx:Canvas>
<mx:Canvas id="clientFinancials"
showEffect="Fade" hideEffect="Fade">
<ClientFinancialsDetailViews:ClientFinancials />
</mx:Canvas>
</mx:ViewStack>
<mx:HBox width="100%" height="40%">
<components:IssuesMilestonesList />
<components:DocumentsList />
</mx:HBox>
</mx:VBox>
</mx:VBox>


Client Overview

The last piece of the application we will inspect here is ClientOverview.mxml. That is about as deep as we can go to fully string together the application.

ClientOverview.mxml (com.af.clientmanager.view.ClientManager.ClientDetailViews)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx=http://www.adobe.com/2006/mxml
xmlns:ClientDetailViews=
"com.af.clientmanager.view.ClientManager.ClientDetailViews.*"
width="100%" height="100%" >
<mx:TabNavigator borderStyle="solid" width="100%" height="100%">
<mx:VBox label="General Information"
width="100%" height="100%">
<ClientDetailViews:clientInfo />
</mx:VBox>
<mx:VBox label="Description" width="100%" height="100%">
<ClientDetailViews:clientDescription />
</mx:VBox>
<mx:VBox label="Location" width="100%" height="100%">
<ClientDetailViews:clientLocation />
</mx:VBox>
<mx:VBox label="Contacts" width="100%" height="100%">
<ClientDetailViews:clientContacts />
</mx:VBox>
<mx:VBox label="Links" width="100%" height="100%">
<ClientDetailViews:clientLinks />
</mx:VBox>
</mx:TabNavigator>
</mx:Canvas>


Flex RemoteObject Configuration


To communicate with the Spring services, you will need to configure a transport protocol in Flex to call those services. The Spring services have been built to return a binary object to Flex. For Flex to call those services, a RemoteObject definition needs to be added to the Cairngorm ServiceLocator. This definition needs to specify a destination that matches the destination running in context in af_Central with BlazeDS.

Services.mxml (com.af.clientmanager.services)


<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="clientService"
destination="clientService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="invoiceService"
destination="invoiceService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="mediaService"
destination="mediaService"
showBusyCursor="true">
</mx:RemoteObject>

<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="projectService"
destination="projectService"
showBusyCursor="true">
</mx:RemoteObject>
<mx:RemoteObject endpoint="{model.SERVICE_ENDPOINT}"
id="secService"
destination="secService"
showBusyCursor="true">
</mx:RemoteObject>
</cairngorm:ServiceLocator>


Notice that each service has an endpoint that points to the af_Central AMF gateway running locally on a Tomcat web server. It is stored as a constant on the model, so we need to change it in only one place when this application is moved to a production environment.


public const SERVICE_ENDPOINT= "http://localhost:8080/af_Central/spring/messagebroker/amf";


The other counterpart to the ServiceLocator is the destination found in the SERVICE_
ENDPOINT. The destination is defined in the applicationContext.xml file, since we are using Spring Blaze Integration (SBI) to expose service beans for Flex Remoting


The destinations in the Services.mxml definitions match the exposed service beans
in applicationContext.xml:


<bean id="clientService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="clientServiceBean"/>
</bean>
<bean id="projectService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="projectServiceBean"/>
</bean>
<bean id="invoiceService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="invoiceServiceBean"/>
</bean>

<bean id="mediaService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="mediaServiceBean"/>
</bean>
<bean id="secService"
class="org.springframework.flex.messaging. ➥
remoting.FlexRemotingServiceExporter">
<property name="messageBroker"
ref="mySpringManagedMessageBroker"/>
<property name="service" ref="secServiceBean"/>
</bean>


we are using SBI to handle communication between Flex and Spring. If we were using SpringFactory instead to expose Spring beans for Remoting, we would set our RemoteObject destinations in the remoting-config.xml


<destination id="secService">
<properties>
<factory>spring</factory>
<source>secService</source>
</properties>
</destination>
<destination id="clientService">
<properties>
<factory>spring</factory>
<source>clientService</source>
</properties>
</destination>


If you compare the destination tag on the RemoteObject to the id on the remoting-config.xml destinations, you will notice they match. This is how the service is located and a method can be called from Flex. The factory definition for Spring wires the destination service in the remotingconfig.xml file to Spring’s WebApplicationContext

Flex Component Construction Using Cairngorm

Flex Component Construction Using Cairngorm


We have configured the BlazeDS server, MySQL database, and Spring beans, and have built the Spring services. Flex is ready to consume those services with the help of Cairngorm to facilitate the transport of data.

We have strung together the entire application and need to populate it with data and application logic to visualize the data. To get started, we will build the main client views that will manage our client’s data through the main CRUD calls for the Client object.


Coding the Flex Transport Layer

To enable Flex to make calls to the Spring services, we need to create commands to support method calls exposed by Spring. Each command in Cairngorm needs a corresponding event. Flex is an event-based framework, and Cairngorm’s Event wraps the base Flex Event class.


DeleteClientEvent.as


package com.af.clientmanager.control.commands.events
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.control.MainController;
import com.af.clientmanager.model.vo.ClientVO;
public class DeleteClientEvent extends CairngormEvent
{
public static const EVENT_DELETE_CLIENT:String =
"event_delete_client";
public var clientVO : ClientVO;
public function DeleteClientEvent( clientVO : ClientVO )
{
super(EVENT_DELETE_CLIENT );
this.clientVO = clientVO;
}
}
}

DeleteClientCommand.as


package com.af.clientmanager.control.commands
{
import com.adobe.cairngorm.commands.Command;
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.business.ClientDelegate;
import com.af.clientmanager.control.commands.events. ➥
DeleteClientEvent;
import com.af.clientmanager.model.ModelLocator;
import mx.controls.Alert;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

public class DeleteClientCommand implements Command, IResponder
{
private var model : ModelLocator =
ModelLocator.getInstance();
public function execute( event : CairngormEvent ) : void
{
var delegate : ClientDelegate = new ClientDelegate(
this );
var deleteClientEvent : DeleteClientEvent =
DeleteClientEvent( event );
delegate.deleteClient( deleteClientEvent.clientVO );
}
public function result( data:Object ) : void
{
var event:ResultEvent = data as ResultEvent;
var arrLength:int = model.clientsDP.length;
// Business logic to remove client
// from the model ArrayCollection
for(var i:int =0; i < arrLength; i++)
{
if(model.selectedClient.objectIdentifier ==
model.clientsDP[i].objectIdentifier)
{
model.clientsDP.removeItemAt(i);
break;
}
}
}
public function fault(event:Object):void
{
var faultEvt:FaultEvent = event as FaultEvent;
Alert.show("ERROR: " + event.toString());
}
}
}

GetClientsEvent.as


package com.af.clientmanager.control.commands.events
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.control.MainController;
public class GetClientsEvent extends CairngormEvent
{
public static const EVENT_GET_CLIENTS:String =
"event_get_clients";
public function GetClientsEvent():void
{
super(EVENT_GET_CLIENTS);
}
}
}

GetClientsCommand.as


package com.af.clientmanager.control.commands
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.business.ClientDelegate;
import com.af.clientmanager.control.commands.events. ➥
GetClientsEvent;
import com.af.clientmanager.model.ModelLocator;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
public class GetClientsCommand implements ICommand, IResponder
{
private var model:ModelLocator =
ModelLocator.getInstance();
public function execute(event:CairngormEvent):void
{
var getClientsEvent:GetClientsEvent =
GetClientsEvent(event);
var delegate : ClientDelegate = new ClientDelegate(
this );
delegate.getClients();
}

public function result(data:Object):void
{
model.clientsDP = ArrayCollection(data.result);
}
public function fault(event:Object):void
{
var faultEvt:FaultEvent = event as FaultEvent;
Alert.show("ERROR: " + event.toString());
}
}
}

InsertClientEvent.as


package com.af.clientmanager.control.commands.events
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.control.MainController;
import com.af.clientmanager.model.vo.ClientVO;
public class InsertClientEvent extends CairngormEvent
{
public static const EVENT_INSERT_CLIENT:String =
"event_insert_client";
public var clientVO : ClientVO;
public function InsertClientEvent( clientVO : ClientVO )
{
super(EVENT_INSERT_CLIENT );
this.clientVO = clientVO;
}
}
}

InsertClientCommand.as


package com.af.clientmanager.control.commands
{
import com.adobe.cairngorm.commands.Command;
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.business.ClientDelegate;
import com.af.clientmanager.control.commands.events. ➥
InsertClientEvent;
import com.af.clientmanager.model.ModelLocator;
import com.af.clientmanager.model.vo.ClientVO;

import mx.controls.Alert;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public class InsertClientCommand implements Command, IResponder
{
private var model : ModelLocator =
ModelLocator.getInstance();
public function execute( event : CairngormEvent ) : void
{
var delegate : ClientDelegate = new ClientDelegate(
this );
var insertClientEvent : InsertClientEvent =
InsertClientEvent( event );
delegate.insertClient( insertClientEvent.clientVO );
}
public function result( data:Object ) : void
{
var event:ResultEvent = data as ResultEvent;
model.clientsDP.addItem(ClientVO(data));
}
public function fault(event:Object):void
{
var faultEvt:FaultEvent = event as FaultEvent;
Alert.show("ERROR: " + event.toString());
}
}
}

UpdateClientEvent.as


package com.af.clientmanager.control.commands.events
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.control.MainController;
import com.af.clientmanager.model.vo.ClientVO;
public class UpdateClientEvent extends CairngormEvent
{
public static const EVENT_UPDATE_CLIENT:String =
"event_update_client";
public var clientVO : ClientVO;

public function UpdateClientEvent( clientVO : ClientVO )
{
super( EVENT_UPDATE_CLIENT );
this.clientVO = clientVO;
}
}
}

UpdateClientCommand.as


package com.af.clientmanager.control.commands
{
import com.adobe.cairngorm.commands.Command;
import com.adobe.cairngorm.control.CairngormEvent;
import com.af.clientmanager.business.ClientDelegate;
import com.af.clientmanager.control.commands.events. ➥
UpdateClientEvent;
import com.af.clientmanager.model.ModelLocator;
import com.af.clientmanager.model.vo.ClientVO;
import mx.controls.Alert;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public class UpdateClientCommand implements Command, IResponder
{
private var model : ModelLocator =
ModelLocator.getInstance();
public function execute( event : CairngormEvent ) : void
{
var delegate : ClientDelegate = new ClientDelegate(
this );
var updateClientEvent : UpdateClientEvent =
UpdateClientEvent( event );
delegate.updateClient( updateClientEvent.clientVO );
}
public function result( data:Object ) : void
{
var event:ResultEvent = data as ResultEvent;
}


public function fault( event:Object ) : void
{
var faultEvt:FaultEvent = event as FaultEvent;
Alert.show("ERROR: " + event.toString());
}
}
}


To allow a command to be executed, you will need to update the MainController class to
map events to commands,

MainController.as


package com.af.clientmanager.control
{
import com.adobe.cairngorm.control.FrontController;
import com.af.clientmanager.control.commands.*;
import com.af.clientmanager.control.commands.events.*;
public class MainController extends FrontController
{
public function MainController():void
{
// Client
addCommand(GetClientsEvent.EVENT_GET_CLIENTS,
GetClientsCommand );
addCommand(InsertClientEvent.EVENT_INSERT_CLIENT,
InsertClientCommand );
addCommand(DeleteClientEvent.EVENT_DELETE_CLIENT,
DeleteClientCommand );

addCommand(UpdateClientEvent.EVENT_UPDATE_CLIENT,
UpdateClientCommand );
}
}
}


The controller in Cairngorm serves two main purposes:

• It accesses the event constants on the events themselves that are used to identify Cairngorm events in the framework.

• It maps the event constants to a corresponding command.The controller is listening
for those event types to be dispatched, and will call a mapped command when it catches
the event.


The commands reference delegates that represent method stubs for each Spring service.
Each Spring service has a delegate defined in Cairngorm. This is done to keep the layers in sync for maintainability purposes. You can see the client methods available in Spring through the ClientDelegate

ClientDelegate.as


package com.af.clientmanager.business
{
import com.adobe.cairngorm.business.ServiceLocator;
import com.af.clientmanager.model.vo.ClientContactsVO;
import com.af.clientmanager.model.vo.ClientLinksVO;
import com.af.clientmanager.model.vo.ClientVO;
import mx.controls.Alert;
import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
public class ClientDelegate
{
private var responder : IResponder;
private var service : Object;
public function ClientDelegate( responder : IResponder )
{
this.service =
ServiceLocator.getInstance().getService(
"clientService" );
this.responder = responder;
}

// Client
public function getClients(): void
{
var call:AsyncToken = service.getClients();
call.addResponder( responder );
}
public function insertClient(clientVO : ClientVO): void
{
var call:AsyncToken =
service.insertClient(clientVO);
call.addResponder( responder );
}
public function deleteClient(clientVO : ClientVO): void
{
var call:AsyncToken =
service.deleteClient(clientVO);
call.addResponder( responder );
}
public function updateClient(clientVO : ClientVO): void
{
var call:AsyncToken =
service.updateClient(clientVO);
call.addResponder( responder );
}
// Client Contacts
public function getClientContacts(key:Number): void
{
var call:AsyncToken =
service.getClientContacts(key);
call.addResponder( responder );
}
public function insertClientContact(
clientContactVO : ClientContactsVO): void
{
var call:AsyncToken =
service.insertClientContact(clientContactVO);
call.addResponder( responder );
}

public function deleteClientContact(
clientContactVO : ClientContactsVO): void
{
var call:AsyncToken =
service.deleteClientContact(clientContactVO);
call.addResponder( responder );
}
public function updateClientContact(
clientContactVO : ClientContactsVO): void
{
var call:AsyncToken =
service.updateClientContact(clientContactVO);
call.addResponder( responder );
}
}
}

Coding the Flex Components

Coding the Flex Components


The last parts we need to code are the actual views to display data.


Coding the Client List

The client list is the first component we will tackle. It is a list of client information containing an uploaded company logo, client name, client location, and client phone number

ClientList.mxml (com.af.clientmanager.view.ClientManager.components)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
height="100%"
xmlns:Renderers="com.af.clientmanager.view.Renderers.*"
creationComplete="initComp()">
<mx:Script>
<![CDATA[
import com.adobe.cairngorm.control. ➥
CairngormEventDispatcher;

import com.af.clientmanager.control.commands.events. ➥
GetClientsEvent;
import com.af.clientmanager.model.ModelLocator;
[Bindable]
private var model:ModelLocator =
ModelLocator.getInstance();
private function initComp():void
{
CairngormEventDispatcher.getInstance().dispatchEvent(
new GetClientsEvent());
}
]]>
</mx:Script>
<mx:VBox width="278" height="100%">
<mx:Repeater id="rp" dataProvider="{model.clientsDP}">
<Renderers:ClientListRenderer clientData="{rp.currentItem}"/>
</mx:Repeater>
</mx:VBox>
</mx:Canvas>

ClientListRenderer.mxml (com.af.clientmanager.view.Renderers)


<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
showEffect="Fade"
width="100%" height="65"
creationComplete="setText()">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.vo.ClientVO;
import mx.effects.Fade;
import mx.effects.Blur;
import mx.effects.Parallel;
import mx.effects.Move;
import mx.core.IUIComponent;
import mx.managers.DragManager;
import mx.core.UIComponent;
import mx.core.DragSource;
import mx.controls.Alert;
import com.af.clientmanager.model.ModelLocator;

[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
// This object is populated with the data
// passed in from the controlling control
[Bindable]
public var clientData:Object;
private function setText():void
{
clName.text = clientData.clientName;
clAddress.text = clientData.city + "," +
clientData.state;
clPhone.text = clientData.phone;
MoveItem();
}
public function MoveItem():void
{
var parallel:Parallel = new Parallel();
parallel.target = clientItem;
var blur:Blur = new Blur();
blur.blurXFrom = 5;
blur.blurXTo = 0;
blur.blurYFrom = 5;
blur.blurYTo = 0;
var move:Move = new Move();
move.target = this;
move.xFrom = -1000;
move.xTo = 0;
move.play();
parallel.addChild(blur);
parallel.addChild(move);
parallel.play();
}
private function setSelectedItem():void
{
model.selectedClient = ClientVO(this.clientData);
this.dispatchEvent(new Event("changeAddressEvent",
true)); // Bubble to parent
}
]]>
</mx:Script>

<mx:HBox id="clientItem" styleName="WeatherPanel"
width="100%" height="65"
useHandCursor="true" mouseChildren="false"
buttonMode="true" click="setSelectedItem()">
<mx:Image source="{clientData.image}" />
<mx:VBox width="100%" verticalGap="0"
horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<mx:Label id="clName" styleName="clientListFont"/>
<mx:Label id="clAddress"
styleName="clientListFont"/>
<mx:Label id="clPhone" styleName="clientListFont"/>
</mx:VBox>
</mx:HBox>
</mx:Canvas>


To populate the client list with data, we first need to trigger a Cairngorm event to tell the
Spring service that Flex wants client data.

CairngormEventDispatcher.getInstance().dispatchEvent(new GetClientsEvent());


This is triggered in the initComp() method that is called in the creationComplete tag for
the Canvas. The CairngormEventDispatcher will execute any event in the Cairngorm framework that is defined by the controller to handle data, or it can be used to trigger other actions in your Flex application, such as state changes. In this case, Cairngorm executes a GetClientsEvent that maps to the GetClientsCommand. This command executes a call to the business delegate ClientDelegate.getClients(). The getClients() method does not take any parameters and will return a list of client objects to the result() method in the command. The result() method then assigns the list to an ArrayCollection on the model, which is bound to from the client list Repeater.dataProvider. The data assignment will cause the Repeater to be populated with the new list of clients.

IssuesMilestonesList.mxml (com.af.clientmanager.view.ClientManager.components)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
styleName="panelSkin">

<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>
<mx:Label text="Issues &amp; Milestones"
styleName="heading"
y="6" x="9"/>
<mx:VBox width="100%" height="100%"
paddingBottom="10" paddingLeft="10" paddingRight="10"
y="34">
<mx:DataGrid width="100%" height="100%"
dataProvider="{model.issuesDP}"
y="49">
<mx:columns>
<mx:DataGridColumn dataField="issueStatus"
headerText="Status" />
<mx:DataGridColumn
dataField="issueDescription"
headerText="Description"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Canvas>

DocumentsList.mxml (com.af.clientmanager.view.ClientManager.components)


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
styleName="panelSkin">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>

<mx:Label text="Documents"
styleName="heading"
y="6" x="9"/>
<mx:VBox width="100%" height="100%"
paddingBottom="10" paddingLeft="10" paddingRight="10"
y="34">
<mx:DataGrid width="100%" height="100%"
dataProvider="{model.issuesDP}">
<mx:columns>
<mx:DataGridColumn dataField="mediaType"
headerText="Type" />
<mx:DataGridColumn dataField="mediaName"
headerText="Document Name"/>
<mx:DataGridColumn dataField="creationDate"
headerText="Upload Date"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Canvas>


DataGrid components with dataProviders that reference ArrayCollection collections loaded on the model. Within the DataGrid tags, columns are defined to show only portions of the objects contained in the list. If you do not supply a series of columns, the DataGrid will be populated with the attributes of the entire object that is part of the dataProvider.


Coding the Client Overview

The client overview has several components that make up the contents of a TabNavigator that controls which part of the client details is visible. The first component is clientInfo.mxml, which contains the main information tied to a client, including name, address, phone, and web site. <mx:Binding> tags that bind form elements to the selected client. If you make a change to the form items, the changes are automatically stored in the selectedClient ClientVO on the model. This makes updating or deleting information easy, since a CairngormEventDispatcher can dispatch an event and add the selectedClient to the call from the model.

clientInfo.mxml (com.af.clientmanager.view.ClientManager.ClientDetailViews)


<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%">
<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;

[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
private function openWebSite(URL:String):void
{
var u:URLRequest = new URLRequest(URL);
navigateToURL(u,"_blank");
}
]]>
</mx:Script>
<mx:Binding source="tiName.text"
destination="model.selectedClient.clientName"/>
<mx:Binding source="tiAddress.text"
destination="model.selectedClient.addressLine1"/>
<mx:Binding source="tiCity.text"
destination="model.selectedClient.city"/>
<mx:Binding source="tiState.text"
destination="model.selectedClient.state"/>
<mx:Binding source="tiZip.text"
destination="model.selectedClient.zip"/>
<mx:Binding source="tiPhone.text"
destination="model.selectedClient.phone"/>
<mx:Binding source="tiLink.text"
destination="model.selectedClient.link"/>
<mx:PhoneNumberValidator
source="{tiPhone}"
property="text" />
<mx:Form>
<mx:FormItem label="Name" required="true">
<mx:TextInput id="tiName" width="250" height="30"
text="{model.selectedClient.clientName}"/>
</mx:FormItem>
<mx:FormItem label="Address">
<mx:TextInput id="tiAddress" width="250"
text="{model.selectedClient.addressLine1}" />
</mx:FormItem>
<mx:FormItem label="City">
<mx:TextInput id="tiCity" width="250"
text="{model.selectedClient.city}" />
</mx:FormItem>

<mx:FormItem label="State">
<mx:TextInput id="tiState" width="250"
text="{model.selectedClient.state}" />
</mx:FormItem>
<mx:FormItem label="Zip">
<mx:TextInput id="tiZip" width="250"
text="{model.selectedClient.zip}" />
</mx:FormItem>
<mx:FormItem label="Phone">
<mx:TextInput id="tiPhone" width="250"
text="{model.selectedClient.phone}" />
</mx:FormItem>
<mx:FormItem label="Web Site">
<mx:TextInput id="tiLink" width="250"
text="{model.selectedClient.link}" />
</mx:FormItem>
<mx:FormItem paddingTop="8">
<mx:HBox>
<mx:Button label="Open Web Site"
click="openWebSite(model.selectedClient.link)"/>
<mx:Button label="Upload Image"
width="100"/>
</mx:HBox>
</mx:FormItem>
</mx:Form>
<mx:VBox width="100%" height="100%" paddingTop="15">
<mx:VBox styleName="frame" minHeight="200" minWidth="300"
paddingBottom="3" paddingLeft="3"
paddingRight="3" paddingTop="3">
<mx:Image id="img" y="14" width="200"
complete="img.visible=true"/>
</mx:VBox>
</mx:VBox>
</mx:HBox>



clientDescription.mxml (com.af.clientmanager.view.ClientManager.ClientDetailViews)


<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
paddingBottom="5" paddingLeft="5"
paddingRight="5" paddingTop="5">

<mx:Script>
<![CDATA[
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
]]>
</mx:Script>
<mx:Binding source="rteDesc.text"
destination="model.selectedClient.description"/>
<mx:TextArea id="rteDesc" width="100%" height="100%"
text="{model.selectedClient.description}" />
</mx:VBox>

clientContacts.mxml (com.af.clientmanager.view.ClientManager.ClientDetailViews)


<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5"
xmlns:Renderers="com.af.clientmanager.view.Renderers.*">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import com.af.clientmanager.view.Dialogs. ➥
DeleteContactConfirmationDialog;
import com.af.clientmanager.model.vo.ClientContactsVO;
import com.af.clientmanager.control.commands.events.*;
import com.af.clientmanager.model.ModelLocator;
import com.adobe.cairngorm.control. ➥
CairngormEventDispatcher;
[Bindable]
private var model:ModelLocator =
ModelLocator.getInstance();

[Embed(source="/assets/packs/cleanSkin/icon_contactDefaultImage.png")]
[Bindable]
public var imgCls:Class;
private function insertContact():void
{
var contact:ClientContactsVO = new ClientContactsVO;
// Set the client to contact relationship
contact.assocobjectID =
model.selectedClient.objectIdentifier;
contact.contactName = tiName.text;
contact.jobTitle = tiTitle.text;
contact.phoneWork = tiPhoneWork.text;
contact.phoneCell = tiPhoneCell.text;
contact.email = tiEmail.text;
contact.responsibility = tiResponsibility.text;
CairngormEventDispatcher.getInstance().dispatchEvent(
new InsertClientContactEvent(contact));
}
private function updateContact():void
{
CairngormEventDispatcher.getInstance().dispatchEvent(
new UpdateClientContactEvent(
ClientContactsVO(model.selectedContact)));
}
private function deleteContact():void
{
// Don't try to delete unless a client is selected
if(model.selectedContact.objectIdentifier > 0)
{
var pop1:DeleteContactConfirmationDialog =
DeleteContactConfirmationDialog(
PopUpManager.createPopUp(this,
DeleteContactConfirmationDialog, true));
pop1.confirmationMessageTitle =
"Delete Confirmation";
pop1.confirmationMessageBody =
"Are you sure you want to delete: " +
model.selectedContact.contactName;
PopUpManager.centerPopUp(pop1);
}
}

private function imageError():void
{
contactImage.source=imgCls;
}
]]>
</mx:Script>
<mx:Binding source="tiName.text"
destination="model.selectedContact.contactName"/>
<mx:Binding source="tiTitle.text"
destination="model.selectedContact.jobTitle"/>
<mx:Binding source="tiPhoneWork.text"
destination="model.selectedContact.phoneWork"/>
<mx:Binding source="tiPhoneCell.text"
destination="model.selectedContact.phoneCell"/>
<mx:Binding source="tiEmail.text"
destination="model.selectedContact.email"/>
<mx:Binding source="tiResponsibility.text"
destination="model.selectedContact.responsibility"/>
<mx:EmailValidator
source="{tiEmail}"
property="text"/>
<mx:PhoneNumberValidator
source="{tiPhoneWork}"
property="text" />
<mx:PhoneNumberValidator
source="{tiPhoneCell}"
property="text" />
<mx:HBox width="100%" height="100%">
<mx:VBox width="40%" height="100%" styleName="frame"
horizontalAlign="center" verticalAlign="middle"
paddingTop="10">
<mx:Label text="Contact List" styleName="heading" />
<mx:List id="contactList" width="100%" height="100%"
dataProvider="{model.contactsDP}"
labelField="contactName"
change="{model.selectedContact=
ClientContactsVO(contactList.selectedItem)}"/>
</mx:VBox>

<mx:VBox width="20%" height="100%">
<mx:HBox>
<mx:Button label="Add" width="75"
click="insertContact()"/>
<mx:Button label="Update" width="75"
click="updateContact()"/>
<mx:Button label="Delete" width="75"
click="deleteContact()"/>
</mx:HBox>
<mx:VBox styleName="frame"
paddingBottom="3" paddingLeft="3"
paddingRight="3" paddingTop="3">
<mx:Image id="contactImage"
width="100%" height="100%"
ioError="imageError()" source="{imgCls}"/>
</mx:VBox>
<mx:Button label="Upload Image" />
</mx:VBox>
<mx:VBox width="80%" height="100%">
<mx:Form width="100%">
<mx:FormItem label="Name" required="false">
<mx:TextInput id="tiName"
text="{model.selectedContact.contactName}"
width="250"/>
</mx:FormItem>
<mx:FormItem label="Title" required="false">
<mx:HBox>
<mx:TextInput id="tiTitle"
text="{model.selectedContact.jobTitle}"
width="250"/>
</mx:HBox>
</mx:FormItem>
<mx:FormItem label="Work" required="false">
<mx:HBox>
<mx:TextInput id="tiPhoneWork"
text="{model.selectedContact.phoneWork}"
width="250"/>
</mx:HBox>
</mx:FormItem>

<mx:FormItem label="Cell" required="false">
<mx:HBox>
<mx:TextInput id="tiPhoneCell"
text="{model.selectedContact.phoneCell}"
width="250"/>
</mx:HBox>
</mx:FormItem>
<mx:FormItem label="Email" required="false">
<mx:HBox>
<mx:TextInput id="tiEmail"
text="{model.selectedContact.email}"
width="250"/>
</mx:HBox>
</mx:FormItem>
</mx:Form>
</mx:VBox>
</mx:HBox>
<mx:HBox width="100%" height="20%">
<mx:Label text="Notes:" styleName="heading" />
<mx:TextArea id="tiResponsibility"
text="{model.selectedContact.responsibility}"
width="100%" height="100%" />
</mx:HBox>
</mx:VBox>

DeleteContactConfirmationDialog.mxml


<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
width="0" height="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
creationComplete="initComp();">

<mx:Script>
<![CDATA[
import com.af.clientmanager.control.commands.events. ➥
DeleteClientEvent;
import com.adobe.cairngorm.control.CairngormEvent;
import mx.effects.Blur;
import mx.effects.Fade;
import mx.effects.Move;
import mx.effects.Parallel;
import mx.effects.Resize;
import mx.effects.easing.*;
import mx.events.EffectEvent;
import mx.controls.Alert;
import mx.managers.PopUpManager;
import com.adobe.cairngorm.control. ➥
CairngormEventDispatcher;
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
[Bindable] public var confirmationMessageTitle: String;
[Bindable] public var confirmationMessageBody: String;
public function initComp():void
{
this.width = 0;
this.height = 0;
var parallel:Parallel = new Parallel();
parallel.target = this;
var resize:Resize = new Resize();
resize.target = this;
resize.widthTo = mainCanvas.width;
resize.heightTo = mainCanvas.height;
var move:Move = new Move();
move.xFrom = this.stage.width/2;
move.yFrom = this.stage.height/2;
move.xTo = this.stage.width/2 - mainCanvas.width/2;
move.yTo = this.stage.height/2 –
mainCanvas.height/2;










var fade:Fade = new Fade();
fade.alphaFrom = 0;
fade.alphaTo = 1;
var blur:Blur = new Blur();
blur.blurXFrom = 30;
blur.blurYFrom = 30;
blur.blurXTo = 0;
blur.blurYTo = 0;
parallel.addChild(resize);
parallel.addChild(move);
parallel.addChild(fade);
parallel.addChild(blur);
resize.easingFunction =
mx.effects.easing.Circular.easeInOut;
move.easingFunction =
mx.effects.easing.Circular.easeInOut;
fade.easingFunction =
mx.effects.easing.Circular.easeInOut;
parallel.duration = 600;
parallel.play();
parallel.addEventListener(EffectEvent.EFFECT_END,
effectEndHandler);
container.visible = false;
}
public function effectEndHandler(event:EffectEvent):void
{
container.visible = true;
}
private function closeDialog():void
{
var parallel:Parallel = new Parallel();
parallel.target = this;
var fade:Fade = new Fade();
fade.alphaFrom = 1;
fade.alphaTo = 0;
parallel.addChild(fade);
fade.easingFunction =
mx.effects.easing.Circular.easeInOut;

parallel.duration = 400;
parallel.play();
parallel.addEventListener(EffectEvent.EFFECT_END,
closeDialogFinal);
}
private function closeDialogFinal(event:EffectEvent):void
{
PopUpManager.removePopUp(this);
}
public function
HeaderMouseDownHandler(event:MouseEvent):void
{
this.startDrag(false);
}
public function
HeaderMouseUpHandler(event:MouseEvent):void
{
this.stopDrag();
}
private function yesClicked():void
{
CairngormEventDispatcher.getInstance().dispatchEvent(
new DeleteClientEvent(model.selectedClient));
closeDialog();
}
]]>
</mx:Script>
<mx:Canvas id="mainCanvas" width="386" height="196"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
styleName="panelSkin">
<mx:VBox width="100%" height="100%" id="container"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
paddingLeft="10" paddingRight="10"
paddingBottom="10" paddingTop="5"
verticalGap="0">
<mx:Canvas width="100%" height="15%"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
mouseDown="HeaderMouseDownHandler(event);"
mouseUp="HeaderMouseUpHandler(event);"
mouseChildren="false" buttonMode="true"
useHandCursor="true">
<mx:Image id="iconImage"

source="@Embed('/assets/images/icon_redXwarning.png')"/>
<mx:Text text="{confirmationMessageTitle}"
width="90%" height="100%" x="25" y="3"
styleName="heading"/>
</mx:Canvas>
<mx:Spacer height="3" />
<mx:HBox width="100%" height="80%"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
horizontalAlign="center" verticalAlign="middle">
<mx:Text id="txtMessageBody"
text="{confirmationMessageBody}"/>
</mx:HBox>
<mx:HBox width="100%" height="5%"
verticalScrollPolicy="off"
horizontalScrollPolicy="off">
<mx:Spacer width="90%" />
<mx:Button label="Yes" height="20" width="80"
click="yesClicked()"/>
<mx:Button label="No" height="20" width="80"
click="closeDialog()"/>
</mx:HBox>
</mx:VBox>
</mx:Canvas>
</mx:Canvas>

The Yahoo! Maps API Component: clientLocation.mxml


<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2008 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are
licensed under the BSD (revised) open source license
-->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
creationComplete="handleCreationComplete()">
<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.controls.Alert;
import mx.events.ResizeEvent;
// import maps classes.
import com.yahoo.maps.api.YahooMap;
import com.yahoo.maps.api.YahooMapEvent;
import com.yahoo.maps.api.core.location.Address;
import com.yahoo.maps.webservices.geocoder.GeocoderResult;
import com.yahoo.maps.webservices.geocoder.events. ➥
GeocoderEvent; ;
import com.af.clientmanager.model.ModelLocator;
[Bindable]
public var model : ModelLocator =
ModelLocator.getInstance();
private var _yahooMap:YahooMap;
private var _address:Address;
public function set changeAddress(value:String):void
{
address = new
Address(model.selectedClient.addressLine1 + " " +
model.selectedClient.city + "," +
model.selectedClient.state);
handleCreationComplete();
}

private function handleCreationComplete():void
{
// This examples uses an application id passed into
// the app via FlashVars.
// Get your own from the Yahoo! Developer Network
// @ http://developer.yahoo.com/wsregapp/
var appid:String = model.YAHOO_APP_ID;
// Create a new YahooMap object.
_yahooMap = new YahooMap();
// List for the MAP_INITIALIZE event from YahooMap
_ yahooMap.addEventListener(
YahooMapEvent.MAP_INITIALIZE,
handleMapInitialize);
// Initialize the map, passing the app-id,
// width and height.
_yahooMap.init(appid,mapContainer.width,
mapContainer.height);
mapContainer.addChild(_yahooMap);
mapContainer.addEventListener(ResizeEvent.RESIZE,
handleContainerResize);
_yahooMap.addPanControl();
_yahooMap.addZoomWidget();
_yahooMap.addTypeWidget();
}
private function
handleMapInitialize(event:YahooMapEvent):void
{
// Creating a new address object, passing our
// address string as the single parameter.
_address = new
Address(model.selectedClient.addressLine1 + " "
+ model.selectedClient.city + "," +
model.selectedClient.state);
// Listen for the GEOCODER_SUCCESS event dispatched
// when the data comes back from the webservice.
_address.addEventListener(
GeocoderEvent.GEOCODER_SUCCESS,
handleGeocodeSuccess);

// Send the geocode request.
_address.geocode();
}
private function
handleGeocodeSuccess(event:GeocoderEvent):void
{
// Retrieve the first result returned by the
// geocoder.
var result:GeocoderResult =
address.geocoderResultSet.firstResult;
// Then we&apos;ll get the zoom level and center
//lat and lon to position the map on.
_yahooMap.zoomLevel = result.zoomLevel;
_yahooMap.centerLatLon = result.latlon;
}
private function
handleContainerResize(event:ResizeEvent):void
{
// Set the size of the map based on its
// container's size.
_yahooMap.setSize(mapContainer.width,
mapContainer.height);
}
]]>
</mx:Script>
<mx:Binding source="lblAddr.text" destination="changeAddress"/>
<mx:VBox width="100%" height="100%"
paddingBottom="5" paddingLeft="5"
paddingRight="5" paddingTop="5">
<mx:Label id="lblAddr"
text="{model.selectedClient.addressLine1}
{model.selectedClient.city},
{model.selectedClient.state}"/>
<mx:UIComponent id="mapContainer"
width="100%" height="100%"/>
</mx:VBox>
</mx:Canvas>

Skinning and Styling the Application and Creating the AF – Client Manager Style Sheet

Skinning and Styling the Application


The AF – Client Manager is a combination of graphical skinning and programmatic styles.
Styles for the application include font sizes, font colors, and background alphas. Skinning,
on the other hand, is a little more complex, since I chose to change the base skins for Button, TabNavigator, and TextInput by overriding skins in those components. To do this, I used the 9-slice scaling technique to apply scaled graphics to Flex components.


Using 9-Slice Scaling for Component Skins

The 9-slice scaling technique slices an image file into nine slices that scale when the size of the component that displays the image changes. Using this technique, you can specify which part of the image you want to scale in CSS.


You can define the properties for scaling when you apply the skin in CSS. These can be
applied to Flex component tags such as borderSkin, skin, overSkin, and so on. This gives you a dynamic way to import skins for your applications.


.applicationSkin {
backgroundImage: Embed("assets/packs/cleanSkin/application_skin.png",
scaleGridLeft="15",
scaleGridRight="975",
scaleGridTop="60",
scaleGridBottom="64");
backgroundSize: "100%";
}


Creating the AF – Client Manager Style Sheet


style sheet for the AF – Client Manager application, which handles its
skinnng and styling. Notice the custom styles that start with a dot (.). They are used throughout the application in the styleName tag on Flex components. You can also see that it sets the fonts used in the application, which are MyriadWebPro. You will need to define a location for the font files under the assets folder in your project. As you can see in the CSS, I have an /assets/ fonts directory from which I reference the font files.


AF – Client Manager CSS


@font-face
{
src: url("assets/fonts/MyriadWebPro.ttf");
font-family: main;
font-style: normal;
font-weight: normal;
}
@font-face
{
src: url("assets/fonts/MyriadWebPro-Bold.ttf");
font-family: main;
font-style: normal;
font-weight: bold;
}
/* Font Styles */
.heading
{
color:#666666;
font-family: main;
font-size: 14pt;
font-weight: bold;
}
.clientListFont
{
color:#CCCCCC;
font-family: main;
font-size: 13pt;
font-weight: bold;
}

.appHeading
{
color:#222222;
font-family: main;
font-size: 20pt;
}
/* End Font Styles */
Application {
padding-top:0;
padding-bottom:0;
padding-left:0;
padding-right:0;
vertical-gap:0;
theme-color: #919191;
font-family: main;
color: #333333;
font-size: 11;
background-color: #FFFFFF;
}
.frame
{
background-image:Embed("assets/packs/cleanSkin/frame_skin.png",
scaleGridLeft="10",
scaleGridRight="430",
scaleGridTop="60",
scaleGridBottom="64");
background-size: "100%";
}
.frameContainerSkin
{
background-image:
Embed("assets/packs/cleanSkin/frame_container_skin.png",
scaleGridLeft="10",
scaleGridRight="430",
scaleGridTop="60",
scaleGridBottom="64");
background-size: "100%";
}
.borderStyleforContainer
{
Border-style: solid;
}

.applicationSkin {
background-image:
Embed("assets/packs/cleanSkin/application_skin.png",
scaleGridLeft="10",
scaleGridRight="975",
scaleGridTop="58",
scaleGridBottom="62");
background-size: "100%";
}
.containerSkin {
background-image:
Embed("assets/packs/cleanSkin/container_skin.png",
scaleGridLeft="15",
scaleGridRight="970",
scaleGridTop="58",
scaleGridBottom="62");
background-size: "100%";
border-style: borderStyleforContainer;
}
.tabContainerSkin {
background-image:
Embed("assets/packs/cleanSkin/tabcontainer_skin.png",
scaleGridLeft="10",
scaleGridRight="430",
scaleGridTop="60",
scaleGridBottom="64");
background-size: "100%";
}
.clientList
{
border-style: solid;
border-shickness: 2;
border-color: #f9f9f9;
border-skin: ClassReference("com.af.components.graphics. ➥
RoundedGradientBorder");
fill-colors: #419af2, #0865c4;
fill-alphas: 1, 1;
drop-shadow-enabled: true;
topCornerRadius: 0;
bottomCornerRadius: 0;
corner-radius: 0;
header-height: 0;
}

TextInput {
color:#222222;
font-family: main;
font-size: 20pt;
border-skin: Embed("assets/packs/cleanSkin/textinput_skin.png",
scaleGridLeft="3",
scaleGridRight="112",
scaleGridTop="3",
scaleGridBottom="26");
padding-left: 2;
}
Button {
skin: Embed("assets/packs/cleanSkin/button_skin.png",
scaleGridLeft="7",
scaleGridRight="21",
scaleGridTop="7",
scaleGridBottom="19");
over-skin: Embed("assets/packs/cleanSkin/button_over_skin.png",
scaleGridLeft="7",
scaleGridRight="21",
scaleGridTop="7",
scaleGridBottom="19");
open-duration: 0;
close-duration: 0;
corner-radius: 2;
}
ButtonBar {
First-button-style-name: firstButton;
button-style-name: middleButton;
last-button-style-name: lastButton;
}
.firstButton {
skin: Embed("assets/packs/cleanSkin/button_bar_first.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
over-skin:
Embed("assets/packs/cleanSkin/button_bar_first_over.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
}

.middleButton {
skin: Embed("assets/packs/cleanSkin/button_bar_middle.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
over-skin:
Embed("assets/packs/cleanSkin/button_bar_middle_over.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
}
.lastButton {
skin: Embed("assets/packs/cleanSkin/button_bar_last.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
over-skin:
Embed("assets/packs/cleanSkin/button_bar_last_over.png",
scaleGridLeft="7",
scaleGridRight="25",
scaleGridTop="7,
scaleGridBottom="19");
}
TabNavigator {
border-style: none;
background-alpha: 0;
corner-radius: 0;
tab-style-name: "tabButtonStyleName";
tab-height: 28;
padding-top:0;
}
.tabButtonStyleName {
font-weight:normal;
text-align:left;
skin:Embed("assets/packs/cleanSkin/tab_skin.png",
scaleGridLeft="1",
scaleGridRight="162",
scaleGridTop="1",
scaleGridBottom="20");

selected-up-skin:
Embed("assets/packs/cleanSkin/tab_selected_skin.png",
scaleGridLeft="1",
scaleGridRight="162",
scaleGridTop="1",
scaleGridBottom="20");
selected-over-skin:
Embed("assets/packs/cleanSkin/tab_selected_skin.png",
scaleGridLeft="1",
scaleGridRight="162",
scaleGridTop="1",
scaleGridBottom="20");
over-skin:Embed("assets/packs/cleanSkin/tab_over_skin.png",
scaleGridLeft="1",
scaleGridRight="162",
scaleGridTop="1",
scaleGridBottom="20");
}
.panelSkin {
border-skin: Embed("assets/packs/cleanSkin/panel_skin.png",
scaleGridLeft="10",
scaleGridRight="274",
scaleGridTop="40",
scaleGridBottom="101");
}

About This Site

Howdy! My name is Suersh Rohan and I am the developer and maintainer of this blog. It mainly consists of my thoughts and opinions on the technologies I learn,use and develop with.
Powered by Blogger.

- Copyright © My Code Snapshots -Metrominimalist- Powered by Blogger - Designed by Suresh Rohan -