I am working on a flex web project where a lot of the functionality is modular and the SWFs are loaded with SWFLoader. I've recently created a module which has tooltips on some elements and I was getting a runtime error #1009 when hovering over these elements to see the tooltip. If, instead of loading the SWF with SWFLoader at runtime, I instantiate it in the application directly, the tooltips work fine. Does anyone know why this is?
I'm using Flex SDK 4.5.1 because one of the libs I have to use doesn't work with later versions.
I've isolated the issue into a small example. Here's the code:
main mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
backgroundColor="#000000"
creationComplete="onComplete(event);">
<fx:Script>
<![CDATA[
import mx.controls.SWFLoader;
import mx.events.FlexEvent;
private function onComplete(event:FlexEvent):void {
// Comment this block out to try without loading external swf
var swfLoader:SWFLoader = new SWFLoader();
swfLoader.addEventListener(Event.COMPLETE, swfLoadComplete);
swfLoader.load("ExternalSWF.swf");
// Uncomment this block to try without loading external swf ... Notice the tooltips suddenly work..
/*var externalSwf:ExternalSWF = new ExternalSWF();
this.addElement(externalSwf);*/
}
private function swfLoadComplete(event:Event):void {
var externalSwf:ExternalSWF = (event.target as SWFLoader).content as ExternalSWF;
this.addElement(externalSwf);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
ExternalSWF class (in flash builder 4.7, this is set to compile as a module):
package
{
import spark.components.Button;
import spark.components.VGroup;
[Frame(factoryClass="ExternalSWF")]
public class ExternalSWF extends VGroup
{
public function ExternalSWF()
{
super();
var btn:Button = new Button();
btn.label = "mouse over this";
btn.toolTip = "moused over";
this.addElement(btn);
}
}
}
Thanks, Zeus. That set me on the correct path and I was able to solve my problem.
Here's one solution:
main mxml AS code:
private var _info:IModuleInfo;
private function onComplete(event:FlexEvent):void {
_info = ModuleManager.getModule("ExternalSWF.swf");
_info.addEventListener(ModuleEvent.READY, modEventHandler);
_info.load(null, null, null, moduleFactory);
}
private function modEventHandler(event:ModuleEvent):void {
var externalSwf:ExternalSWF = _info.factory.create() as ExternalSWF;
this.addElement(externalSwf);
}
ExternalSWF
package
{
import mx.controls.Button;
import spark.modules.Module;
public class ExternalSWF extends Module
{
public function ExternalSWF()
{
super();
var btn:Button = new Button();
btn.label = "mouse over this";
btn.toolTip = "moused over";
this.addElement(btn);
}
}
}
Related
I have a Flex Mobile Project that I am working on. I am trying to pass parameters from one view to another. The only problem is that I cannot use navigator.pushView to push the View and the parameter as the view I am pushing to was the previous view. So this wipes out using the addHandler() and the returnObjectsCreated() as I cannot use pushView. I am having to use popView because it is my previous view that I have to pass parameters too. Any help would be appreciated.
That class that has the parameters I need to pass is below. It is a view that shows a list. So I need to pass list.selectedItem to the popview or previous view...
<?xml version="1.0" encoding="utf-8"?>
<amec:BaseBrowseView xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:amec="com.amec.Components.*"
title="Select an item">
<fx:Script>
<![CDATA[
import com.amec.BaseSql;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.events.IndexChangeEvent;
[Bindable]private var resultArr:ArrayCollection = new ArrayCollection();
protected function myList_changeHandler(event:IndexChangeEvent):void
{
navigator.popView();
//Either send a ref to the last view or override createReturn
}
[Bindable(event="myDataChanged")]
private function get myData():ArrayCollection
{
}
]]>
</fx:Script>
<s:List id="list"
height="100%" width="100%"
dataProvider="{myData}"
labelField="DMV_VALUE_1"
change="myList_changeHandler(event);">
</s:List>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
Now below is the previous view that I want to popView to that I need to pass parameters to so I can populate the TextInput with.
<?xml version="1.0" encoding="utf-8"?>
<amec:BaseControl xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:amec="com.amec.Components.*"
horizontalCenter="true">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
protected var textValue:String;
protected function control_creationCompleteHandler(event:FlexEvent):void
{
// todo: get control data from view.data
}
protected function control_clickHandler(event:MouseEvent):void
{
parentView.navigator.pushView(TextListView);
}
]]>
</fx:Script>
<s:Label text="Robert says something meaningful goes here" />
<s:TextInput id="ns" text="{textValue}" editable="false" click="control_clickHandler(event)"/>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
Again I cannot use pushView as the view is already on the stack.
In case you want to do it the right way, on the previous view (the one you want the result going to, add an eventListener for 'add'.
protected function addHandler(event:FlexEvent) : void {
if (navigator.poppedViewReturnedObject != null) {
var returnedObject : Array = navigator.poppedViewReturnedObject.object as Array;
if(!returnedObject) return;
yourData= new ArrayCollection(returnedObject);
}
}
On the view that you are going to pass the object from:
override public function createReturnObject():Object {
var returnedObject : Object = new Object();
returnedObject = dataYouWantToSendBack;
return returnedObject;
}
I looked at using static variables but that was a little messy, I also looked at using Dependency Injection but spending a few hours to set up the framework and get it running is to costly when all I am doing is passing parameters on a popView, so what I came up with is the similar approach I have used when doing Native Android Apps with the Android SDK. A Singleton with getters and setters.
Basically the way I have been able to do this is with the use of a Singleton sharedInstance. See code below:
package com.controls.Text
{
import flash.utils.Dictionary;
[Bindable]
public class ParameterManager
{
private static var instance:ParameterManager = new ParameterManager();
private var dictionary:Dictionary=new Dictionary();
public function ParameterManager( )
{
if (instance != null) { throw new Error('Must use ParameterManager.getInstance().') }
}
public static function getInstance(): ParameterManager {
return instance;
}
public function setParameter(key:*, value:*):void
{
dictionary[key]=value;
}
public function getParameter(key:*):*
{
return dictionary[key];
}
}
}
The method setting the value of the parameter:
protected function myList_changeHandler(event:IndexChangeEvent):void
{
var listViewReturnObject:String = new String();
listViewReturnObject = list.selectedItem.DMV_VALUE_1;
ParameterManager.getInstance().setParameter("selectedItem", listViewReturnObject);
navigator.popView();
}
The method getting the value:
protected function control_creationCompleteHandler(event:FlexEvent):void
{
var listViewReturnObject:Object = new Object();
listViewReturnObject= ParameterManager.getInstance().getParameter("selectedItem");
if (listViewReturnObject != null)
{
dataTxt.text= String(listViewReturnObject);
ParameterManager.getInstance().setParameter("", null); //Make sure objects are cleared when done.
}
}
I'd be extremely grateful if somebody could help me, or point me in the right direction.
I've been trying to get an adobe air application start in system tray, so far I've used this snippet: http://www.swamicharan.com/blog/air/minimizing-an-air-app-to-systemtray/ which works as described, however no matter what I do I can't seem to make it start, minimized, in the system tray. This is the code I have so far:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="100" creationComplete="initApp()" layout="horizontal">
<fx:Script>
<![CDATA[
import mx.events.CloseEvent;
private var trayIcon:BitmapData;
public function initApp():void{
loadTrayIcon();
this.addEventListener(Event.CLOSING, minToTray);
}
public function loadTrayIcon():void{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, readyToTray);
loader.load(new URLRequest("assets/icon.PNG"));
}
private function minToTray(event:Event):void{
event.preventDefault();
dock();
}
public function readyToTray(event:Event):void{
trayIcon = event.target.content.bitmapData;
var myMenu:NativeMenu = new NativeMenu();
var openItem:NativeMenuItem = new NativeMenuItem("Options");
var closeItem:NativeMenuItem = new NativeMenuItem("Close");
openItem.addEventListener(Event.SELECT, unDock);
closeItem.addEventListener(Event.SELECT, closeApp);
myMenu.addItem(openItem);
myMenu.addItem(new NativeMenuItem("", true));
myMenu.addItem(closeItem);
if(NativeApplication.supportsSystemTrayIcon){
SystemTrayIcon(NativeApplication.nativeApplication.icon).tooltip = "Notifier";
SystemTrayIcon(NativeApplication.nativeApplication.icon).
addEventListener(MouseEvent.CLICK, unDock);
stage.nativeWindow.addEventListener(
NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, winMinimized);
SystemTrayIcon(NativeApplication.nativeApplication.icon).menu = myMenu;
}
}
private function winMinimized(displayStateEvent:NativeWindowDisplayStateEvent):void{
if(displayStateEvent.afterDisplayState == NativeWindowDisplayState.MINIMIZED){
displayStateEvent.preventDefault();
dock();
}
}
public function dock():void{
stage.nativeWindow.visible = false;
NativeApplication.nativeApplication.icon.bitmaps = [trayIcon];
}
public function unDock(event:Event):void{
stage.nativeWindow.visible = true;
stage.nativeWindow.orderToFront();
NativeApplication.nativeApplication.icon.bitmaps = [];
}
private function closeApp(event:Event):void{
stage.nativeWindow.close();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
s|WindowedApplication
{
skinClass:ClassReference("spark.skins.spark.SparkChromeWindowedApplicationSkin");
background-color:#999999;
background-alpha:"0.7";
}
</fx:Style>
<s:Label text="Hello AIR"/>
</mx:WindowedApplication>
Many Thanks.
I think you'll manage by calling dock() at the end of readyToTray(event:Event).
To make sure your initialWindow is invisible when it launches you can set it's visible property to false in the application descriptor file.
I have a flex application and a papervision BasicView. I would like to add a new flex UIComponent (such as a button) from within the papervision class. I have posted the full example code below. It works but I would like to be able to accomplish my goal without the "(this.parent.parent as Group).addElement(button);" line.
<!--Application MXML-->
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="50" minHeight="50"
creationComplete="Init()" applicationComplete="Start()">
<fx:Script>
<![CDATA[
import mx.core.UIComponent;
import spark.components.Button;
public var start:QuickStart;
public function Init():void
{
start = new QuickStart();
var uicomp:UIComponent = new UIComponent();
addElement( uicomp );
uicomp.addChild( start );
}
public function Start():void
{
start.GoTime();
}
]]>
</fx:Script>
</s:Application>
//QuickStart.as
package{
import org.papervision3d.view.BasicView;
public class QuickStart extends BasicView
{
public function QuickStart()
{
super(500, 500, true, true);
}
public function GoTime():void
{
var button:Button = new Button;
//this is the offending line
(this.parent.parent as Group).addElement(button);
}
}
}
The version I have does work so please excuse any minor typos.
Logically you would dispatch an event inside your BasicView, listen for it in your main application, and create the button from up there. In a prefect OOP world, every class should be a black box sending events :)
I have a Flex ActionScript Application, I need to draw a simple rectangle in stage. I am using
firstapp.mxml and another Class called Book.as;
here is the complete code which i have done.
firstapp.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Script>
<![CDATA[
import com.books.Book;
import flash.display.*;
var n:Book = new Book;
//n.var1 = "Another String";
addChild(n);
]]>
</mx:Script>
</mx:Application>
Book.as
package com.books
{
import flash.display.*;
public class Book extends Sprite
{
public var var1:String = "Test Var";
public var var2:Number = 1000;
public function Book()
{
var b = new Sprite;
b.graphics.beginFill(0xFF0000, 1);
b.graphics.drawRect(0, 0, 500, 200);
b.graphics.endFill();
addChild(b);
}
}
}
I'm new in Flex, So please help me to fix this. I want to show the rectangle.
Since you're trying to add this to a flex component, you probably need to wrap Book in a UIComponent instance:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:Script>
<![CDATA[
import com.books.Book;
import flash.display.*;
var n:UIComponent = new UIComponent;
n.addChild(new Book);
addChild(n);
]]>
</mx:Script>
</mx:Application>
Another way of doing this would be to instead make Book inherit UIComponent, like so:
package com.books
{
import flash.display.*;
public class Book extends UIComponent
{
public var var1:String = "Test Var";
public var var2:Number = 1000;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
graphics.beginFill(0xff0000, 1);
graphics.drawRect(0, 0, 500, 200);
graphics.endfill();
}
}
}
Then you can add Book directly to your application, like so:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:books="com.books.*"
layout="absolute">
<books:Book />
</mx:Application>
Additionally, I suggest you read up on the Flex component architecture. There's some pretty good documentation from Adobe on the subject, but you should be aware that the information is specific to Flex 3 (I noticed you're using Flex 3, hence the link). Even while a lot of the information may still be applicable to Flex 4 (the component lifecycle for instance) there are differences, especially in terms of skinning.
USE var n:Book = new Book(); instead of var n:Book = new Book;
macke showed you good way. But if you want just to show something quickly, change
addChild(b);
to
rawChildren.addChild(b);
Edit: Some clarifications: application is UIComponent, therefore its addChild method needs UIComponent. You want to add Sprite. This can be done with rawChildren.addChild method from application. rawChildren is useful to get Sprites work with UIComponents, but you have to manage sprites yourself (sizes and layout).
I am having a problem with binding values in my ActionScript components. I basically want to set the value of a a variable in my component to a value in the model, and have the component variable automatically update when the model value is updated. I think that I just don't fully understand how data binding works in Flex - this is not a problem when using MXML components, but, when using ActionScript classes, the binding does not work.
This is the code I'm using, where the values are not binding:
package
{
public class Type1Lists extends TwoLists
{
public function Type1Lists()
{
super();
super.availableEntities = super.composite.availableType1Entities;
super.selectedEntities = super.composite.selectedType1Entities;
}
}
}
package
{
public class Type2Lists extends TwoLists
{
public function Type2Lists()
{
super();
super.availableEntities = super.composite.availableType2Entities;
super.selectedEntities = super.composite.selectedType2Entities;
}
}
}
/* TwoLists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public var __model:ModelLocator = ModelLocator.getInstance();
public var composite:Composite =
__model.selectedComposite;
[Bindable]
public var availableEntities:ArrayCollection;
[Bindable]
public var selectedEntities:ArrayCollection;
]]>
</mx:Script>
<mx:List id="availableEntitiesList" dataProvider="{availableEntities}" />
<mx:List id="selectedEntitiesList" dataProvider="{selectedEntities}" />
</mx:HBox>
To use binding by code you should use mx.binding.utils.*
Take a look and the BindingUtils.bindProperty and bindSetter methods.
Also, be careful with manual databinding, it could lead you to memory leaks.
To avoid them, save the ChangeWatcher returned by bindProperty and bindSetter and call watcher's unwatch method when is no more used (i.e, in the dipose or destructor method)
You need to add the [Bindable] tag either to the class itself (making all properties bindable) or the properties you want to be [Bindable]. Marking properties or objects as [Bindable] in your MXML is not sufficient.
To fix this, I simply converted the classes to MXML components, and added a private variable for my ModelLocator.
/* Type1Lists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<TwoLists xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
availableEntities="{__model.selectedComposite.availableType1Entities}"
selectedEntities="{__model.selectedComposite.selectedType1Entities}">
<mx:Script>
<![CDATA[
import model.ModelLocator;
[Bindable]
private var __model:ModelLocator = ModelLocator.getInstance();
</mx:Script>
</TwoLists>
/* Type2Lists.mxml */
<?xml version="1.0" encoding="utf-8"?>
<TwoLists xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
availableEntities="{__model.selectedComposite.availableType2Entities}"
selectedEntities="{__model.selectedComposite.selectedType2Entities}">
<mx:Script>
<![CDATA[
import model.ModelLocator;
[Bindable]
private var __model:ModelLocator = ModelLocator.getInstance();
</mx:Script>
</TwoLists>