Enrico Foschi’s official blog

Something about me – .NET Team Leader with an entrepreneurial mind

Archive for the ‘ActionScript 3 - AS3’ Category

Funny ActionScript 3 reformatter

with one comment

Well, this is a funny thing that happened today.

While writing Actionscript 3 code, I wrote this line:

return (SpokeRotation + FinalRotation) / 2 * Math.PI / 180;

Well, after pressing the inliner (I really don’t know exactly the name of the feature, but is that button very useful that just inline the code), the line was changed in this:

return SpokeRotation + FinalRotation / 2 * Math.PI / 180;

Whoa!! Isn’t it funny? No parentheses, no party!

Written by Enrico Foschi

February 28, 2008 at 2:49 pm

Left label align with FormItem component (when textAlign attribute isn’t working) – Allineamento a sinistra dell’etichetta del componente FormItem (quando l’attributo textAlign non funziona)

without comments

[ENGLISH]

Today i was working on a simple, stupid stylesheet setup: I wanted my FormItem instance labels to align to the left side.

Unluckely, writing this code:

<mx:FormItem textAlign="left" label="looooooooongLabel">
    <mx:TextInput id="my_txt1" />
</mx:FormItem>
<mx:FormItem textAlign="left" label="shortLabel">
    <mx:TextInput id="my_txt2" />
</mx:FormItem>

the output was this (the label was always right aligned even if I used textAlign css set to “left”):

Flex - Extended Component

So, i tryed to change my CSS, to create my stylenames, but everything was useless.

Googoling around the web, I found a solution to label alignment in FormItem in this page:

http://skovalyov.blogspot.com/2007/01/left-aligned-label-in-formitem.html

So, i created my component (an instance of Skovalyov extended component) with text aligned in the left side.
Unluckily RMGConnecter (the Flex application we are developing) became crazy… something disappeared, something lamped, etc…

The final solution was to write my own extended FormItem component (Extended Form Item). This is the simple code:

package RMGConnect.Form {

    import flash.display.DisplayObject;
    import mx.containers.FormItem;
    import mx.controls.Label;

    public class ExtendedFormItem extends FormItem {

        public var align_left:Boolean = false;

        override protected function updateDisplayList(w : Number, h : Number) : void {
            super.updateDisplayList(w, h);
            if(align_left) {
                var index_label:int = -1;
                var current_label:Label = null;
                if (label.length > 0) {
                    for(var i:Number = 0; i < rawChildren.numChildren; i++) {
                        if(rawChildren.getChildAt(i) is Label) {
                            index_label = i;
                            current_label = Label(rawChildren.getChildAt(i));
                            current_label.x = 0;
                            break;
                        }
                    }
                }
                if (required && current_label) {
                    for(var k:Number = rawChildren.numChildren-1; k >= 0; k--) {
                        if(k != index_label && rawChildren.getChildAt(k) is DisplayObject) {
                            var indicator : DisplayObject = rawChildren.getChildAt(k);
                            indicator.x = current_label.width + ((getStyle("indicatorGap")-indicator.width) / 2);
                            break;
                        }
                    }
                }
            }
        }
    }
}

and, including the container package in the component first tag (it is an .as file in RMGConnectForm directory):

xmlns:rmgext="RMGConnect.Form.*"

i created my left aligned component

<rmgext:ExtendedFormItem align_left="true" label="looooooooongLabel">
    <mx:TextInput id="my_txt1" />
</rmgext:ExtendedFormItem>
<rmgext:ExtendedFormItem align_left="true" label="shortLabel">
    <mx:TextInput id="my_txt2" />
</rmgext:ExtendedFormItem>

obtaining the requested output:

Flex - Extended Component

[ITALIANO]

Oggi mi sono trovato ad affrontare un problema di allineamento delle etichette (label) del componente FormItem.

Sfortunatamente, usando il seguente codice

<mx:FormItem textAlign="left" label="looooooooongLabel">
    <mx:TextInput id="my_txt1" />
</mx:FormItem>
<mx:FormItem textAlign="left" label="shortLabel">
    <mx:TextInput id="my_txt2" />
</mx:FormItem>

l’output era il seguente (le label risultavano sempre allineate a destra nonostante l’attributo di stile textAlign fosse impostato a left):

Flex - Extended Component

Provai così a cambiare i CSS, a creare nuovi styleName, ma tutto risultò inutile.

Googolando nel web, trovai una soluzione proposta dal mitico Skovalyov:

http://skovalyov.blogspot.com/2007/01/left-aligned-label-in-formitem.html

Utilizzando questa classe (che altro non era che un’estensione della classe FormItem), creai il mio componente con la label allineata a sinistra.
Sfortunatamente, RMGConnecter (l’applicativo Flex che stiamo sviluppando) cominciò a comportarsi in maniera alquanto strana… ogni tanto scomparivano scritte, ogni tanto lampeggiavano componenti, etc….

La nuova classe non era proprio la soluzione adatta.

La soluzione finale è stata la ri-scrittura da zero di una nuova estensione del componente FormItem (ExtendedFormItem). Questa soluzione si rivelò più che soddisfacente.

Di seguito vi riporto il codice:

package RMGConnect.Form {

    import flash.display.DisplayObject;
    import mx.containers.FormItem;
    import mx.controls.Label;

    public class ExtendedFormItem extends FormItem {

        public var align_left:Boolean = false;

        override protected function updateDisplayList(w : Number, h : Number) : void {
            super.updateDisplayList(w, h);
            if(align_left) {
                var index_label:int = -1;
                var current_label:Label = null;
                if (label.length > 0) {
                    for(var i:Number = 0; i < rawChildren.numChildren; i++) {
                        if(rawChildren.getChildAt(i) is Label) {
                            index_label = i;
                            current_label = Label(rawChildren.getChildAt(i));
                            current_label.x = 0;
                            break;
                        }
                    }
                }
                if (required && current_label) {
                    for(var k:Number = rawChildren.numChildren-1; k >= 0; k--) {
                        if(k != index_label && rawChildren.getChildAt(k) is DisplayObject) {
                            var indicator : DisplayObject = rawChildren.getChildAt(k);
                            indicator.x = current_label.width + ((getStyle("indicatorGap")-indicator.width) / 2);
                            break;
                        }
                    }
                }
            }
        }
    }
}

Includendo il package contenitore nel primo tag (il componente è un file .as salvato nella directory RMGConnectForm directory):

xmlns:rmgext="RMGConnect.Form.*"

…ho posizionato il mio componente FormItem personalizzato con la label allineata a sinistra

<rmgext:ExtendedFormItem align_left="true" label="looooooooongLabel">
    <mx:TextInput id="my_txt1" />
</rmgext:ExtendedFormItem>
<rmgext:ExtendedFormItem align_left="true" label="shortLabel">
    <mx:TextInput id="my_txt2" />
</rmgext:ExtendedFormItem>

ottenendo l’effetto desiderato inizialmente:

Flex - Extended Component

Written by Enrico Foschi

February 15, 2007 at 8:59 am

Disabling tree nodes in Flex 2

without comments

[ENGLISH]

Well, today i had to disable some tree nodes in my tree view, but i didn’t find how to. In Flex 1 I could directly use the row property in this way:

my_tree.rows[node]._alpha = 20;
my_tree.rows[node].enabled = false;
my_tree.rows[node].onPress = null;

In Flex 2, the best way is to work with XMLListCollection object properties.
So, I have created an enabled property, that has the following value:
- 1: the node is enabled
- 0: the node is disabled

Now, for disabling the node, I make a strange operation. If the node clicked is disabled, I select manually the previous selected node (or nothing if there wasn’t any node selected).
So, I have to declare a current_node_selected variable.

Then, in the click event, I have added the following instruction:

