I want to create a custom check box which toggles between label and image - apache-flex

I want to create a custom reusable component by extending a spark Button class so that it has a checkbox and a label which says Show Image. When checkbox is selected an image will be displayed instead of the label. The Image path should be exposed as an API. How can we extend spark.components.Button to have it check box with labe or image (image path should be dynamic).
I tried to extend Button class as below but not sure how to create check box in it and how to pass image path as parameter to that.
package myClasses
{
import spark.components.Button;
public class ImageCheckBox extends Button
{
public function ImageButton()
{
super();
this.buttonMode = true;
}
}
}
I want to use the custom components something like below in application.
<myClasses:ImageCheckBox skinClass="mySkins.HelpButtonSkin" path="...."" label="Show Image" />

Something like this
package myClasses {
import flash.events.Event;
import spark.components.CheckBox;
import spark.components.Image;
import spark.components.Label;
import spark.components.supportClasses.SkinnableComponent;
public class ImageCheckBox extends SkinnableComponent {
[SkinPart(required=true)]
public var checkBox:CheckBox;
[SkinPart(required=true)]
public var labelComp:Label;
[SkinPart(required=true)]
public var image:Image;
private var pathChanged:Boolean = false;
private var _path:String;
public function get path():String {
return _path;
}
public function set path(value:String):void {
if (_path != value) {
_path = value;
pathChanged = true;
invalidateProperties();
}
}
private var labelChanged:Boolean = false;
private var _label:String;
public function get label():String {
return _label;
}
public function set label(value:String):void {
if (_label != value) {
_label = value;
labelChanged = true;
invalidateProperties();
}
}
public function ImageCheckBox() {
super();
setStyle("skinClass", ImageCheckBoxSkin);
}
override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (instance == checkBox) {
checkBox.addEventListener(Event.CHANGE, checkBoxChangeHandler)
}
else if (instance == labelComp) {
labelComp.text = label;
}
else if (instance == image) {
image.source = path;
}
}
override protected function partRemoved(partName:String, instance:Object):void {
super.partRemoved(partName, instance);
if (instance == checkBox) {
checkBox.removeEventListener(Event.CHANGE, checkBoxChangeHandler)
}
}
override protected function getCurrentSkinState():String {
return checkBox.selected ? "selected" : super.getCurrentSkinState();
}
override protected function commitProperties():void {
if (labelChanged) {
labelChanged = false;
labelComp.text = label;
}
if (pathChanged) {
pathChanged = false;
image.source = path;
}
super.commitProperties();
}
private function checkBoxChangeHandler(event:Event):void {
invalidateSkinState();
}
}}
And skin
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
xmlns:mx="library://ns.adobe.com/flex/mx">
<!-- host component -->
<fx:Metadata>
<![CDATA[
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("myClasses.ImageCheckBox")]
]]>
</fx:Metadata>
<s:states>
<s:State name="normal"/>
<s:State name="selected"/>
</s:states>
<s:HGroup width="100%" height="100%" gap="3">
<s:CheckBox id="checkBox"
verticalCenter="0"
height="100%"/>
<s:Label id="labelComp"
verticalCenter="0" verticalAlign="middle"
width="100%" height="100%"
visible.normal="true" includeInLayout.normal="true"
visible.selected="false" includeInLayout.selected="false"/>
<s:Image id="image"
verticalCenter="0"
width="100%" height="100%"
fillMode="scale" scaleMode="letterbox"
visible.normal="false" includeInLayout.normal="false"
visible.selected="true" includeInLayout.selected="true"/>
</s:HGroup>

Related

Labels are not shown for HeaderRenderer

