I am trying to style a Flex 4 GridItem using actionscript, I have tried the following:
<mx:VBox
height="878" width="1920"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:s="library://ns.adobe.com/flex/spark" xmlns:local="*" creationComplete="addStyles()">
<mx:Script>
<![CDATA[
public var selectedLot:String = "";
private function addStyles():void
{
testBorder.setStyle("borderThickness", "3");
}
but I get the following error:
setStyle is not a function.
Am I missing something?
The GridItem is inside a repeater.
Here is my GridItem:
<mx:GridItem id="testBorder" width="101" height="52.25" horizontalAlign="center" verticalAlign="middle" borderStyle="solid" borderColor="gray">
<mx:Image source="assets/ruler-icon.png" />
<s:Label text="{r.currentItem.sqft}" fontSize="10" color="#808080" fontFamily="Helvetica" />
</mx:GridItem>
When using a repeater the GridItem's id will not be the same. To access any item inside a repeater you have to specify an index which is correspondent to the repeated item.
Example: Array consists of ["Audi", "BMW"], we set this array to your repeater's dataProvider, then to access "Audi"'s grid item and set its style, we have to use:
testBorder[0].setStyle("borderThickness", "3");
Additionally, an important point to consider, the VBox "creationComplete" can be executed before the repeater is fully built, therefore, the best place to call your function "addStyles" is in the repeater's "repeatEnd" event i.e (repeatEnd="setTransactionPropertyType()").
Hope this helps,
Goodluck.
Related
I seem to have a problem of items not being created as expected, resulting in event-listeners not being fired.
I am using a DataGrid column of checkboxes as seen here:
<mx:DataGrid id="gridComponents" height="100%">
<mx:columns>
<fwctrl:DataGridCheckBoxColumn id="colChkBox" width="20" selectionChanged="onGridChbBoxChange(event)" />
<mx:DataGridColumn headerText="Components" dataField="name" width="250"/>
</mx:columns>
</mx:DataGrid>
(fwctrl is the namespace of an internal library.)
And here is what the mxml of fwctrl:DataGridCheckBoxColumn looks like:
<mx:DataGridColumn xmlns:mx="http://www.adobe.com/2006/mxml" resizable="false" xmlns:grid="fwctrl.grid.*" xmlns:CheckBoxColumn="fwctrl.grid.CheckBoxColumn.*">
<mx:Metadata>
[Event(name="selectionChanged", type="fwctrl.grid.CheckBoxColumn.DataColumnCheckBoxEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
...
]]>
</mx:Script>
<mx:headerRenderer>
<mx:Component>
<CheckBoxColumn:CheckBoxHeaderRenderer textAlign="center" click="checkboxheaderrenderer1_clickHandler(event)">
<mx:Script>
...
</mx:Script>
</CheckBoxColumn:CheckBoxHeaderRenderer>
</mx:Component>
</mx:headerRenderer>
<mx:itemRenderer>
<mx:Component>
<mx:Box horizontalAlign="center"
verticalAlign="middle" enabled="{outerDocument.isEnabled}">
<mx:Script>
<![CDATA[
...
protected function checkBox_initializeHandler(event:FlexEvent):void
{
trace("init checkbox");
}
]]>
</mx:Script>
<mx:CheckBox id="checkBox" click="onCheckBoxClick()" change="onChange(event)" initialize="checkBox_initializeHandler(event)"/>
</mx:Box>
</mx:Component>
</mx:itemRenderer>
CheckBoxHeaderRenderer is a class that inherits from checkbox.
using the mxml above I bind an ArrayCollection to the data provide by doing: gridComponents.dataProvider = coll;
Having the checkBox_initializeHandler event bound, I expect to see a message printed for each element in my collection.
In reality, the message will only be displayed for element that don't "fall off the grid", i.e. if there is a scroll bar (due to a lot of items being rendered), the items that are below the bottom of the grid (i.e. the ones that need to be scrolled to) will not print a message.
This also means that they are not created correctly and that the click="onCheckBoxClick()" is not hooked correctly and that onCheckBoxClick() does not get triggered.
Why is this happening and how can I fix this?
In Flex, itemRenderers aren't created on a one-to-one basis with your underlying data collection. You only get enough instances to display one screen's worth of data, and those instances get reused as you scroll.
To handle this in your code, refactor so that your click handler in the itemRenderer updates a property in your dataCollection (e.g. data.foo = true;).
working on flex mobile list. list is represented using itemrenderer.
here is the itemrendeerer..
<s:Image id="img" x="30" y="50"/>
<s:Label id="nameLbl" width="100%" height="100%" text="Label" textAlign="center" verticalAlign="middle"/>
<s:Button id="checkMarkLabel" label="+" height="100%" />
on selected::
protected function onClicked(event:MouseEvent):void
{
if(checkMarkLabel.label =="✓")
{
checkMarkLabel.label = "+";
}
else if(checkMarkLabel.label == "+" )
{
checkMarkLabel.label = "✓";
trace("id::"+event.currentTarget.data.id)
//FlexGlobals.topLevelApplication.selectedId = event.currentTarget.data.id;
}
}
the image gives you the clear picture.
after selecting some items in the list,if i scroll the list,the checked items gets unchecked and the unchecked items gets checked.any answer is appreciable..
Flex don't create renderer for each item in list instead Flex create few renderers (in your case 6) and reuse them.
You must save data about clicked list items (maybe use some singleton), and then you must override set data method in renderer class (in this method you get list item for renderer), then you figure out clicked this item or not and set to checkMarkLabel corresponded symbol.
On the list you can set the property "useVirtualLayout" to false. This will generate an unique itemrenderer for each item in the list. This is also what Anton was talking about.
This is however less resource friendly. If your list get's really large it will take up more memory.
You should work with states that are already made use of by lists in combination with itemrenderers.
Example itemrenderer:
<fx:Script>
<![CDATA[
protected function marker_clickHandler(event:MouseEvent):void {
marker.label = marker.label == "V" ? "+" : "V";
}
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
</s:states>
<s:Button label.selected="V" label.normal="+"/>
<s:Button y="10" id="marker" click="marker_clickHandler(event)" label="+"/>
If you were to use this as an itemrenderer, the button utilizing states will update as expected, but like Anton mentioned, the ones marked manually onClick will not get updated as expected.
This answer builds on Robin van den Bogaard's answer.
Assuming your current dataProvider has objects that look like this:
public class MyData {
[Bindable]
private var imgUrl:String;
[Bindable]
private var name:String;
}
Add a new field to it:
[Bindable]
private var chosen:Boolean;
(If you don't have [Bindable], add it to enable data binding on the data items.)
Then your itemRenderer can look like this, automatically updating as you scroll:
<fx:Script>
<![CDATA[
protected function marker_clickHandler(event:MouseEvent):void {
data.chosen = !data.chosen;
}
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
</s:states>
<s:Image id="img" x="30" y="50" source="{data.imgUrl}" />
<s:Label id="nameLbl" width="100%" height="100%" text="Label" textAlign="center" verticalAlign="middle" text="{data.name}" />
<s:Button id="checkMarkLabel" label="{data.chosen ? '✓' : '+'}" height="100%" click="marker_clickHandler(event)" />
I am monitoring my internet connection. When there is no service available I get an Event from my URLMonitor. I am listening to this event and calling a function that opens a SkinnablePopUpContainer. It is a very simple Component I have no listeners attached to it and it is defined only inside the function. When the user clicks the button inside the SkinnablePopUpContainer I close the component and try to destroy it using all possible ways I know of. When I check the Profiler Tool from Flash Builder the SkinnablePopUpContainer is still there. How do I destroy this component freeing the memory it is using.
Here is the listener function:
protected function onNoServiceAvailable(e:*):void
{
var noserviceWindow:NoInternetError = new NoInternetError();
noserviceWindow.open(this,false);
noserviceWindow.x= 0;
noserviceWindow.y= 0;
noserviceWindow.width = SharedObject.getLocal('localObj').data.appMeasures.appWidth;
noserviceWindow.height = SharedObject.getLocal('localObj').data.appMeasures.appHeight+200;
}
and here is my SkinnablePopUpContainer
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:nxTextInput="nx.components.extended.nxTextInput.*"
xmlns:nxButton="nx.components.extended.nxButton.*"
backgroundAlpha="0.4"
horizontalCenter="0" verticalCenter="0" width="100%" height="100%">
<fx:Script>
<![CDATA[
protected function loginButton_clickHandler(event:Event):void
{
loginButton.removeEventListener(MouseEvent.CLICK,loginButton_clickHandler);
this.close();
var ob = this;
ob = null;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Panel title="Fehler"
horizontalCenter="0" verticalCenter="0" color="white">
<s:VGroup horizontalAlign="center" verticalAlign="middle" gap="20"
height="100%" width="100%">
<s:BitmapImage source="#Embed('assets/nxInspect/mobile/assetsCI/redAssets/alert_80x80.png')" id="iconBitmpapDownOnline" verticalCenter="0" />
<s:Label id="serviceFailure" text="Keine internetverbindung." width="90%" styleName="interactable" textAlign="center" color="white"/>
<nxButton:NxButton id="loginButton" label="OK" width="100%" height="100" click="loginButton_clickHandler(event)" styleName="alert"/>
</s:VGroup>
</s:Panel>
First off, the line var ob = this; just creates a reference variable to "this". Setting this variable to null will not make it delete itself. It will just re-reference the variable you just created to null again, so those 2 lines are useless.
Because you've contained your local variable noserviceWindow within the scope of the function onNoServiceAvailable, it should automatically be marked for Garbage Collection when there are no more references to it. If your profiler is recognizing it's existence, then there is probably another reference to it somewhere in your code.
In a Flex mobile app I have 5 Views, handling OAuth logins through 5 different social networks - including Google+ and Facebook. The Views are being selected through the menu shown below:
The filenames are FB.mxml, GG.mxml, MR.mxml, OK.mxml, VK.mxml and their source code looks very similar:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
viewActivate="loadInfo(event)"
viewDeactivate="closeSWV(event)"
title="Facebook.com">
....
private function closeSWV(event:Event=null):void {
_busy.visible = _busy.includeInLayout = false;
_reload.visible = _reload.includeInLayout = true;
stage.removeEventListener(Event.RESIZE, resizeSWV);
if (! _swv)
return;
_swv.removeEventListener(Event.COMPLETE, extractAccessToken);
_swv.removeEventListener(LocationChangeEvent.LOCATION_CHANGE, extractAccessToken);
_swv.removeEventListener(IOErrorEvent.IO_ERROR, closeSWV);
_swv.removeEventListener(ErrorEvent.ERROR, closeSWV);
_swv.dispose();
_swv = null;
}
private function resizeSWV(event:Event=null):void {
if (! _swv)
return;
// align to the right-bottom corner
_swv.viewPort = new Rectangle(stage.stageWidth - Preferans.SCALE * width,
stage.stageHeight - Preferans.SCALE * height,
Preferans.SCALE * width,
Preferans.SCALE * height);
}
....
<s:VGroup>
<s:Label id="_first" fontWeight="bold" text="Your name:" />
<s:Label id="_gender" fontWeight="bold" text="Gender:" />
<s:Label id="_city" fontWeight="bold" text="City:" />
</s:VGroup>
<s:Button id="_play"
label="Play"
click="startGame(event)"
includeInLayout="false"
visible="false" />
</s:View>
My problem is: The 5 mxml files listed above have many similar methods and variables and UI elements and only few different methods and variables.
I've tried to introduce a "base class" for them all several times already and have always given up, because it is not straightforward for mxml files (versus pure AS3 classes).
Does anybody please have an idea, how to approach this?
You can define an abstract class that extends the UI component that you want to use in each row of the list. In your case, it will extend View.
package your.package{
public class SocialAccountView extends View{
// in this class you can add the generic methods
// that you want the view to have
}
}
After that, in the mxml file you can use the class you created as the main type instead of View
<your.package:SocialAccountView
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
viewActivate="loadInfo(event)"
viewDeactivate="closeSWV(event)"
title="Facebook.com">
</your.package:SocialAccountView>
With this you know that all the items in the list are of that type, and have the methods of the parent class. You can even override methods in each view.
Regarding the UI components, the maximum abstraction level (that I am aware of) you can have with them is to build an extra component and use it in all views. Something like...
Labels.mxml
<s:VGroup>
<s:Label id="_first" fontWeight="bold" text="Your name:" />
<s:Label id="_gender" fontWeight="bold" text="Gender:" />
<s:Label id="_city" fontWeight="bold" text="City:" />
</s:VGroup>
and then use it in each component like
<your.package:Labels
id="labelsComponent"/>
Hope this helps.
I created a custom MXML component, TurboContent, that extends the NavigatorContent class:
<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Metadata>
[DefaultProperty("mxmlContentFactory")]
</Fx:Metadata>
<s:Group id="midWrapper">
<s:Button id="btnAdd" />
</s:Group>
<s:Group id="rightWrapper" >
<s:DataGrid id="dgdSelect" >
<s:columns>
<s:ArrayList>
<s:GridColumn headerText="Yo"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<s:Button id="btnRemove" />
<s:Button id="btnClear" />
</s:Group>
</s:NavigatorContent>
I am trying to extend that custom component but when I add display elements to the second extended componet they elements are never seen. For instance: (The first custom component is in the comp package)
<comp:TurboContent 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:comp="comp.*">
<s:Button id="myButton"/>
</comp:TurboContent>
In this case, the 'myButton' component never shows up, but all the elements of the base component do (3 buttons and a datagrid).
I'm curious to understand what you're trying to achieve?
From the pieces of code it seems you're trying to compose a new view which visually inherits the TurboContent-component and adds another button to it.
What happens really under the hood is that the NavigatorContent extends SkinnableContainer. SkinnableContainer has as a default property mxmlContentFactory, which once initialized can not be changed or substituted, or add stuff on top of it, unless you do that with ActionScript:
mxmlContentFactory = null; // or some IDeferredInstance
But then your approach of visual inheritance won't be visual inheritance, but content substitution.
The base class already has initialized it, so the subclasses can't do modifications to it.
From what I can tell, the pattern of defining a component directly in MXML (as you referenced was done in the FIAW series) disallows the ability to then visually insert children in the container's display list. One of the problems is that the mxmlContent (which normally a skin would control) is statically defined in the component and it doesn't seem like one can use contentGroup inside the MXML component directly.
For better control, and what I consider a more strict implementation of the MVC pattern (which Flex 4, as a framework, implements), try separating your visual layout out into an MXML skin, and defining the component in AS3.
From what little I see of your component, I can't really make a judgment as to what properties of the component you want to expose to the container that will instantiate it. I'll at least give an example here of how one can pass info from the component to the skin.
I apologize for the MX components, but I only have Flex 4.1, and I wanted to make sure the program compiled fine. It shouldn't be too hard for you to swap in the spark versions.
Example Component (TurboContentAS.as)
package components {
import mx.controls.DataGrid;
import spark.components.NavigatorContent;
public class TurboContentAS extends NavigatorContent {
public function TurboContentAS() {
super();
}
// Skin Parts that constitute the necessary parts of the component
[SkinPart(required="true")]
public var dgdSelect:DataGrid; //just an example
// property you want to expose to the instantiating object
[Bindable]
public var firstColumnHeader:String = "default header";
}
}
Example Skin (TurboContentSkin.mxml)
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
alpha.disabled="0.5" >
<fx:Metadata>[HostComponent("components.TurboContentAS")]</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<!-- this is where the children that you add later go-->
<s:Group id="contentGroup" left="0" right="0" top="100" bottom="0" minWidth="0" minHeight="0">
<s:layout>
<s:BasicLayout/>
</s:layout>
</s:Group>
<s:Group id="midWrapper">
<s:Button id="btnAdd" label="Add" />
</s:Group>
<s:Group id="rightWrapper" left="200">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<mx:DataGrid id="dgdSelect">
<mx:columns>
<fx:Array>
<!-- This will bind to the publicly exposed property in the component definition -->
<mx:DataGridColumn headerText="{hostComponent.firstColumnHeader}"/>
</fx:Array>
</mx:columns>
</mx:DataGrid>
<s:Button id="btnRemove" label="Remove"/>
<s:Button id="btnClear" label="Clear"/>
</s:Group>
</s:Skin>
Example Instantiation
<components:TurboContentAS skinClass="skins.TurboContentSkin" firstColumnHeader="myHeader">
<s:Button label="myButton"/>
</components:TurboContentAS>