if(tree_group.selectedItem.@enabled != "1") {
   tree_group.selectedItem = current_node_selected;
}
Here is the complete code:
<![CDATA[
	var current_node_selected:Object = null;
	private function treeClick(e:Event):void {
	if(tree_group.selectedItem.@enabled != "1") {
   		 tree_group.selectedItem = current_node_selected;
	} else {
		current_node_selected = tree_group.selectedItem;
		// my actions for enabled nodes
	}
}
]>

<mx:Tree id="tree_group" change="treeClick(event)" />

[ITALIANO]

Buongiorno a tutti. Il problema di oggi era il disabilitare alcuni nodi di un tree component in Flex 2. In Flex 1 non avrei avuto problemi… avrei utilizzato un codice simile al seguente sfruttando la proprietà row:

my_tree.rows[node]._alpha = 20;
my_tree.rows[node].enabled = false;
my_tree.rows[node].onPress = null;

In Flex 2 purtroppo non posso utilizzare la stessa sintassi. La soluzione migliore che ho trovato riguarda l’utilizzo di una proprietà di ogni nodo della sorgente di dati XMLListcollection.
Ho deciso a questo punto di utilizzare la proprietà enabled (inizializzata a 1 quando il nodo è attivo).

Ora, per disabilitare il nodo, eseguo delle strane operazioni. Se il nodo cliccato è disabilitato (enabled non è uguale a 1), seleziono manualmente il nodo precedentemente selezionato (o niente se non ve ne era alcuno). Utilizzo quindi una variabile current_node_selected che memorizza continuamente il nodo appena selezionato.

Al click eseguo queste istruzioni:

if(tree_group.selectedItem.@enabled != "1") {
	tree_group.selectedItem = current_node_selected;
}

Di seguito il codice completo

<![CDATA[

	var current_node_selected:Object = null;
	private function treeClick(e:Event):void {
		if(tree_group.selectedItem.@enabled != "1") {
			tree_group.selectedItem = current_node_selected;
		} else {
			current_node_selected = tree_group.selectedItem;
			// my actions for enabled nodes
		}
	}
]]>
<mx:Tree id="tree_group" change="treeClick(event)" />

Written by Enrico Foschi

February 8, 2007 at 2:11 pm

How to include an XML into a DataGrid using custom columns – come includere un XML all’interno di un DataGrid utilizzando colonne personalizzate

without comments

Screenshot

[ENGLISH]

Today I am working about registered user views in DataGrid and I have to view all of them in a DataGrid. I can get them by an XML generated by a back-end script.

The code i have used is this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas implements="RMGConnect.Services.RMGIService" xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#009900" width="100%" height="100%">
	<mx:Script>
		<![CDATA[
			import RMGConnect.Core.*;
			[Bindable]
			private var user_data:XML = <preferences>
				<item id="1" userid="admin" />
				<item id="2" userid="collaborator" />
				<item id="3" userid="editor" />
				<item id="4" userid="vice-admin" />
				<item id="5" userid="alphatester" />
				<item id="6" userid="betatester" />
				<item id="7" userid="omegatester" />
				<item id="8" userid="dummyuser" />
			</preferences>

			public function startService():void {
				RMGCommon.showDebug(dg_users.columns.toString());
			}
		]]>
	</mx:Script>
	<mx:DataGrid dataProvider="{user_data.item}"  id="dg_users" width="100%" height="100%">
		<mx:columns>
			<mx:DataGridColumn headerText="id" dataField="@id" />
			<mx:DataGridColumn headerText="user-id" dataField="@userid" />
		</mx:columns>
	</mx:DataGrid>
</mx:Canvas>

Binding the XML foo-variables with the DataGrid, automatically i get an XMLListCollection of all “item” nodes. Using mx:columns and mx:DataGridColumn can set header text and source data of every column of my DataGrid.

[ITALIANO]

Oggi sto sviluppando un servizio che consenta di visualizzare una lista completa di tutti gli utenti registrati in un DataGrid. La lista la ottengo da un XML generato in automatico dai servizi back-end. Il codice che ho utilizzato è il seguente:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas implements="RMGConnect.Services.RMGIService" xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#009900" width="100%" height="100%">
	<mx:Script>
		<![CDATA[
			import RMGConnect.Core.*;
			[Bindable]
			private var user_data:XML = <preferences>
				<item id="1" userid="admin" />
				<item id="2" userid="collaborator" />
				<item id="3" userid="editor" />
				<item id="4" userid="vice-admin" />
				<item id="5" userid="alphatester" />
				<item id="6" userid="betatester" />
				<item id="7" userid="omegatester" />
				<item id="8" userid="dummyuser" />
			</preferences>

			public function startService():void {
				RMGCommon.showDebug(dg_users.columns.toString());
			}
		]]>
	</mx:Script>
	<mx:DataGrid dataProvider="{user_data.item}"  id="dg_users" width="100%" height="100%">
		<mx:columns>
			<mx:DataGridColumn headerText="id" dataField="@id" />
			<mx:DataGridColumn headerText="user-id" dataField="@userid" />
		</mx:columns>
	</mx:DataGrid>
</mx:Canvas>

Effettuando un binding tra la variabile XML (contenente dati fasulli) ed il DataGrid, automaticamente ottengo una XMLListCollection di tutti i nodi “item”, che posso gestire comodamente nelle colonne tramite i tag mx:columns e mx:DataGridColumn con gli attributi headerText (titolo della colonna) e dataField (dato da visualizzare).

Written by Enrico Foschi

February 2, 2007 at 10:45 am

getDefinitionByName & AS3 reflection

with 9 comments

[ENGLISH]

Today i solved an AS3 reflection problem: I wanted to create dynamically a class instance, using a string as class name.

So, why have I to make my life so hard?

Obviously, for giving more elegance to RMGConnecter, the RIA we are currently developing. It is intended to be a platform that will include many services. We still don’t know most of these services: we will develop them in the early future. But now, we still don’t know their names.

So, how to build RMGConnecter for creating instances of not-yet-known services (contained in a simple Canvas component)? How to import them?

With reflection all becomes very simply:
- we insert main components of every services in a single directory (for example: RMGConnectService[component name].mxml)
- we will import these component with the following code:

import RMGConnect.Services.*;

- we will use the getDefinitionByName method from package flash.utils for creating a reference to one of these classes. Considering a foo name, “RMGTest”, the code will be:

var myClass:Class = flash.utils.getDefinitionByName("RMGTest");
var myComponent:* = new myClass();
this.addChild(myComponent);

Unluckily, Flex 2 Artificial Intelligence will include in compiled files just the classes that are explicitly used in the code. So, if i will not declare any RMGTest variable, Flex Builder 2 will be sure that i will have not the need of that class and will not include it in swf files.
Executing the code, the debugger will trace a ReferenceError: Error #1065.
This feature was studied just for excluding not-used classes in compiled files (that will surely be more optimized and faster to execute) but it will also exclude from including all classes referenced dinamically through function getDefinitionByName.

Solution

We can solve the problem creating a variable for each type/classes that we want to instanziate with the following code:

var fooRMGTest:RMGTest = null;

Now, Flex Builder 2 will be sure that we will use RMGTest class and it will include it in compiled files withouth giving us any error. Unluckely, we loose our elegance :( .

[ITALIANO]

Oggi mi sono trovato ad affrontare un problema di reflection in ActionScript 3: volevo creare un’istanza di una classe dinamicamente, utilizzando una stringa contenente il nome della classe.

Ma perchè mi voglio complicare così tanto la vita?

Semplice, per una questione di eleganza. RMGConnecter, la RIA che stiamo sviluppando con tanto impegno, è una piattaforma studiata per essere implementata con una grande varietà di servizi.
Questi servizi verranno sviluppati nei prossimi mesi e ad oggi non abbiamo ancora le idee chiare su quali siano, quali funzionalità abbiano, come si possano comportare ma soprattutto non sappiamo come si possano CHIAMARE.

Come fare quindi a predisporre RMGConnecter per creare istanze di servizi che ancora non conosciamo (il cui componente contenitore è un semplice Canvas)? Come fare ad importarle?

Con la reflection tutto questo diventava assai semplice: si inseriscono i principali componenti di ogni servizio in una specifica directory (per esempio: RMGConnectServices[nome componente].mxml), si importano i componenti di tale directory con l’apposita sintassi:

import RMGConnect.Services.*;

e, grazie al metodo getDefinitionByName del package flash.utils, si crea un riferimento alla nostra classe (chiamata “RMGTest” in questo esempio):

var myClass:Class = flash.utils.getDefinitionByName("RMGTest");
var myComponent:* = new myClass();
this.addChild(myComponent);

Purtroppo l’intelligenza artificiale di Flex Builder 2 vincola l’inclusione nei file compilati soltanto alle librerie effettivamente usate nel codice: nell’eseguire il codice il debugger ci restituirà quindi “ReferenceError: Error #1065“.
Pertanto, se non dichiaro alcuna variabile esplicitamente di tipo RMGTest, Flex Builder 2 intuirà che io non avrò bisogno di tale classe ed escluderà in questo modo dal file compilato.
Tale funzionalità, studiata appositamente per non appesantire i file compilati con librerie mai utilizzate, esclude però dalla compilazione tutte quelle classi istanziate dinamicamente tramite la funzione getDefinitionByName.

La soluzione?

Creare una variabile per ogni tipo di servizio che vogliamo istanziare:

var fooRMGTest:RMGTest = null;

Ora Flex Builder 2 sarà sicuro del fatto che noi utilizziamo tale classe, la includerà nel file compilato e non restituirà alcun errore di istanziamento in fase di esecuzione. Purtroppo sarà necessario aggiungere una variabile per ogni tipo di servizio implementato.

Written by Enrico Foschi

January 30, 2007 at 2:36 pm