Change the visibility of itemRemderer - apache-flex

I have and add button (last row) in one column of the AdvancedDataGrid.
for the row having the add button the rest of fields are not visible.
On click of the add button a new row is added to the grid for the user to add.
After that this button becomes delete button (label becomes '-' and a new row is added at the bottom for adding another row).
When i click on the delete button (label '-'), the last row gets the add button('+' label) but the fields of the row become visible.
Can somebody please explain
Below is the sample code
<?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="400"
height="300"
initialize="group1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.AdvancedDataGridEvent;
import mx.events.CollectionEvent;
import mx.events.DataGridEvent;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import spark.events.IndexChangeEvent;
protected function group1_initializeHandler(event:FlexEvent):void
{
alarms=new ArrayCollection();
alarms.addItem(initalarmRow());
//alarms.addEventListener(CollectionEvent.COLLECTION_CHANGE, populateFieldDetails);
alarms.addItem(populateFieldforButton());
populateEvents();
}
private var _addButton:Boolean
[Bindable]
public function get addButton():Boolean
{
return _addButton;
}
public function set addButton(value:Boolean):void
{
_addButton=value;
}
private var _alarms:ArrayCollection;
[Bindable]
public function get alarms():ArrayCollection
{
return _alarms;
}
public function set alarms(value:ArrayCollection):void
{
_alarms=value;
}
private var _alarmRow:alarmVO;
[Bindable]
public function get alarmRow():alarmVO
{
return _alarmRow;
}
public function set alarmRow(value:alarmVO):void
{
_alarmRow=value;
}
// Initiliaze an alarmVO for a new row
private function initalarmRow():alarmVO
{
alarmRow=new alarmVO();
alarmRow.buttonLabel='-';
channels=new ArrayCollection;
for (var i:int=0; i < 10; i++)
{
var vo:ChannelVO=new ChannelVO();
vo.id=i;
vo.name="channel_" + i;
vo.messageType="Message_" + i;
channels.addItem(vo);
}
alarmRow.eventName="Event_1";
alarmRow.channel=channels.getItemAt(5) as ChannelVO;
return alarmRow;
}
private var _events:ArrayCollection;
[Bindable]
public function get events():ArrayCollection
{
return _events;
}
public function set events(value:ArrayCollection):void
{
_events=value;
}
private var _channels:ArrayCollection;
[Bindable]
public function get channels():ArrayCollection
{
return _channels;
}
public function set channels(value:ArrayCollection):void
{
_channels=value;
}
public function populateFieldforButton():alarmVO
{
alarmRow=new alarmVO();
alarmRow.buttonLabel="+";
return alarmRow;
}
public function populateEvents():void
{
events=new ArrayCollection();
for (var i:int=0; i < 3; i++)
{
var event:EventVO=new EventVO();
event.id=i;
event.eventName="Event_" + i;
events.addItem(event);
}
}
public function populateFieldDetails(event:Event):void
{
for (var count:int; count < alarms.length; count++)
{
//trace('alarms.getItemAt(count).buttonLabel :' + alarms.getItemAt(count).buttonLabel);
if (alarms.getItemAt(count).buttonLabel == '+')
{
alarms.getItemAt(count).channel=null;
alarms.getItemAt(count).eventName=null;
}
adgdalarmManagement.invalidateDisplayList();
}
}
public function preventEditing(event:AdvancedDataGridEvent):void
{
//check if it is the last row(it should not be editable)
if (event.rowIndex == alarms.length - 1)
{
event.preventDefault();
//trace('**** :' + event.currentTarget);
}
}
public function adgdalarmManagement_creationCompleteHandler(event:FlexEvent):void
{
}
protected function adgdalarmManagement_dataChangeHandler(event:FlexEvent):void
{
// TODO Auto-generated method stub
}
public function ddlEventType_creationCompleteHandler(event:FlexEvent, data:Object):void
{
for (var count:int=0; count < alarms.length; count++)
{
for (var count1:int=0; count1 < events.length; count1++)
{
if (events.getItemAt(count1).eventName == alarms.getItemAt(count).eventName)
{
event.currentTarget.selectedIndex=count1;
break;
}
}
}
checkEventTypeVisible(event, data);
}
public function checkEventTypeVisible(event:FlexEvent, data:Object):void
{
if (data == '-')
{
event.currentTarget.visible=true;
}
else
{
event.currentTarget.visible=false;
}
}
public function button1_clickHandler(event:MouseEvent):void
{
if (event.currentTarget.label == '-')
{
event.currentTarget.parent.parent.parent.parent.dataProvider.removeItemAt(event.currentTarget.parent.parent.parent.parent.selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.refresh();
adgdalarmManagement.validateNow();
}
else
{
var selectedIndex:int=event.currentTarget.parent.parent.parent.parent.selectedIndex;
alarmRow=new alarmVO();
alarmRow.buttonLabel='-';
alarmRow.eventName="";
alarmRow.channel=new ChannelVO();
event.currentTarget.parent.parent.parent.parent.dataProvider.removeItemAt(event.currentTarget.parent.parent.parent.parent.selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.addItemAt(alarmRow, selectedIndex);
event.currentTarget.parent.parent.parent.parent.dataProvider.addItemAt(populateFieldforButton(), selectedIndex + 1);
}
}
public function ddlChannel_changeHandler(event:IndexChangeEvent):void
{
event.target.parent.data.typeDisplay=event.target.selectedItem.name;
event.target.parent.data.messageTypeDisplay=event.target.selectedItem.messageType;
}
public function ddlChannel_creationCompleteHandler(event:FlexEvent, data:Object):void
{
// TODO Auto-generated method stub
if (channels != null)
{
if (alarms != null)
for (var count:int=0; count < alarms.length; count++)
{
for (var count1:int=0; count1 < channels.length; count1++)
{
if (alarms.getItemAt(count).channel != null)
{
if (channels.getItemAt(count1).name == alarms.getItemAt(count).channel.name)
{
event.currentTarget.selectedIndex=count1;
break;
}
}
else
{
event.currentTarget.selectedIndex=0;
}
}
}
}
else
{
event.currentTarget.selectedIndex=0;
}
checkVisible(event, data);
}
public function checkVisible(event:FlexEvent, data:Object):void
{
if (data == '-')
{
event.currentTarget.visible=true;
}
else
{
event.currentTarget.visible=false;
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup paddingTop="10"
paddingBottom="10"
paddingLeft="10"
paddingRight="10"
width="100%"
height="100%"
id="vbxChannelManagement">
<!-- Added for Rounding off Corners of GRID-->
<mx:ApplicationControlBar width="100%"
cornerRadius="8"
height="100%">
<mx:AdvancedDataGrid id="adgdalarmManagement"
width="100%"
height="100%"
dataProvider="{alarms}"
dataChange="adgdalarmManagement_dataChangeHandler(event)"
creationComplete="adgdalarmManagement_creationCompleteHandler(event)">
<mx:columns>
<mx:AdvancedDataGridColumn id="adgcAdRemove"
width="30"
dataField="buttonLabel">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<s:Button label="{data.buttonLabel}"
width="30"
click="outerDocument.button1_clickHandler(event)"/>
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcEvent"
dataField="buttonLabel"
headerText="Event">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<s:HGroup id="eventGroup"
visible="{(this.parent.data.buttonLabel=='-')?true:false}}">
<s:DropDownList id="ddlEventType"
dataProvider="{outerDocument.events}"
creationComplete="outerDocument.ddlEventType_creationCompleteHandler(event,data.buttonLabel)"
labelField="eventName"/>
</s:HGroup>
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcChannel"
dataField="buttonLabel"
headerText="Channel">
<mx:itemRenderer>
<fx:Component>
<s:MXAdvancedDataGridItemRenderer>
<!--<s:HGroup width="100%"
id="channelField"
>-->
<s:DropDownList id="ddlChannel"
width="100%"
dataProvider="{outerDocument.channels}"
creationComplete="outerDocument.ddlChannel_creationCompleteHandler(event,data.buttonLabel)"
labelField="id"
change="outerDocument.ddlChannel_changeHandler(event)"
/>
<!--</s:HGroup>-->
</s:MXAdvancedDataGridItemRenderer>
</fx:Component>
</mx:itemRenderer>
</mx:AdvancedDataGridColumn>
<mx:AdvancedDataGridColumn id="adgcChannelType"
headerText="Type"
dataField="typeDisplay"/>
<mx:AdvancedDataGridColumn id="adgcMessageType"
headerText="Message Type"
dataField="messageTypeDisplay"/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:ApplicationControlBar>
</s:VGroup>
</s:Group>

I got it working by overriding commitproperties function and in that function checking the viisbility of the dropdown.
something like
override public function commitproperties():void{
super.commitporperties();
if(data.buttonLabel == '-')
this.visible = false;
}

Related

Combobox NoClose when embedded Checkbox is clicked [FLEX]

I've reached the point where I have got a dropdown in which is embedded a tree. Each node has got a checkbox.
The idea is to be able to navigate and tick the checkboxes without having the dropdown closing.
Can't make this dropdown to remain open after a checkbox has been clicked!
The event that closes the dropdown occurs when the xml is updated in PermissionTreeItemRendererV2.as > handleChkClick(evt) > this.itemXml.#checked = {"0" or "1"};
Any idea how to tweak the code to disable this annoying event?
sample_combobox.mxml:
<?xml version="1.0"?>
<mx:Application xmlns:local="local.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="loadXML()">
<mx:Script>
<![CDATA[
import mx.collections.XMLListCollection;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.mxml.HTTPService;
public var xmlService:HTTPService = new HTTPService();
[Bindable]
public var xmlResult:XML;
[Bindable]
public var xmlList:XMLList;
[Bindable]
public var xmlTeams:XMLListCollection;
public function loadXML():void
{
xmlService.url = "mlb.xml"
xmlService.resultFormat = "e4x";
xmlService.addEventListener(ResultEvent.RESULT, resultHandler);
xmlService.send();
}
public function resultHandler(event:ResultEvent):void
{
xmlResult = XML(event.result);
xmlList = xmlResult.league;
xmlTeams = new XMLListCollection(xmlList);
}
]]>
</mx:Script>
<local:TreeComboBox
width="300"
id="combo"
labelField="#label" dataProvider="{xmlTeams}" />
</mx:Application>
TreeCombobox.mxml:
<?xml version="1.0" encoding="utf-8"?>
<local:ComboBoxNoClose xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="local.*">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _label:String;
[Bindable]
public var treeSelectedItem:Object;
public function updateLabel(event:*):void
{
_label = event.currentTarget.selectedItem[this.labelField];
treeSelectedItem = event.currentTarget.selectedItem;
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(dropdown && _label != null){
text = "";//_label;
}
}
]]>
</mx:Script>
<local:dropdownFactory>
<mx:Component>
<mx:Tree change="outerDocument.updateLabel(event)" height="500"
width="500"
itemRenderer="local.PermissionsTreeItemRendererV2"
folderClosedIcon="{null}"
folderOpenIcon="{null}"
defaultLeafIcon="{null}" />
</mx:Component>
</local:dropdownFactory>
</local:ComboBoxNoClose>
PermissionTreeItemRendererV2.as
// ActionScript file
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.collections.ListCollectionView;
import mx.controls.CheckBox;
import mx.controls.treeClasses.TreeItemRenderer;
import mx.controls.treeClasses.TreeListData;
public class PermissionsTreeItemRendererV2 extends TreeItemRenderer{
public var chk:CheckBox;
public var itemXml:XML;
public function PermissionsTreeItemRendererV2(){
super();
mouseEnabled = false;
}
override public function set data(value:Object):void{
if(value != null){
super.data = value;
this.itemXml = XML(value);
if(this.itemXml.#checked == "1"){
this.chk.selected = true;
}else{
this.chk.selected = false;
}
}
}
override protected function createChildren():void{
super.createChildren();
chk = new CheckBox();
chk.addEventListener(MouseEvent.CLICK, handleChkClick);
addChild(chk);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
if(super.data){
var tld:TreeListData = TreeListData(super.listData);
//In some cases you only want a checkbox to appear if an
//item is a leaf
//if so, then keep the following block uncommented,
//otherwise you can comment it out to display the checkbox
//for branch nodes
if(tld.hasChildren){
this.chk.visible = true;
}else{
//You HAVE to have the else case to set visible to true
//even though you'd think the default would be visible
//it's an issue with itemrenderers...
this.chk.visible = true;
}
if(chk.visible){
//if the checkbox is visible then
//reposition the controls to make room for checkbox
this.chk.x = super.label.x
super.label.x = this.chk.x + 17;
this.chk.y = super.label.y+8;
}
}
}
private function handleChkClick(evt:MouseEvent):void
{
if(this.chk.selected)
{
this.itemXml.#checked = "1";
}
else
{
this.itemXml.#checked = "0";
}
}
}
}
ComboboxNoClose.as:
package local
{
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.controls.ComboBox;
import mx.events.DropdownEvent;
import mx.events.ListEvent;
public class ComboBoxNoClose extends ComboBox
{
public function ComboBoxNoClose()
{
super();
}
public function onOpen(event:Event):void
{
event.stopImmediatePropagation();
}
public override function close(trigger:Event = null):void
{
if (trigger != null)
{
super.close();
}
}
}
}
mlb.xml that populates the tree:
<?xml version="1.0" encoding="utf-8"?>
<root>
<league label="American League">
<division label="West">
<team label="Los Angeles" />
<team label="Seattle" />
<team label="Oakland" />
<team label="Texas" />
</division>
<division label="Central">
<team label="Cleveland" />
<team label="Detroit" />
<team label="Minnesota" />
<team label="Chicago" />
<team label="Kansas City" />
</division>
<division label="East">
<team label="Boston" />
<team label="New York" />
<team label="Toronto" />
<team label="Baltimore" />
<team label="Tampa Bay" />
</division>
</league>
</root>
Default behavior of popup is that, when you click on dropdown it closes it self. You can solve this problem like this
<mx:PopUpButton id="popup" width="100%" label="{label}" close="popup_closeHandler(event)" open="popup_openHandler(event)" openAlways="true">
<mx:popUp>
<mx:VBox width="{popup.width*1.25}" mouseEnabled="false" verticalGap="1">
<mx:List id="listSelectAll" width="100%" rowCount="1" selectable="true" itemClick="listSelectAll_itemClickHandler(event)">
<mx:dataProvider>
<mx:Array>
<mx:Object id="selectAll" selected="" label="All"/>
</mx:Array>
</mx:dataProvider>
<mx:itemRenderer>
<mx:Component>
<mx:HBox width="100%" height="100%" mouseChildren="false">
<mx:CheckBox selected="{data.selected}" label="{data.label}" width="100%"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:List id="listItems" width="100%" dataProvider="{_dataProvider}" itemClick="listItems_itemClickHandler(event)" variableRowHeight="true">
<mx:itemRenderer>
<mx:Component>
<!--
<mx:HBox width="100%" height="100%" mouseChildren="false" verticalAlign="middle">
<mx:CheckBox selected="{data[outerDocument.selectedField]}" label="{data[outerDocument.labelField]}" width="100%"/>
</mx:HBox>
-->
<mx:HBox width="100%" mouseChildren="false" verticalAlign="middle" horizontalAlign="left" paddingLeft="4">
<mx:Script>
<![CDATA[
override public function set data(value: Object) : void {
super.data = value;
if(data.iconCache == null || outerDocument.cacheIcon == false) {
imgIcon.source = data[outerDocument.iconField];
} else {
imgIcon.source = new Bitmap(data.iconCache);
}
}
protected function image_ioErrorHandler(event:IOErrorEvent):void {
imgIcon.visible = imgIcon.includeInLayout = false;
}
protected function imgIcon_completeHandler(event:Event):void {
imgIcon.visible = imgIcon.includeInLayout = true;
if(outerDocument.cacheIcon) {
var bitmapData:BitmapData = Bitmap(imgIcon.content).bitmapData;
//var bitmap:Bitmap = new Bitmap(bitmapData);
data.iconCache = bitmapData;
//imgIcon.removeEventListener(Event.COMPLETE, imgIcon_completeHandler);
}
}
]]>
</mx:Script>
<mx:CheckBox id="chkSelected" selected="{data[outerDocument.selectedField]}"/>
<mx:Image id="imgIcon" width="{outerDocument.iconWidth}" height="{outerDocument.iconHeight}" visible="{data[outerDocument.iconField]}" includeInLayout="{data[outerDocument.iconField]}" complete="imgIcon_completeHandler(event)" ioError="image_ioErrorHandler(event)"/>
<mx:Label id="lblText" text="{data[outerDocument.labelField]}"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:VBox>
</mx:popUp>
</mx:PopUpButton>
or you can visit my post for a complete implementation. here

Spark List in Flex mobile app: Pull down to refresh - with test code and screenshot

Trying to implement "pull down to refresh" I've created the following simple test code (please just add to a new Flash Builder project, with "blank" template, i.e. without navbar):
Screenshot:
TestPull.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"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.PropertyChangeEvent;
private static const PADDING:uint = 20;
[Bindable]
private var _ac:ArrayCollection = new ArrayCollection();
private function init():void {
updateList();
_list.scroller.viewport.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleScroll);
}
private function updateList():void {
_ac.source = new Array();
for (var i:int = 0; i < 42; i++) {
_ac.source.push(Math.random());
}
_ac.refresh();
}
private function handleScroll(e:PropertyChangeEvent):void {
if (e.source == e.target && e.property == "verticalScrollPosition") {
trace(e.property, ': ', e.oldValue, ' -> ', e.newValue);
if (e.newValue < -2 * PADDING &&
e.oldValue >= -2 * PADDING) {
_hint.visible = true;
setTimeout(hideHint, 2000);
//updateList();
}
}
}
private function hideHint():void {
_hint.visible = false;
}
]]>
</fx:Script>
<s:List id="_list"
dataProvider="{_ac}"
width="100%"
height="100%" />
<s:Label id="_hint"
text="Pull down to refresh..."
width="100%"
textAlign="center"
fontStyle="italic"
backgroundColor="#FFFFCC"
paddingTop="{PADDING}"
paddingBottom="{PADDING}"
visible="false" />
</s:Application>
This seems to work well and the _hint visibility is being toggled just once per pull (I've verified this with a trace).
However when I uncomment the updateList() call above (simulating data fetch from a web server) - everything breaks, the hint.visible=true is being set again and again and the _list is flickering.
Does anybody please have a suggestion, how to fix my poor man's pull to refresh?
I've ended up with this solution based on Michaël CHAIZE blog entry:
TestPull.mxml (add to a new Flex Mobile project):
<?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"
applicationComplete="init()">
<fx:Declarations>
<s:ArrayCollection id="_ac"/>
<s:Fade id="_fadeIn" duration="500" alphaFrom="0" alphaTo="1" />
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
private static const PADDING:uint = 20;
private function init():void {
updateList();
_list.dataGroup.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleScroll);
}
private function handleScroll(event:PropertyChangeEvent):void {
if (_busy.visible ||
event.source != event.target ||
event.property != 'verticalScrollPosition') {
return;
}
if (event.newValue < -3 * PADDING &&
event.oldValue >= -3 * PADDING) {
_hintDown.visible = true;
_hintUp.visible = false;
_fadeIn.play([_hintDown]);
} else if (event.newValue < -6 * PADDING &&
event.oldValue >= -6 * PADDING) {
_hintDown.visible = false;
_hintUp.visible = true;
_fadeIn.play([_hintUp]);
} else if (event.newValue >= -6 * PADDING &&
event.oldValue < -6 * PADDING) {
_hintDown.visible = true;
_hintUp.visible = false;
_fadeIn.play([_hintDown]);
} else if (event.newValue >= -3 * PADDING &&
event.oldValue < -3 * PADDING) {
_hintDown.visible = false;
_hintUp.visible = false;
}
}
private function startLoading(event:MouseEvent):void {
if (_hintUp.visible) {
_busy.includeInLayout = _busy.visible = true;
setTimeout(updateList, 5000);
}
_hintDown.visible = false;
_hintUp.visible = false;
}
private function updateList():void {
_ac.source = new Array();
for (var i:int = 0; i < 42; i++) {
_ac.source.push(Math.random());
}
_ac.refresh();
_busy.includeInLayout = _busy.visible = false;
}
]]>
</fx:Script>
<s:VGroup width="100%">
<s:HGroup id="_busy"
verticalAlign="baseline"
includeInLayout="false"
visible="false">
<s:BusyIndicator />
<s:Label text="Loading data..." />
</s:HGroup>
<s:List id="_list"
width="100%"
contentBackgroundColor="#FFFFFF"
dataProvider="{_ac}"
mouseUp="startLoading(event)" />
</s:VGroup>
<s:Label id="_hintDown"
text="↓ Pull down to refresh... ↓"
width="100%"
textAlign="center"
paddingTop="{PADDING}"
visible="false" />
<s:Label id="_hintUp"
text="↑ Release to refresh... ↑"
width="100%"
textAlign="center"
paddingTop="{PADDING}"
visible="false" />
</s:Application>