I want my datagrid header to be rendered with date labels. I am using following code for the same. However my lables are not visible when I run actual application. Can anyone please suggest?
import mx.controls.Label;
public class HeaderRenderer extends Label
{
private var lbl:Label;
private const LEFT_PADDING:int = 12;
public function HeaderRenderer()
{
super();
lbl = new Label();
lbl.visible=true;
lbl.text="Label";
}
override protected function updateDisplayList(w:Number, h:Number):void
{
lbl.x = LEFT_PADDING;
}
I am accessing it from datagrid column as below.
<mx:columns>
<mx:DataGridColumn dataField="title" headerText="Task" width="{taskHeaderWidth}"/>
<mx:DataGridColumn itemRenderer="{ganttItemEditor}"
headerRenderer="{new ClassFactory(HeaderRenderer)}"
rendererIsEditor="true"
/>
</mx:columns>
Hope below code may help u: -
package
{
import mx.controls.listClasses.MXItemRenderer;
import spark.components.Label;
public class HeaderRenderer extends MXItemRenderer
{
private const LEFT_PADDING:int = 12;
private var lbl:Label;
public function HeaderRenderer()
{
super();
lbl = new Label();
lbl.visible=true;
lbl.text="Label";
this.addElement(lbl);
}
override protected function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w, h);
lbl.x = LEFT_PADDING;
}
}
}

Trim Whitespace from AsyncListView of a RSS Feed

