In the code below, the presentedAlbumIndex is used to control the selectedIndex of the TileList. Item "five" is initially selected. Whenever the button is pressed the first item in the array is deleted, and the presentedAlbumIndex is decremented.
In theory, the selected index should stay with "five" every time the button is clicked (until "five" is itself deleted). It works this way for the first button press. However, on the 2nd button press the highlighting changes to "six" for some reason. Also, the TileList selectedIndex is always one behind.
Why?
I tried looking into ListBase and monitoring selectedIndex. It looks like the selectedIndex is updated initially to the correct index, but then it reverts back at some point to the correct index+1. I do not know why it's setting back.
It seems it's due to the fact that I am doing a data provider delete and index change in the same operation.
Is there some function in TileList I could override to keep selectedIndex up to date?
Steve
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
applicationComplete="init()">
<mx:Label id="label1" text="{'TileList selected index: ' + albumsThumbnailList.selectedIndex}"
x="255" y="55" width="234"/>
<mx:Label id="label2" text="{'Presented album index: ' + presentedAlbumIndex}" x="255" y="81" width="234"/>
<mx:TileList id="albumsThumbnailList" direction="vertical"
dataProvider="{presentedAlbums}"
selectedIndex="{presentedAlbumIndex}" x="25" y="13"
change="presentedAlbumIndex=albumsThumbnailList.selectedIndex"
height="400"/>
<mx:Button click="test2()" x="297" y="150"/>
<mx:Script>
<![CDATA[
import mx.events.CollectionEvent;
import mx.collections.ArrayCollection;
private var _includedAlbums:ArrayCollection = new
ArrayCollection(["zero","one","two","three","four","five","six","seven"]);
[Bindable]
private var presentedAlbumIndex:int = 5;
private function init():void {
_includedAlbums.addEventListener(CollectionEvent.COLLECTION_CHANGE,
function():void {
dispatchEvent(new Event("albumDataChanged"));
}
);
}
public function test2():void {
_includedAlbums.removeItemAt(0);
presentedAlbumIndex--;
}
[Bindable(event="albumDataChanged")]
public function get presentedAlbums():ArrayCollection {
return _includedAlbums;
}
]]>
</mx:Script>
</mx:Application>
With the COLLECTION_CHANGED listener the List gets a new dataProvider, since this is an async event it fires after the binding on the selectedIndex. By removing this listener and not providing a new collection dataProvider whenever the collection changes, the selectedIndex binding functions as expected.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
applicationComplete="init()">
<mx:Label id="label1" text="{'TileList selected index: ' + albumsThumbnailList.selectedIndex}"
x="255" y="55" width="234"/>
<mx:Label id="label2" text="{'Presented album index: ' + presentedAlbumIndex}" x="255" y="81" width="234"/>
<mx:TileList id="albumsThumbnailList" direction="vertical"
dataProvider="{presentedAlbums}" x="25" y="13"
selectedIndex="{presentedAlbumIndex}"
height="400"/>
<mx:Button click="test2()" x="297" y="150"/>
<mx:Script>
<![CDATA[
import mx.events.CollectionEvent;
import mx.collections.ArrayCollection;
private var _includedAlbums:ArrayCollection = new
ArrayCollection(["zero","one","two","three","four","five","six","seven"]);
[Bindable]
private var presentedAlbumIndex:int = 5;
private function init():void {
}
public function test2():void {
_includedAlbums.removeItemAt(0);
presentedAlbumIndex--;
}
[Bindable(event="albumDataChanged")]
public function get presentedAlbums():ArrayCollection {
return _includedAlbums;
}
]]>
</mx:Script>
</mx:Application>
Related
I I have a view in Flex 3 where I use a tab navigator and a number of views inside the tab navigator. I need to be know which view was clicked because of it's one specific view then I need to take action, i.e. if view with id "secondTab" is clicked then do something.
I have set it up to be notified, my problem is that I need to be able to know what view it is. Calling tab.GetChildByName or a similar method seems to only get me back a TabSkin object.
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
height="100%"
xmlns:local="*"
creationComplete="onCreationComplete(event)">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.controls.Button;
protected function onCreationComplete(event:Event):void {
for(var i:int = 0; i < myTN.getChildren().length; i++) {
var tab:Button = myTN.getTabAt(i);
tab.addEventListener(FlexEvent.BUTTON_DOWN, tabClickHandler);
}
}
private function tabClickHandler(event:FlexEvent):void {
var tab:Button;
if(event.currentTarget is Button) {
tab = event.currentTarget as Button;
// how do I access the actual view hosted in a tab that was clicked?
}
}
]]>
</mx:Script>
<mx:TabNavigator id="myTN">
<local:ProductListView id="firstTab"
label="First Tab"
width="100%" height="100%" />
<local:ProductListView id="secondTab"
label="Second Tab"
width="100%" height="100%" />
</mx:TabNavigator>
</mx:VBox>
TabNavigator is a subclass of ViewStack and it will fire a change event when you select a tab.
<mx:TabNavigator id="myTN" change="childChanged()">
<local:ProductListView id="firstTab"
label="First Tab"
width="100%" height="100%" />
<local:ProductListView id="secondTab"
label="Second Tab"
width="100%" height="100%" />
</mx:TabNavigator>
It is as straightforward as:
private function childChanged():void
{
if(myTN.selectedChild == this.firstTab) //or myTN.selectedIndex == 0
{
trace("selected the first one");
}
else if(myTN.selectedChild == this.secondTab) //or myTN.selectedIndex == 0
{
trace("selected the second one");
}
}
As TabNavigator is an extension of ViewStack you can access the selected view with the selectedChild property:
private function tabClickHandler(event:FlexEvent):void {
view = myTN.selectedChild;
// Do what you need to do with it here...
}
For more information on how TabNavigator works, check out the Documentation:
http://livedocs.adobe.com/flex/3/html/help.html?content=navigators_4.html
I have a datagrid column with a button that opens a modal dialog box allowing the user to upload multiple files. In the code below, the browseAndUpload() method does that. When the user finished uploading files and closes the upload box the closeUpload() method is called. I know for a fact that the uploaded files are being copied into arrFiles.
The problem I am having is that the repeater will not show the files in arrFiles. Here is the code:
<mx:DataGridColumn id="dgcUpload" width="42" headerText="Uploaded Files"
editable="false">
<mx:itemRenderer>
<mx:Component>
<mx:VBox>
<mx:Script>
<![CDATA[
[Bindable]public var arrFiles:ArrayCollection = new ArrayCollection();
public var fileUpload:FileUpload = new FileUpload();
private function browseAndUpload(event:MouseEvent):void
{
fileUpload = FileUpload(PopUpManager.createPopUp(this, FileUpload, true));
fileUpload.addEventListener(CloseEvent.CLOSE, closeUpload);
fileUpload["btnClose"].addEventListener("click", closeUpload);
}
private function closeUpload(event:Event):void
{
arrFiles = fileUpload.arrFiles;
}
]]>
</mx:Script>
<mx:HBox paddingLeft="3" paddingRight="3">
<mx:Button width="36" label="..." click="browseAndUpload(event)"/>
</mx:HBox>
<mx:Repeater id="rpFiles" dataProvider="{arrFiles}">
<mx:Label text="{FileVO(rpFiles.currentItem).name}"/>
</mx:Repeater>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
Thank you in advance for any insight,
Orville
Got it! I made the following changes:
private function closeUpload(event:Event):void
{
arrFiles = fileUpload.arrFiles;
rpFiles.dataProvider = arrFiles;
}
<mx:Repeater id="rpFiles">
<mx:Label text="{FileVO(rpFiles.currentItem).name}"/>
</mx:Repeater>
You are assigning fileUpload.arrFiles directly to arrFiles. Is the former an Array or ArrayCollection? You might need to do arrFiles = new ArrayCollection(fileUpload.arrFiles);
That being said, I hate flex binding and generally avoid it because it can be unreliable. In your case, I'd write my own AS3 component that implements the ItemRenderer and then assign the repeater's dataprovider manually when it changes. You will have more control over the behavior if you do it that way. And a much easier time debugging.
I am having a problem with a checkbox in my datagrid. It pulls in a dataprovider (xml file) and I am using it to set the checkbox selection with a custom itemrenderer. I then save the datagrid, when updated, back to the xml file.
The checkbox, when clicked, saves to the xml fine.. I know this because when I reload the application it shows the correct result. However, when I just refresh the dataprovider without closing out the flex application then the checkboxes revert back to what they were before the change.
So here is the custom checkbox:
<?xml version="1.0" encoding="utf-8"?>
<mx:CheckBox xmlns:mx="http://www.adobe.com/2006/mxml" >
<mx:Script>
<![CDATA[
override public function set data( value:Object ):void{
super.data = value;
trace("data = " + data.#hidden);
if(data.#hidden == "true")
{
this.selected = true;
}else
{
this.selected = false;
}
}
]]>
</mx:Script>
</mx:CheckBox>
And here is where I am calling it:
<mx:DataGridColumn width="75" headerText="hide?" dataField="#hidden">
<mx:itemRenderer>
<mx:Component>
<local:itemRendCheckBox />
</mx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
I imagine what is happening is that everything is updating except the custome itemrenderer. Is there something I can do to tell the checkbox to refresh with the dataprovider?
I think I figured it out, I was calling the datagrid to refresh, which is different then refreshing the actual dataprovider. So instead of myDataGrid.send() I called dataProv.dataprovider.refresh();
I have a textArea and a Button - I want the Button to disappear when the user clicks anywhere in the app window EXCEPT the "Send" button
<mx:Button x="306" y="168" label="Button" id="btn" click="Alert.show('Button clicked')"/>
<mx:TextArea x="138" y="146" focusOut="btn.visible=false" focusIn="btn.visible=true"/>
I tried calling btn.visible=false when TextArea loses focus (using focusOut event) - if I click anywhere in the app it works, but it also works when I click the Button - the TextArea focusOut event is processed first and the click for the button later - can someone please help out with this?
Thanks!
were u trying to override the default focusOut handler? That doesn't work...
private function setBtnNotVisible():void
{
btn.visible=false;
}
override protected function focusOutHandler(event:FocusEvent):void
{
callLater(setBtnNotVisible);
}
Neither does writing my own event handler...
private function setBtnNotVisible():void
{
btn.visible=false;
}
private function focusOutHandler2(event:FocusEvent):void
{
callLater(setBtnNotVisible);
}
--------------------- Full Code ------------------
##### Try clicking INSIDE the textArea and then on the button, I cannot catch the Alert
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private function onFocusOut(event:FocusEvent):void{
callLater(
function ():void{
btn.visible=false;
}
)
}
]]>
</mx:Script>
<mx:Button x="306" y="168" label="Button" id="btn" click="Alert.show('Button clicked')"/>
<mx:TextArea x="138" y="146" focusOut="onFocusOut(event)" focusIn="btn.visible=true"/>
</mx:Application>
I previously said use call later, but on testing it doesn't work, sorry for wasting anyone's time. Instead you need to use the focus manager something like this: I tested this and it seems solid.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.managers.FocusManager; //pull in the manager
private function onFocusOut(event:FocusEvent):void{
if(getFocus() != null){ //in case focus goes outside the flash player
if(getFocus().name == "btn"){ //the focus went to the item with the ID "btn"
return; //do nothing, let the click handler work
}else{ //any other item gets focus
btn.visible=false; //disappear
}
}
}
private function clickHandler():void{ // made it it's own function so do more than just alert
Alert.show('Button clicked');
btn.visible=false;
}
]]>
</mx:Script>
<mx:Button x="306" y="168" label="Button" id="btn" click="clickHandler();"/>
<mx:TextArea x="138" y="146" focusOut="onFocusOut(event)" focusIn="btn.visible=true"/>
</mx:Application>
I have a Flex application where I'm using a Canvas to contain several other components. On that Canvas there is a Button which is used to invoke a particular flow through the system. Clicking anywhere else on the Canvas should cause cause a details pane to appear showing more information about the record represented by this control.
The problem I'm having is that because the button sits inside the Canvas any time the user clicks the Button the click event is fired on both the Button and the Canvas. Is there any way to avoid having the click event fired on the Canvas object if the user clicks on an area covered up by another component?
I've created a simple little test application to demonstrate the problem:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function onCanvasClick(event:Event):void {
text.text = text.text + "\n" + "Canvas Clicked";
}
private function onButtonClick(event:Event):void {
text.text = text.text + "\n" + "Button Clicked";
}
]]>
</mx:Script>
<mx:Canvas x="97" y="91" width="200" height="200" backgroundColor="red" click="onCanvasClick(event)">
<mx:Button x="67" y="88" label="Button" click="onButtonClick(event)"/>
</mx:Canvas>
<mx:Text id="text" x="97" y="330" text="Text" width="200" height="129"/>
</mx:Application>
As it stands when you click the button you will see two entries made in the text box, "Button Clicked" followed by "Canvas Clicked" even though the mouse was clicked only once.
I'd like to find a way that I could avoid having the second entry made such that when I click the Button only the "Button Clicked" entry is made, but if I were to click anywhere else in the Canvas the "Canvas Clicked" entry would still appear.
The event continues on because event.bubbles is set to true. This means everything in the display heirarchy gets the event. To stop the event from continuing, you call
event.stopImmediatePropagation()
Laplie's answer worked like a charm. For those interested the updated code looks like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function onCanvasClick(event:Event):void {
text.text = text.text + "\n" + "Canvas Clicked";
}
private function onButtonClick(event:Event):void {
text.text = text.text + "\n" + "Button Clicked";
event.stopImmediatePropagation();
}
]]>
</mx:Script>
<mx:Canvas x="97" y="91" width="200" height="200" backgroundColor="red" click="onCanvasClick(event)">
<mx:Button x="67" y="88" label="Button" click="onButtonClick(event)"/>
</mx:Canvas>
<mx:Text id="text" x="97" y="330" text="Text" width="200" height="129"/>
</mx:Application>
The only difference is the one additional line in the onButtonClick method.
I have 2 ideas, first try this:
btn.addEventListener(MouseEvent.Click,function(event:MouseEvent):void{
event.stopImmediatePropagation();
...
});
if that doesn't work, see if you can add the click listener to the canvas and not the button and check the target property on the event object. something like:
btn.addEventListener(MouseEvent.Click,function(event:MouseEvent):void{
if(event.target == btn){
...
}
else{
...
}
});
Again, these are some ideas of the top of my head...I'll create a small test app and see if these work...