How is an mx:Canvas measured when inside a renderer in Flex 3?

I'm having a sizing issue with a canvases located inside an HBox. It seems "_graphic", "_border" and "_fill" canvases (in com.example.ThingRenderer.mxml) do not get measured at the same time as all the other measurements inside the renderer. However, this problem is only observed on the first pass-through. Refer to the images for a visual... 1st image shows the state of the app after it finished loading. 2nd image represents what the screen looks like after the Populate button is clicked. 3rd image shows what happens when the stepper is incremented. The question is how come the drawing in the 3rd image doesn't get rendered once the data is populated into the table?
RendererTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="handleCreationComplete(event)"
>
<mx:Script>
<![CDATA[
import com.example.Thing;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.NumericStepperEvent;
private const _thingProvider:ArrayCollection = new ArrayCollection();
private var _thing1:Thing;
protected function handleCreationComplete(event:FlexEvent):void {
_thing1 = new Thing("thingy", 0xff0000, 0.3);
_stepper.value = _thing1.ratio;
}
protected function handlePopulateClick(event:MouseEvent):void {
_thingProvider.addItem(_thing1);
}
protected function handleStepperChange(event:NumericStepperEvent):void {
_thing1.ratio = event.value;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="Populate" click="handlePopulateClick(event)" />
<mx:NumericStepper id="_stepper" minimum="0" maximum="1" stepSize="0.01" change="handleStepperChange(event)" />
<mx:AdvancedDataGrid dataProvider="{_thingProvider}" variableRowHeight="true" width="100%" height="100%">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="Name" dataField="name" />
<mx:AdvancedDataGridColumn headerText="Display"
width="150" sortable="false"
itemRenderer="com.example.ThingRenderer"
/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:VBox>
</mx:Application>
com.exampleThingRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
>
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
private var _thing:Thing;
private var _ratioWatcher:ChangeWatcher;
private var _doClearContent:Boolean;
private var _doDrawBorder:Boolean;
private var _doUpdateFill:Boolean;
override public function set data(value:Object):void {
if(value && value is Thing) {
_thing = Thing(value);
if(_ratioWatcher) {
_ratioWatcher.unwatch();
}
_ratioWatcher = ChangeWatcher.watch(_thing, "ratio", handleRatioChanged);
_doClearContent = false;
_doDrawBorder = true;
_doUpdateFill = true;
_graphic.invalidateSize();
_border.invalidateSize();
}
else {
_doClearContent = true;
_doDrawBorder = false;
_doUpdateFill = false;
}
super.data = value;
}
private function handleRatioChanged(event:Event):void {
_doUpdateFill = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
if(_doClearContent) {
_container.visible = false;
_container.includeInLayout = false;
_doClearContent = false;
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(_doDrawBorder) {
trace("_thingContainer.width="+_container.width, "_thingGraphic.width="+_graphic.width, "_thingBorder.width="+_border.width);
_border.graphics.clear();
_border.graphics.moveTo(0, 0);
_border.graphics.lineStyle(1, _thing.color);
_border.graphics.lineTo(_border.width, 0);
_border.graphics.lineTo(_border.width, _border.height);
_border.graphics.lineTo(0, _border.height);
_border.graphics.lineTo(0, 0);
_doDrawBorder = false;
}
if(_doUpdateFill) {
_percentage.text = Math.round(_thing.ratio * 100.0) + "%";
_fill.graphics.clear();
_fill.graphics.beginFill(_thing.color);
_fill.graphics.drawRect(0, 0, _fill.width * _thing.ratio, _fill.height);
_doUpdateFill = false;
}
}
]]>
</mx:Script>
<mx:HBox id="_container" width="100%" paddingLeft="5" paddingTop="5" paddingRight="5" paddingBottom="5">
<mx:Label id="_percentage" width="45" />
<mx:Canvas id="_graphic" width="100%" height="15">
<mx:Canvas id="_border" x="0" y="0" width="100%" height="100%" />
<mx:Canvas id="_fill" x="0" y="0" width="100%" height="100%" />
</mx:Canvas>
</mx:HBox>
</mx:Canvas>
com.example.Thing.as
package com.example {
public class Thing {
[Bindable] public var name:String;
[Bindable] public var color:uint;
[Bindable] public var ratio:Number;
public function Thing(name:String, color:uint, ratio:Number) {
this.name = name;
this.color = color;
this.ratio = ratio;
}
}
}
All this happens because you can't use width and height properties in updateDisplayList, they are not updated yet. Make separate component (e.g. ThingProgressBar) and put drawing logick inside it, that will solve everything:
package
{
import mx.core.UIComponent;
public class ThingProgressBar extends UIComponent
{
private var _ratio:Number;
public function get ratio():Number
{
return _ratio;
}
public function set ratio(value:Number):void
{
_ratio = value;
invalidateDisplayList();
}
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
if (unscaledWidth > 0 && unscaledHeight > 0)
{
graphics.lineStyle(1, 0xFF0000);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, unscaledWidth * ratio, unscaledHeight);
graphics.endFill();
}
}
}
}
So your renderer might look like this:
<mx:HBox
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
horizontalScrollPolicy="off" verticalScrollPolicy="off" xmlns:local="*"
>
<fx:Script>
<![CDATA[
[Bindable] private var _thing:Thing;
override public function set data(value:Object):void
{
_thing = value as Thing;
super.data = value;
}
]]>
</fx:Script>
<mx:HBox width="100%"
paddingLeft="5" paddingTop="5"
paddingRight="5" paddingBottom="5">
<mx:Label text="{_thing.name}" width="45" />
<local:ThingProgressBar width="100%" height="15"
ratio="{_thing.ratio}"/>
</mx:HBox>
</mx:HBox>
I removed watcher. Binding by watcher is considered a bad practice, use mxml binding or events instead.
I removed two Canvases with separated border and fill - they can be cobined together.
I used UIComponent instead of Canvas. Don't use containers unless you need layout, they are heavy.
I used HBox instead of Canvas in renderer because I like boxes more :) But you can't avoid using second container in renderer if you need custom styles since List overwrites renderer's stylesheet.

