Flex mxml custom component - how to add uicomponents? - apache-flex

how to I pass in a uicomponent to a mxml custom component?
ex: I want to pass in any number of buttons, and I want my custom component to lay them out in a certain order.
MainApp.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" xmlns:local="*"
>
<local:myComp >
<s:Button label='Button1' />
<s:Button label='Button2' />
<!--I want to add anything else here -->
</local:myComp>
myComp.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()"
width="400" height="300"
>
<fx:Script>
<![CDATA[
private function init():void {
// how do I access the added components and place them where I want?
// do I use removeChildren and AddChildren?
// addItemsHere.addchild(nextButton);
]]>
</fx:Script>
<s:HGroup id='addItemsHere' />

As your component extends Group, you shall use addElement instead of addChild (and for all other methods with 'child' in their name it shall be replaced with 'element'. So, access to all elements will be like that:
for(var i:int =0; i < numElements; i++){
var button:Button = Button(getElementAt(i));
doWhatIWantWithMyButton(button);
}
It is also better to override createChildren method of your component if you know what to add at the creation moment.
If you don't need very specific button placement, you can set layout property of your component to any desired layout (like VerticalLayout, for example), and those layouts are tunable.

You seem to be trying to recreate functionality that already exists in the SDK. This is wat SkinnableContainer is for.
Depending on your use case, there are two ways to use it:
You only need to add some custom graphic elements to you custom component, but no additional behaviour
In this scenario, you would simple reuse SkinnableContainer and assign it a custom skin, like so:
<s:SkinnableContainer skinClass="MySkin">
<s:Button label='Button1' />
<s:Button label='Button2' />
</s:SkinnableContainer>
With MySkin.mxml perhaps something like:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>
[HostComponent("spark.components.SkinnableContainer")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:Label text="I'm a SkinnableContainer"/>
<s:Group id="contentGroup" width="100%" height="100%">
</s:SkinnableContainer>
Your Buttons will now automatically be added to the contentGroup; the SkinnableContainer class handles this for you. Note that this Group must have exactly that id; it's a required SkinPart.
You want to add some behaviour to you component
The procedure is the same, but you would now subclass SkinnableContainer (this is usually done in pure ActionScript), write some behaviour in there, and assign the skin to an instance of this class.
public class MyComp extends SkinnableContainer {
//additional behaviour goes here
}
Usage:
<local:MyComp skinClass="MySkin">
<s:Button label='Button1' />
<s:Button label='Button2' />
</local:MyComp>

Related

How to access MXML component instance in ItemRenderer

I have been developing an Adobe Flex (v3.5 Flex SDK) based application and I have a question on How we can access (call) a method written in MXML file (embeded in script tag) from the ItemRenderer file.
The MXML component has a datagrid and for one of the columns, the itemrenderer is my own custom item renderer.
In my custom item renderer, for some event I need to call a method which exist in its parent MXML component. How do we get access to its parent MXML instance ?
I have explored for this in google and found that we can access to 'data' object which refers to the dataProvider of the datagrid. But I wanted access to the instance of MXML component (so that I can call a method in it) which has the datagrid.
The AdvancedDataGridColumn in AdvancedDataGrid is like this
<mx:AdvancedDataGridColumn dataField="total" headerText="Total" width="120" itemRenderer="renderers.MyItemRenderer"/>
Here MyItemRenderer is a separate action script file.
Appreciate the response.
Thanks
Raagu
As Raghavendra Nilekani suggested This works:
TestGrid.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">
<fx:Script>
<![CDATA[
[Bindable]
public var data:Array = [
{name:"name",value:"valeu1",timestamp:"423423"},
{name:"name1",value:"valeu2",timestamp:"423423"},
{name:"name2",value:"valeu3",timestamp:"423423"},
{name:"name3",value:"valeu5",timestamp:"423423"}
]
public function calculateValue():Number{
return Math.random();
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<mx:VBox height="100%" width="100%">
<mx:AdvancedDataGrid dataProvider="{data}">
<mx:columns>
<mx:AdvancedDataGridColumn itemRenderer="ItemRenderer">
</mx:AdvancedDataGridColumn>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</s:Application>
e ItemRendere.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true"
addedToStage="mxadvanceddatagriditemrenderer1_addedToStageHandler(event)"
>
<fx:Script>
<![CDATA[
[Bindable]
var value:Number;
import mx.containers.VBox;
import mx.controls.AdvancedDataGrid;
protected function mxadvanceddatagriditemrenderer1_addedToStageHandler(event:Event):void
{
var grid:AdvancedDataGrid = ((AdvancedDataGrid)(this.owner));
var box:VBox = ((VBox)(grid.owner))
var comp:TestGrid = (TestGrid)(box.owner);
value = comp.calculateValue();
}
]]>
</fx:Script>
<s:Label id="lblData" top="0" left="0" right="0" bottom="0" text="{value}" />
</s:MXAdvancedDataGridItemRenderer>
Anyway I agree with zenbeni that this lead to a not reausable item renderer.

flex 4 air app open modules in a new modal window

I am new to flex and I am trying to open modules in a new window modal.
I have managed to load and add a module in a container ( vbox ) but what I really need is to open them in separate windows.
All posts that I could find are samples on how to load modules but nothing on how to show them.
I found a post here
Modules and Panels issue
and it looks by the screenshot exactly what I am looking for.
Thanks for your help.
I found a way of doing this.
Thanks for all the pointers
here is how I did it for all of you to see and maybe use. If you have suggestions to make it better, do not hesitate to comment.
I am using a component : collapsabletitlewindow
mainapp.mxml
<?xml version="1.0" encoding="utf-8"?>
<s: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"
xmlns:components="de.aggro.components.*">
<fx:Script>
<![CDATA[
import de.aggro.components.*;
private function createWindow():void{
var w:window = new window;
w.title = "Window " + container.numChildren;
container.addChild(w);
}
]]>
</fx:Script>
<components:CollapsableTitleWindowContainer id="container" width="100%" height="100%" >
</components:CollapsableTitleWindowContainer>
<s:Button buttonDown="createWindow()" label="Create Window" />
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:WindowedApplication>
window.mxml
<?xml version="1.0" encoding="utf-8"?>
<components:CollapsableTitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:components="de.aggro.components.*" layout="absolute" width="400" height="300"
creationComplete="initApp()" >
<fx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.ModuleManager;
import mx.modules.IModuleInfo;
import mx.core.IVisualElement;
public var info:IModuleInfo;
private function initApp():void {
info = ModuleManager.getModule("mod.swf");
info.addEventListener(ModuleEvent.READY, modEventHandler);
/* Load the module into memory. Calling load() makes the
IFlexModuleFactory available. You can then get an
instance of the class using the factory's create()
method. */
info.load(null, null, null, moduleFactory);
}
/* Add an instance of the module's class to the display list. */
private function modEventHandler(e:ModuleEvent):void {
this.addElement(info.factory.create() as IVisualElement);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</components:CollapsableTitleWindow>
mod.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Module 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="400" height="300">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:LinkButton x="131" y="124" label="Module link"/>
</s:Module>

Blurring and disabling background while reconnecting Flex application to backend

My Flex 4.5 application has many users from Russia and Ukraine with poor internet connections and Socket connections often interrupt and have to be reconnected.
Currently I set currentState to "offline" on IOErrorEvent.IO_ERROR and Event.CLOSE events and display just 1 component in that state:
<mx:ProgressBar indeterminate="true"
horizontalCenter="0" verticalCenter="0"
label="Reconnecting..." labelPlacement="center"
includeIn="offline" />
but this is not the best way - because the users are suddenly presented by the white screen and the progress bar, while the background disappears.
(Actually it is not an application, but a card game - so the users could at least study their cards while being reconnected).
So I wonder, if there is a way to blur and disable background in Flex - similar to mx.controls.Alert, but without an OK-button and being dismissable when the Socket connection is restored?
UPDATE:
I've used a PopUpManager as suggested by Chris, but the indeterminate ProgressBar is not animated for some reason. How can I "kick start" it?
MyApp.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"
width="700" height="525"
backgroundColor="#CCFFCC"
initialize="systemManager.stage.scaleMode=StageScaleMode.SHOW_ALL"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import mx.controls.ProgressBar;
private function init():void {
var bar:Connecting = PopUpManager.createPopUp(this, Connecting, true) as Connecting;
PopUpManager.centerPopUp(bar);
}
private function fullScreen(event:MouseEvent):void {
stage.displayState =
stage.displayState == StageDisplayState.NORMAL ?
StageDisplayState.FULL_SCREEN :
StageDisplayState.NORMAL;
}
]]>
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="connected" />
</s:states>
<s:CheckBox right="10" bottom="10"
label="Full screen"
click="fullScreen(event)" />
</s:Application>
Loading.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:ProgressBar
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
indeterminate="true" fontWeight="normal"
label="Connecting..." labelPlacement="center">
</mx:ProgressBar>
UPDATE 2:
Solved that by embedding ProgressBar in a Group
When the connection is lost, set the enabled property of your Application (or the top level component you want to blur) to false and back to true when the connection is reestablished.
'Application' defines a skin state disabled which automatically becomes the currentState when the components 'enabled' property is set to 'false'. This means you can create a skin like this:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" >
<fx:Metadata>[HostComponent("spark.components.Application")]</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:Group id="contentGroup" left="0" right="0" top="0" bottom="0" />
<s:filters.disabled>
<s:BlurFilter />
</s:filters.disabled>
</s:Skin>
This will include the 'BlurFilter' only in the 'disabled' state. And setting the 'enabled' property to 'false' will automatically block all user interaction with the component.
When you are using Alert what is really happening is that a popup component is displayed on top of your app. You can achieve the same effect using the PopUpManager to blur the background while displaying a small message to the user(maybe a custom component using Canvas).

Custom component dataGrid selectionMode as a property

I have a custom component of which have a advancedDataGrid inside it. I want this component to be reusable so need is set the datagid selectionMode as a component property.
In mxml i want set property like this:
<comp:MyComp itemDataGridSelectionMode="singleCell" .../>
Inside MyComp actionScript i have a metatag like this:
[Inspectable(enumeration="singleRow, multipleRows, singleCell, multipleCells", defaultValue="singleRow")]
public var itemDataGridSelectionMode:String;
How do i bind this itemDataGridSelectionMode variable to advancedDatagrid selectionMode?
UPDATE: Here is a small test application fully working code:
<!--MyComp.mxml-->
<?xml version="1.0" encoding="utf-8"?>
<s:Group 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="638" height="500">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
[Inspectable(enumeration="singleRow, multipleRows, singleCell, multipleCells", defaultValue="singleRow")]
public function set itemsSelectionMode(value:String):void
{
this.adgItems.selectionMode = value;
}
public function get itemsSelectionMode():String
{
return this.adgItems.selectionMode;
}
]]>
</fx:Script>
<mx:AdvancedDataGrid id="adgItems" designViewDataType="flat" width="100%" height="100%">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Column 1" dataField="col1"/>
<mx:AdvancedDataGridColumn headerText="Column 2" dataField="col2"/>
<mx:AdvancedDataGridColumn headerText="Column 3" dataField="col3"/>
</mx:columns>
</mx:AdvancedDataGrid>
</s:Group>
<!-- 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="955" minHeight="600" xmlns:comp="*">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<comp:MyComponent x="272" y="86" itemsSelectionMode="singleCell"/>
</s:Application>
Error: Invalid value: multipleRows. It must be one of singleRow, multipleRows, singleCell, multipleCells.
Where you have your public var within your custom component, do this:
[Inspectable(enumeration="singleRow, multipleRows, singleCell, multipleCells", defaultValue="singleRow")]
public function set itemDataGridSelectionMode(value:String):void
{
advancedDatagrid.selectionMode = value;
}
public function get itemDataGridSelectionMode():String
{
return advancedDatagrid.selectionMode;
}
I guess you can set the itemDataGridSelectionMode as [Bindable] and then can bind it with the selectionMode property of the AdvancedDataGrid.
One way is:
BindingUtils.bindProperty(datagridId, 'selectionMode', this, itemDataGridSelectionMode);
OR Use a setter method instead of variable definition.

Navigation within ItemRenderer

How can we navigate within an itemRenderer?
For example, in Views we use the View.navigator (ViewNavigator) to push and pop views, there is no such feature in ItemRenderer.
Navigation within a View (Easy)
<s:View>
<s:HGroup >
<s:Button label="Questionnaire" click="navigator.pushView(view.QuestionnaireCategory1View)"/>
</s:HGroup>
Navigation within a Item Renderer (Impossible?)
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="true" height="56">
<s:HGroup>
<s:Button text="Button" click="?????????"/>
</s:HGroup>
</s:ItemRenderer>
You want to use bubbling events to catch when the user interacts with an item renderer.
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:HGroup>
<s:Button text="Button" click="dispatchEvent(new Event('buttonClicked', true));"/>
</s:HGroup>
</s:ItemRenderer>
Then when do this with whatever is using your item renderer:
<DataGroup id="group" itemRenderer="YourItemRenderer" dataProvider="{someData}" creationComplete="group.addEventListener('buttonClick', someHandlerFunction);" />
And then within your handler function, do whatever action you wanted to do. In this case, I'm adding the event listener on creation complete of the DataGroup, but you can add it to the creation complete event of the main container. This way you keep your item renderer decoupled and reusable, as well as using proper software practices (data in, events out).
In when you create your itemRenderer
<comp:MyItemRenderer navigator="{navigator}"/>
In your itemRenderer (here call MyItemRenderer)
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
autoDrawBackground="true" height="56">
<fx:Script>
<![CDATA[
import spark.components.ViewNavigator;
private var _navigator:ViewNavigator;
public function set navigator(value:ViewNavigator):void
{
_navigator = value;
}
]]>
</fx:Script>
<s:HGroup>
<s:Button label="Button" click="{_navigator.pushView(view.QuestionnaireCategory1View)}"/>
</s:HGroup>

Resources