This is some Flex code from a Flex mobile project. I want to load Rss Feed into an IList which has errorneous white space. So far i know of Stringutil.trim(String). But im stuck where I should use it to make the list work. Please advise.
<?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"
xmlns:studierende="services.studierende.*"
xmlns:allgemein="services.allgemein.*"
xmlns:veranstaltung="services.veranstaltung.*"
xmlns:wissenschaft="services.wissenschaft.*"
overlayControls="false">
<fx:Script>
<![CDATA[
import flash.net.dns.AAAARecord;
import mx.events.FlexEvent;
import mx.utils.StringUtil;
protected function openmenu(event:MouseEvent):void
{}
protected function reload(event:MouseEvent):void
{
asynclist.removeAll();
getDataResult.token = feed1.getData();
}
protected function loadRSS(event:FlexEvent):void
{
getDataResult.token = feed1.getData();
//I guess here is a good place to trim the whitespaces. But how ?
}
protected function loadfeed(event:MouseEvent,rid:Number):void
{
var rssid:Number = rid;
switch(rssid)
{
case 0:
asynclist.removeAll();
getDataResult.token = feed1.getData();
break;
case 1:
asynclist.removeAll();
getDataResult.token = feed2.getData();
break;
case 2:
asynclist.removeAll();
getDataResult.token = feed3.getData();
break;
case 3:
asynclist.removeAll();
getDataResult.token = feed4.getData();
break;
}
}
]]>
</fx:Script>
<fx:Declarations>
<s:CallResponder id="getDataResult"/>
<allgemein:Allgemein id="feed1"/>
<studierende:Studierende id="feed2"/>
<veranstaltung:Veranstaltung id="feed3"/>
<wissenschaft:Wissenschaft id="feed4"/>
</fx:Declarations>
<s:navigationContent>
<s:Button id="menubtn" label="Menu" click="openmenu(event)"/>
</s:navigationContent>
<s:actionContent>
<s:Button id="reloadbtn" label="RELOAD" click="reload(event)"/>
</s:actionContent>
<s:List id="list" left="10" right="10" top="50" bottom="10" creationComplete="loadRSS(event)"
itemRenderer="icItemRender" labelField="title">
<s:AsyncListView id="asynclist" list="{getDataResult.lastResult}" />
</s:List>
<s:HGroup x="10" y="10" width="748" height="32">
<s:Button height="32" label="Allgemein" fontSize="12" click="loadfeed(event,0)"/>
<s:Button height="32" label="Studierende" fontSize="12" click="loadfeed(event,1)"/>
<s:Button height="32" label="Veranstaltung" fontSize="12" click="loadfeed(event,2)"/>
<s:Button height="32" label="Wissenschaft" fontSize="12" click="loadfeed(event,3)"/>
</s:HGroup>
I'm assuming you want to trim the whitespace from the title that is displayed in each element of the list.
The easiest way is probably to use a labelFunction. Set it on your list, like this:
<s:List id="list" left="10" right="10" top="50" bottom="10" creationComplete="loadRSS(event)"
itemRenderer="icItemRender" labelFunction="myLabelFunction">
Noticed, I removed your labelField declaration. The function should be something like this:
<fx:Script>
<![CDATA[
private function myLabelFunction(item:Object):String {
return Stringutil.trim(item.title);
}
]]>
</fx:Script>
You could also create a custom itemRenderer to achieve the same end result.
I solved things with help of flextras via a custom item renderer
package
{
import com.adobe.fiber.styles.Style;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.StageWebView;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import mx.utils.StringUtil;
import spark.components.Button;
import spark.components.IconItemRenderer;
import spark.components.Image;
import spark.components.LabelItemRenderer;
import spark.components.supportClasses.StyleableTextField;
/**
*
* ASDoc comments for this item renderer class
*
*/
public class asListItemRenderer extends IconItemRenderer
{
private var titleDisplay:StyleableTextField;
private var msgDisplay:StyleableTextField;
private var link:String;
private var readon:Image;
private var stagewebview:StageWebView;
private var warning:AlertPopUp;
public function asListItemRenderer()
{
//TODO: implement function
super();
super.labelField = "title";
super.messageField = "description";
}
/**
* #private
*
* Override this setter to respond to data changes
*/
override public function set data(value:Object):void
{
/*super.data = value;*/
titleDisplay.text = value.title;
msgDisplay.text = trimMessage(value);
link = value.link;
// the data has changed. push these changes down in to the
// subcomponents here
}
/**
* #private
*
* Override this method to create children for your item renderer
*/
override protected function createChildren():void
{
titleDisplay = createDataField(false, false, "bold");
addChild(titleDisplay);
msgDisplay = createDataField(true, true);
addChild(msgDisplay);
readon = new Image();
readon.source="assets/readmore_32px.png";
readon.width=32;
readon.height=32;
addChild(readon);
readon.addEventListener(MouseEvent.CLICK,openlink);
// super.addEventListener(MouseEvent.CLICK,openlink);
// create any additional children for your item renderer here
}
private function openlink(e:Event):void
{
/*if(StageWebView.isSupported==true){
stagewebview=new StageWebView();
stagewebview.viewPort=new Rectangle(0,0,stage.width,stage.height);
stagewebview.stage=this.stage;
stagewebview.loadURL(link);
}else{
warning = new AlertPopUp();
warning.open(this);
}*/
}
/**
* #private
*
* Override this method to change how the item renderer
* sizes itself. For performance reasons, do not call
* super.measure() unless you need to.
*/
override protected function measure():void
{
//super.measure();
// measure all the subcomponents here and set measuredWidth, measuredHeight,
// measuredMinWidth, and measuredMinHeight
measuredHeight = measuredMinHeight = 120;
measuredWidth = measuredMinWidth = unscaledWidth;
}
/**
* #private
*
* Override this method to change how the background is drawn for
* item renderer. For performance reasons, do not call
* super.drawBackground() if you do not need to.
*/
override protected function drawBackground(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
// do any drawing for the background of the item renderer here
}
/**
* #private
*
* Override this method to change how the background is drawn for this
* item renderer. For performance reasons, do not call
* super.layoutContents() if you do not need to.
*/
private function createDataField(multiline:Boolean = false, wordWrap:Boolean = false, style:String = "normal"):StyleableTextField
{
var labelField:StyleableTextField = new StyleableTextField();
labelField.styleName = this;
labelField.editable = false;
labelField.selectable = false;
labelField.multiline = multiline;
labelField.wordWrap = wordWrap;
labelField.setStyle("fontSize", 14);
labelField.setStyle("color", 0x333333);
labelField.setStyle("fontWeight", style);
return labelField;
}
override protected function layoutContents(unscaledWidth:Number,
unscaledHeight:Number):void
{
setElementPosition(titleDisplay, 10, 10);
setElementSize(titleDisplay, unscaledWidth, measuredMinHeight);
setElementPosition(msgDisplay, 10, titleDisplay.y + 30);
setElementSize(msgDisplay, unscaledWidth-50, measuredHeight);
setElementPosition(readon,unscaledWidth-32,44);
setElementSize(readon,32,32);
// super.layoutContents(unscaledWidth, unscaledHeight);
// layout all the subcomponents here
}
private function trimMessage(item:Object):String {
return StringUtil.trim(item.description);
}
}
}