Passing multiple data items through checkbox

I have 2 checkbox repeaters: The first gets populated w/ an ArrayCollection & the second gets populated w/ the "Label" and "Data" fields selected from the first checkbox....how do I pass another item to the second checkbox? In the code below, I am currently only passing the "key" and "val" fields & would like to pass "another" field.
<?xml version="1.0" encoding="utf-8"?>
<mx: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"
creationComplete="updateOlder();"
>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.core.Repeater;
[Bindable]
public var older:ArrayCollection = new ArrayCollection ([]);
[Bindable]
public var newer:ArrayCollection = new ArrayCollection ([]);
private function updateOlder():void{
older.addItem({key:'item1',val:{ name:'desc1' ,another:'another1'}});
older.addItem({key:'item2',val:{ name:'desc2' ,another:'another2'}});
older.addItem({key:'item3',val:{ name:'desc3' ,another:'another3'}});
}
private function updateNewer(evt:MouseEvent):void {
var tmpBox:CheckBox = evt.target as CheckBox;
if(tmpBox.selected) {
// add item to array
newer.addItem({key:tmpBox.label, val:tmpBox.data.val, another:tmpBox.data.another});
}
else {
// remove item from array
var newArrIndex: int = getArrayElementIndex(newer, older[tmpBox.instanceIndex]);
if(newArrIndex != -1){
newer.removeItemAt(newArrIndex);
}
}
newer.refresh();
}
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;
}
]]>
</fx:Script>
<mx:HBox width="100%" height="100%">
<mx:VBox width="100%" height="100%">
<mx:Repeater id="oldRepeater" dataProvider="{older}">
<mx:CheckBox color="black" fontFamily="Arial" fontSize="14"
label="{oldRepeater.currentItem.key}"
data="{oldRepeater.currentItem.val}"
click="updateNewer(event);"
/>
</mx:Repeater>
</mx:VBox>
<mx:VBox width="100%" height="100%">
<mx:Repeater id="newRepeater" dataProvider="{newer}">
<mx:CheckBox color="red" fontFamily="Arial" fontSize="14"
label="{newRepeater.currentItem.key}"
data="{newRepeater.currentItem.val}"
/>
</mx:Repeater>
</mx:VBox>
</mx:HBox>
</mx:Application>
I'm not sure what exactly you want to accomplish, but it seems to me the easiest way to go is to make the Object you assign to the "val" field on each of the ArrayCollections contain both variables, i.e.
older.addItem({ key:'item1', val:{ name:'desc1' ,another:'another1'} });
and retrieve those values later:
private function getArrayElementIndex(arr:ArrayCollection, elementValue:Object):int{
for (var retInd: int = 0; retInd < arr.length; retInd++) {
if (arr[retInd]['key'] == elementValue['key'] && arr[retInd]['val']['name'] == elementValue['val']['name']
&& arr[retInd]['val']['another'] == elementValue['val']['another']) {
return retInd;
}
}
return -1;

Bindable combobox - selected item/index

I am trying to bind a datagrid item to a combox, so that when the user selects the item in the datagrid the form displays the selected data for editing, however one of my items is a combobox using a dataprovider.
I would like the item selected in the datagrid to match the selected item in the combobox, this part is fine however if my datagrid item is null then I cannot get the combox to set the selected index to -1?
(the same happens if you use the CRUD wizard in Flex builder 3 for ColdFusion)
I am using the following code for my custom combobox:
<mx:ComboBox
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
creationComplete="componentInit()"
>
<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;
import mx.controls.Alert;
[Bindable]
public var valueField:String = "";
[Bindable]
public var labelFields:Array = [];
public function componentInit():void {
this.labelFunction = renderLabelFunction;
}
public function renderLabelFunction(item:Object):String {
var result:String = "";
if (labelFields.length == 0) {
if (labelField != null) {
return item[labelField];
} else {
return item.toString();
}
}
else {
for(var i:int=0; i < labelFields.length; i++) {
if (i > 0) {
result += " ";
}
result += item[labelFields[i]];
}
}
return result;
}
override public function set selectedItem(val:Object):void {
//Alert.show(valueField +":" +ObjectUtil.toString(val));
if( this.valueField != null) {
for(var i:int=0; i < this.dataProvider.source.length; i++) {
var item:Object = this.dataProvider.source[i];
if ( item[valueField]== val ) {
// if it matches, make it selected.
this.selectedIndex = i;
break;
}
}
} else {
this.selectedIndex = -1;
}
}
public function get selectedItemValue():Object {
if( this.valueField != null && selectedItem != null) {
return selectedItem[valueField];
} else {
return text;
}
}
]]>
</mx:Script>
</mx:ComboBox>
and the MXML part calling the combox is:-
<mx:DataGrid id="clientDatagrid" selectedIndex="1" visible="true"/>
<mx:Form height="305">
<mx:FormItem direction="horizontal" label="Surname" required="true" visible="true" width="100%" horizontalAlign="left">
<mx:TextInput enabled="true" id="Surname" text="{clientDatagrid.selectedItem.Surname}" width="100%" visible="true"/>
</mx:FormItem>
<mx:FormItem direction="horizontal" label="Forename" required="true" visible="true" width="100%" horizontalAlign="left">
<mx:TextInput enabled="true" id="Forename" text="{clientDatagrid.selectedItem.Forename}" width="100%" visible="true"/>
</mx:FormItem>
<components:BindableComboBoxa id="gender"
dataProvider="{genderData}"
valueField="Code"
labelField="Description"
/>
</mx:form>
Any help would be much appreciated.
Thank you.
In selectedItem setter, testing this.valueField for nullity is useless because you set it to "Code" in the mxml. Instead you should test if val is null.
So just replace
if( this.valueField != null)
with
if( val != null)
and then it should work.
try setting a prompt for the combobox like this:
<components:BindableComboBoxa id="gender"
dataProvider="{genderData}"
valueField="Code"
labelField="Description"
prompt="Please Select"
/>

Resources