3

I have prepared a simplified test case for my question. It will run instantly in your Flash Builder if you put the 2 files below into a project.

I'm trying to display a List of strings and a confirmation checkbox in a popup:

screenshot

In the real application I dispatch a custom event with the string selected in the list, but in the test code below I just call trace(str);

My problem: if I use click event, then the window closes, even if I click at a scrollbar (the !str check below doesn't help, when an item had been selected in previous use). And if I use change event, then the window doesn't close, when I click on the same item as the last time. And the itemClick event seems not to be present in spark.components.List anymore.

Any suggestions please on how to handle this probably frequent problem?

Writing a custom item renderer and having a click event handler for each item seems to be overkill for this case, because I have strings in the list.

Test.mxml: (please click myBtn few times - to see my problems with click and change)

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    minWidth="400" minHeight="300">

    <fx:Script>
        <![CDATA[
            import mx.managers.PopUpManager;

            private var _popup:Popup = new Popup();

            private function showPopup(event:MouseEvent):void {
                PopUpManager.addPopUp(_popup, this, true);
                PopUpManager.centerPopUp(_popup);
            }
        ]]>
    </fx:Script>

    <s:Button id="myBtn" right="5" bottom="5" 
        label="Open window" click="showPopup(event)" />

</s:Application>

Popup.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow 
    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="240" height="240"
    creationComplete="init(event)"
    close="close()">    

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayList;
            import mx.controls.Alert;
            import mx.events.FlexEvent;
            import mx.events.CloseEvent;
            import mx.events.ItemClickEvent;
            import mx.managers.PopUpManager;

            private var myData:ArrayList = new ArrayList();

            private function init(event:FlexEvent):void {
                // XXX in the real app data is updated from server
                myData.removeAll();
                for (var i:uint = 1; i <= 10; i++)
                    myData.addItem('Item #' + i);
            }

            public function close(event:TimerEvent=null):void {
                PopUpManager.removePopUp(this);
            }

            private function handleClick(event:MouseEvent):void {
                var str:String = myList.selectedItem as String;
                if (!str)
                    return;

                if (myBox.selected) {
                    Alert.show(
                        'Select ' + str + '?', 
                        null, 
                        mx.controls.Alert.YES | mx.controls.Alert.NO,
                        null,
                        handleConfirm,
                        null,
                        mx.controls.Alert.NO
                    );
                } else {
                    sendEvent();
                }
            }

            private function handleConfirm(event:CloseEvent):void {
                if (event.detail == mx.controls.Alert.YES)
                    sendEvent();
            }

            private function sendEvent():void {
                close();
                // XXX in the real app dispatchEvent() is called
                trace('selected: ' + (myList.selectedItem as String));
            }
        ]]>
    </fx:Script>

    <s:VGroup paddingLeft="20" paddingTop="20" 
            paddingRight="20" paddingBottom="20" gap="20" 
            width="100%" height="100%">
        <s:List id="myList" dataProvider="{myData}" 
            click="handleClick(event)"
            width="100%" height="100%" fontSize="24" />
        <s:CheckBox id="myBox" label="Confirm" />
    </s:VGroup>
</s:TitleWindow>

Also I wonder, why do I get the warning above:

Data binding will not be able to detect assignments to "myData".
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
  • 2
    Data binding will not be able to detect assignments means that you need to declare the variable myData with a [Bindable] tag. – Angelo Dec 08 '11 at 15:06

3 Answers3

5

The Spark List dispatches an 'IndexChangeEvent.CHANGE'. You can listen for this event to know when the selection in the List has changed.

    <s:List id="myList" dataProvider="{myData}" 
            change="handleIndexChange()"
            width="100%" height="100%" fontSize="24" />

That event is only dispatched whenever the selected index actually changes, which means that when you reopen the window a second time an item might still be selected and when you click on that one, no CHANGE event will be fired. To fix this just deselect the selection before you close the window:

        public function close():void {
            myList.selectedIndex = -1;
            PopUpManager.removePopUp(this);
        }

Also make sure to dispatch your event with the selected item before you close the window (and deselect it).

As for your question about the binding warning: you get that message because you didn't mark 'myData' to be bindable. To fix this just use the [Bindable] tag:

[Bindable]
private var myData:ArrayList = new ArrayList();

or skip the binding altogether if you don't need it and just assign the dataprovider to the list in ActionScript:

myList.dataProvider = myData;
RIAstar
  • 11,912
  • 20
  • 37
  • Thanks, but as I have written in the original question: if you use the myBtn few times and try selecting same item again, then it won't work. The popup doesn't close and the trace isn't printed (i.e. custom event not dispatched in the real app). – Alexander Farber Dec 08 '11 at 15:14
2

I'd recommend two solutions if you absolutely wnat to display what item was selected. Otherwise, the solution provided by RIAStar would do the trick.

Listen to rendererAdd and rendererRemove events within your PopUp

As explained here, you can easily access to your list's renderers without interfering with its virtualLayout business.

Use a custom renderer

I know. But as long as you keep your code clean, itemRenderers won't blow up your application's memory. They're made to render huge amount of items without memory leaks.

LoremIpsum
  • 4,328
  • 1
  • 15
  • 17
  • Actually I have an item renderer in my real app anyway, so I could use it. But how do I know the selected string while being inside its click event handler? – Alexander Farber Dec 08 '11 at 16:56
-2

In your Test.mxml, modify the codes like:

  <fx:Script>
            <![CDATA[
                import mx.managers.PopUpManager;

                private var _popup:Popup;

                private function showPopup(event:MouseEvent):void {
                    _popup = new Popup();
                    PopUpManager.addPopUp(_popup, this, true);
                    PopUpManager.centerPopUp(_popup);
                }
            ]]>
        </fx:Script>

And in your Popup.mxml, I am not sure why you have the TimerEvent in the close function.

Also the trace won't be shown, as you are calling the close() function immediately after the alert's YES button has been clicked..

Angelo
  • 1,578
  • 3
  • 20
  • 39
  • That's not what Alexander Farber asked. Here you just reinitialize the popup. You lose the information about what item was selected. – LoremIpsum Dec 08 '11 at 15:34
  • Sorry, the TimerEvent is a leftover here from the real app where I display a countdown in TitleWindow's title. Doesn't really matter here. – Alexander Farber Dec 08 '11 at 17:09