Stop List From Scrolling

I have a datagrid that's using a custom itemrender with a button in it. I have a problem whereby when I click on a button the datagrid list scrolls its self. How can I prevent this behaviour?
Extended datagrid
<mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml"
doubleClickEnabled="false"
alternatingItemColors="[#ffffff, #f1f1f1]"
mouseDown="mdhandler(event)"
sortableColumns="true"
xmlns:datagrids="components.content.contents.datagrids.*"
creationComplete="_creationcomplete(event)">
<mx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import components.preview.PreviewPlayEvent;
import mx.controls.Button;
import components.preview.PreviewPlay;
import components.content.contents.FixedHelp.FixedHelp;
import mx.events.FlexEvent;
import mx.core.Application;
import mx.events.ToolTipEvent;
import flash.utils.describeType;
import mx.utils.ObjectUtil;
import components.remix.PadDisplay.PadContent;
import components.remix.PadDisplay.PalletteCode;
import mx.core.IUIComponent;
import mx.controls.Image;
import mx.managers.DragManager;
import mx.core.DragSource;
import mx.events.DragEvent;
// Embed icon image.
[Embed(source='assets/proxy.png')]
public var dragImg:Class;
private var _scrollEnabled:Boolean=true;
private var _currentpath:String="";
public static const TYPE_ARRAY:Array=["loop", "one-shot sample", "stem", "track"]
private function _creationcomplete(e:FlexEvent=null):void
{
addEventListener(Event.ENTER_FRAME, enterframe, false, 0, true);
addEventListener(PreviewPlayEvent.PLAY, previewplayrequest, false, 0, true);
addEventListener(PreviewPlayEvent.STOP,previewstoprequest,false,0,true)
}
private function previewplayrequest(e:PreviewPlayEvent):void
{
_currentpath=e._path;
//Application.application.previewplay(e._path)
invalidateList();
}
private function previewstoprequest(e:PreviewPlayEvent):void
{
_currentpath="";
//Application.application.previewplay(e._path)
invalidateList();
}
public function get critera():String
{
return _currentpath;
}
public function set criteria(value:String):void
{
_currentpath=value;
}
private function enterframe(e:Event):void
{
if (Application.application.hashelp == false)
{
}
else
{
removeEventListener(Event.ENTER_FRAME, enterframe);
var xml:XML=Application.application.helpxml;
var fh:FixedHelp=Application.application.fixedhelp;
var helptext:String=Application.application.helpxml.studio.loops.text;
fh.addItem(helptext, this);
}
}
private function mdhandler(e:MouseEvent):void
{
describeType(e.currentTarget)
e.currentTarget.toString()
if (e.currentTarget.selectedItem != null && getQualifiedClassName(e.target) != "mx.controls::Button")
{
_scrollEnabled=false
var dragdata:XML=XML(e.currentTarget.selectedItem) as XML
var ds:DragSource=new DragSource()
var dragdataasObject:Object=ObjectUtil.copy(e.currentTarget.selectedItem)
ds.addData(dragdataasObject, PadContent.LOOP_FORMAT);
//trace(ds.dataForFormat(PadContent.LOOP_FORMAT)) // I expect xml data to trace out here but it does not
var imageproxy:Image=new Image();
imageproxy.source=dragImg;
imageproxy.height=40;
imageproxy.width=40;
DragManager.doDrag(this, ds, e, imageproxy, (this.x - e.stageX) + 40, (this.y - e.stageY) + 130)
this.selectedItem=null;
}
else
{
e.stopImmediatePropagation();
}
}
private function userLabelFunction(item:Object, column:DataGridColumn):String
{
var newstr:String=new String(item.t + "\n" + TYPE_ARRAY[item.ty])
return newstr;
}
private function catFunction(item:Object, column:DataGridColumn):String
{
var newstr:String=TYPE_ARRAY[item.ty];
return newstr;
}
private function bpmFunction(item:Object, column:DataGridColumn):int
{
var newstr:Number=new Number(item.bp);
return newstr;
}
private function beatCountFunction(item:Object, column:DataGridColumn):int
{
var newstr:int=new int(item.bc);
return newstr;
}
private function sortbpm(obj1:Object, obj2:Object):int
{
var valueA:Number=obj1.bp;
var valueB:Number=obj2.bp;
return ObjectUtil.numericCompare(valueA, valueB);
}
private function sortbeatcount(obj1:Object, obj2:Object):int
{
var valueA:Number=obj1.bc;
var valueB:Number=obj2.bc;
return ObjectUtil.numericCompare(valueA, valueB);
}
public function get scrollEnabled():Boolean
{
return _scrollEnabled;
}
public function set scrollEnabled(value:Boolean):void
{
_scrollEnabled=value;
}
override protected function dragScroll():void
{
if (_scrollEnabled)
{
super.dragScroll();
}
}
]]>
</mx:Script>
<mx:columns>
<mx:DataGridColumn dataField="r"
headerText=""
itemRenderer="components.content.contents.datagrids.ImageRendererLoops"/>
<mx:DataGridColumn dataField="t"
headerText="Title"
labelFunction="userLabelFunction"/>
<mx:DataGridColumn dataField="bc"
headerText="Beats"
labelFunction="beatCountFunction"
sortCompareFunction="sortbeatcount"/>
<mx:DataGridColumn dataField="BPM"
headerText="BPM"
labelFunction="bpmFunction"
sortCompareFunction="sortbpm"/>
<mx:DataGridColumn itemRenderer="components.preview.PreviewPlay"/>
</mx:columns>
</mx:DataGrid>
Item Renderer
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
implements="mx.controls.listClasses.IDropInListItemRenderer">
<mx:Script>
<![CDATA[
import mx.controls.DataGrid;
import components.content.contents.datagrids.LoopMastersDataGrid;
import components.remix.PadDisplay.PadContent;
import mx.controls.listClasses.BaseListData;
[Embed(source='assets/play_20.png')]
[Bindable]
public var play20Img:Class;
public static const PLAYING:String="playing";
public static const LOADING:String="loading";
public static const IDLE:String="idle";
private var _listData:BaseListData;
private var _bpm:int;
private var _path:String;
private var _state:String="idle";
public function get listData():BaseListData
{
return _listData;
}
public function set listData(value:BaseListData):void
{
_listData=value;
var criteria:String = (_listData.owner as LoopMastersDataGrid).critera;
if(this._path==criteria)
{
// this track is playing
previewbutton.styleName="stopPreviewButtonStyle";
}else
{
// this track is not playing
previewbutton.styleName="playPreviewButtonStyle";
}
//trace("list data crit=="+criteria);
}
override public function set data(value:Object):void
{
super.data=value;
this._path=new String(PadContent.LOOP_ROOT + value.r + "/" + value.u + ".rocudo");
this._bpm=value.bp;
//trace(this._path);
}
private function sendPreviewRequest(event:Event):void
{
switch(this._state)
{
case(PreviewPlay.IDLE):
(listData.owner as LoopMastersDataGrid).dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.PLAY,this._bpm,this._path));
break;
case(PreviewPlay.LOADING):
// preview play probably shouldnt care about loading
break;
case(PreviewPlay.PLAYING):
(listData.owner as LoopMastersDataGrid).dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.STOP,this._bpm,this._path));
break;
}
//dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.PLAY,this._bpm,this._path));
}
/** override protected function commitProperties():void
{
super.commitProperties();
this._state=PreviewPlay.IDLE;
}**/
]]>
</mx:Script>
<mx:Button id="previewbutton"
width="20"
height="20" click="sendPreviewRequest(event)" styleName="playPreviewButtonStyle">
</mx:Button>
</mx:HBox>
Update
I removed my mouse down handler from the datagrid and removed the cusom component. Clicking on a item in the datagrid makes the datagrid scroll so that the item selected appears first in the list. This is the behaviour I need to stop.

Flex 4: Accessing public method in main application from component

I need to be able to call a method from a component located under the main application in Flex 4. Can anyone tell me please how to do this without using FlexGlobals please?
Sample code is attached. Thanks in advance.
// TestApp.mxml (application)
<?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"
creationComplete="initApp()">
<fx:Script>
<![CDATA[
import com.TestComp;
import mx.managers.PopUpManager;
public function myMethod():void
{
// do something
}
protected function initApp():void
{
var popUp:TestComp = new TestComp();
PopUpManager.addPopUp(popUp, this, true);
}
]]>
</fx:Script>
</s:WindowedApplication>
// TestComp.mxml (component)
<?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">
<fx:Script>
<![CDATA[
private function doSomething(event:MouseEvent):void
{
// call to myMethod() in TestApp.mxml
}
]]>
</fx:Script>
<s:Button click="doSomething(event)" label="Click Me"/>
</s:Group>
This is bad design. You should provide a callback function or an event listener.
// TestComp.mxml
<mx:Metadata>
[Event(name="doSomethingEvent", type="flash.events.Event")]
</mx:Metadata>
<mx:Script><![CDATA[
private function doSomething(event:MouseEvent):void
{
this.dispatchEvent(new Event("doSomethingEvent"));
}
]]></mx:Script>
// TestApp.mxml
protected function initApp():void
{
var popUp:TestComp = new TestComp();
popUp.addEventListener("doSomethingEvent", myMethod);
PopUpManager.addPopUp(popUp, this, true);
}
private function myMethod(event: Event): void
{
// do something
}
And this is a callback example:
// TestComp.mxml
public var doSomethingCallback: Function;
private function doSomething(event:MouseEvent):void
{
doSomethingCallback.call();
}
// TestApp.mxml
protected function initApp():void
{
var popUp:TestComp = new TestComp();
popUp.doSomethingCallback = myMethod;
PopUpManager.addPopUp(popUp, this, true);
}
private function myMethod(): void
{
// do something
}
Easiest option?
Take out the click handler from the button in TestComp.
In your main app, add a listener to TestComp (if it's a direct child of the main application) or itself (if TestComp is further down the display list) for MouseEvent.CLICK. In the handler, test to see if the event's target is the TestComp either through == if you've got a direct reference, or through "is" if not.
That's the least amount of effort from what you have just now, still relies on (bubbling) events, and is more "correct"
I do agree with splash that it's bad design, but the following should work
//in TestApp
protected function initApp():void
{
var popUp:TestComp = new TestComp(this);
PopUpManager.addPopUp(popUp, this, true);
}
//in TestComp
private var app:TestApp;
public function TestComp(app:TestApp)
{
this.app = app;
}
private function doSomething(event:MouseEvent):void
{
// call to myMethod() in TestApp.mxml
app.myMethod();
}
or you could do it this way
//in TestApp
protected function initApp():void
{
var popUp:TestComp = new TestComp(this);
popUp.addEventListener( 'test' , eventListener );
PopUpManager.addPopUp(popUp, this, true);
}
private function eventListener(event:Event):void
{
//in which case myMethod doesn't need to be public
myMethod();
}
//in TestComp
private function doSomething(event:MouseEvent):void
{
dispatchEvent( new Event('test') );
}

Some [Bindable] properties in Flex work, some don't

Problem solved, see below
Question
I'm working in Flex Builder 3 and I have two ActionScript 3 classes (ABC and XYZ) and a Flex MXML project (main.mxml). I have an instance of XYZ as a property of ABC, and I want XYZ's properties to be visible ([Bindable]) in the Flex project in text controls.
Unfortunately, only prop3 and prop4 update when they are changed. I've entered the debugger to make sure that prop1 and prop2 change, but they are not being updated in the text controls.
Here's the code:
ABC.as
[Bindable]
public class ABC extends UIComponent {
/* Other properties */
public var xyz:XYZ = new XYZ();
/* Methods that update xyz */
}
XYZ.as
[Bindable]
public class XYZ extends Object {
private var _prop1:uint = 0;
private var _prop2:uint = 0;
private var _prop3:uint = 0;
private var _prop4:uint = 1;
public function get prop1():uint {
return _prop1;
}
public function set prop1(value:uint):void {
_prop1 = value;
}
/* getters and setters for prop2, prop3, and prop4 */
}
main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:com="com.*" />
<com:ABC id="abc" />
<mx:Text text="{abc.xyz.prop1}" />
<mx:Text text="{abc.xyz.prop2}" />
<mx:Text text="{abc.xyz.prop3}" />
<mx:Text text="{abc.xyz.prop4}" />
</mx:Application>
Answer
It's not apparent from the small code snippets that I posted, but from within XYZ I was updating _prop3 and _prop4 using their setters. In constrast, I updated _prop1 and _prop2 through their private variables, not their setters. Thus, properties 1 and 2 were not dispatching update events.
It looks like your getters are returning voids. They should be returning uints, according to your field types.
Otherwise your code should be working fine. I've assembled and tested a working version with a Timer that sets all four values, so you can see all four updating:
Main.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
private function onCreationComplete():void
{
var t:Timer = new Timer(1000);
t.addEventListener(TimerEvent.TIMER, t_tick);
t.start();
}
private function t_tick(event:TimerEvent):void
{
var i:uint = Timer(event.currentTarget).currentCount;
abc.xyz.prop1 = i;
abc.xyz.prop2 = i;
abc.xyz.prop3 = i;
abc.xyz.prop4 = i;
}
]]>
</mx:Script>
<local:ABC id="abc" />
<mx:VBox>
<mx:Text text="{abc.xyz.prop1}" />
<mx:Text text="{abc.xyz.prop2}" />
<mx:Text text="{abc.xyz.prop3}" />
<mx:Text text="{abc.xyz.prop4}" />
</mx:VBox>
</mx:Application>
ABC.as
package
{
import mx.core.UIComponent;
[Bindable]
public class ABC extends UIComponent
{
public var xyz:XYZ = new XYZ();
public function ABC()
{
super();
}
}
}
XYZ.as
package
{
[Bindable]
public class XYZ extends Object
{
private var _prop1:uint = 0;
private var _prop2:uint = 0;
private var _prop3:uint = 0;
private var _prop4:uint = 1;
public function XYZ()
{
super();
}
public function get prop1():uint {
return _prop1;
}
public function set prop1(value:uint):void {
_prop1 = value;
}
public function get prop2():uint {
return _prop2;
}
public function set prop2(value:uint):void {
_prop2 = value;
}
public function get prop3():uint {
return _prop3;
}
public function set prop3(value:uint):void {
_prop3 = value;
}
public function get prop4():uint {
return _prop4;
}
public function set prop4(value:uint):void {
_prop4 = value;
}
}
}
Have a look at those, plug things in and you should see it all come together. Post back if you have any questions. Cheers.
When you define your bindable sources through the use of getters and setters, the bindings don't seem to work. The solution is to declare a bindable event for your getter and dispatch the event in your setter:
[Bindable]
public class XYZ extends Object {
private var _prop1:uint = 0;
private var _prop2:uint = 0;
private var _prop3:uint = 0;
private var _prop4:uint = 1;
[Bindable(event="prop1Changed")]
public function get prop1():uint {
return _prop1;
}
public function set prop1(value:uint):void {
_prop1 = value;
dispatchEvent (new Event ("prop1Changed"));
}
/* getters and setters for prop2, prop3, and prop4 */
}
So whenever your private member changes, an event is dispatched that notifies any component linked to the getter that the property has changed.

Resources