Programatically Select Multiple [Flex] List/Data Grid Items From ActionScript

Jul 20, 2009

I needed to display to the users a List component with checkboxes;

  • I needed to do this to remove the need for CTRL & CTRL+Shift commands that might have been 'above' the technical level of my users.
  • I needed the visual effect they provided.
  • I wanted the items to be highlighted and apear mostly as they do in a standard list.
  • And lastly I wanted to be able to access what was choosen by the [myList.selectedItems] and not a custom holder.

I'm sure there are a few ways to make this happen, so I welcome any alternative advice. "View Source" is enabled on this flex app.

<flash source="/FlexApps/ProgramaticMultipleSelect/ProgramaticMultipleSelect.swf" width="386" height="163" quality="high" scriptAccess="none" align="center" />

 

My start page sets up the array for testing and calls on my extended List component "CheckBoxList.<mx:Application ...>

    <mx:Script> />        <![CDATA[
            private static const people:Array = [
                "Christina Coenraets", "Joanne Wall", "Maurice Smith", ...
            ];
        ]]>

    </mx:Script> />
    <local:CheckBoxList id="listOfPeople" dataProvider="{people}" width="200" height="140" />

    <mx:List dataProvider="{this.listOfPeople.selectedItems}" width="154" height="140" />

</mx:Application>


My custom List component overrides the mouse events, doing nothing with them. Instead I listen for itemChecked and itemUnchecked events to fire off my selection methods.

The part I struggled with in doing this was getting multiple options to be selected. It turns out that even though the List.selectedItems is an array, you can't just push to it. You have to do a replacement. The explains the concat and splice code you see below.<?xml version="1.0" encoding="utf-8"?>
<mx:List
    xmlns:mx="http://www.adobe.com/2006/mxml"
    creationComplete="onCreationComplete();"
    itemRenderer="ItemRenderer"
>


    <mx:Script> />        <![CDATA[
            private function onCreationComplete():void {
                 this.addEventListener("ItemChecked", onItemChecked);
                 this.addEventListener("ItemUnchecked", onItemUnchecked);
            }

             private function onItemChecked(event:ListItemEvent):void {
                 event.stopPropagation();
                this.selectedItems = this.selectedItems.concat([event.targetItem]);
                this.dispatchEvent(new Event(Event.CHANGE));
            }

             private function onItemUnchecked(event:ListItemEvent):void {
                 event.stopPropagation();

                var pos:int = this.selectedItems.indexOf(event.targetItem);

                this.selectedItems = this.selectedItems.filter(
                    function (item:*, index:int, array:Array):Boolean {
                        return item != this.targetItem;
                    },
                    event
                );

                this.dispatchEvent(new Event(Event.CHANGE));
            }

            override protected function mouseUpHandler(event:MouseEvent):void {}
            override protected function mouseDownHandler(event:MouseEvent):void {}
            override protected function focusInHandler(event:FocusEvent):void {}
        ]]>

    </mx:Script> />
</mx:List>

 

My ItemRenderer handles throwing the ItemChecked and ItemUnchecked events. Notice my checkbox selection check in my "set date" function. This makes sure that the correct boxes are always checked when the user scrolls.<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalScrollPolicy="off">
    <mx:Script> />        <![CDATA[
            import mx.controls.List;

            [Bindable] private var displayName:String;

             override public function set data(value:Object):void {
                super.data = value;
                displayName = value as String;

                var list:List = this.parent.parent as List;
                checkbox.selected = (list.selectedItems.indexOf(value) >
-1);
            }

            private function flipSelectedSwitch():void {
                this.checkbox.selected = !this.checkbox.selected;
                this.onSelectedChange();
            }

            private function onSelectedChange():void {
                if (checkbox.selected)
                    dispatchEvent(new ListItemEvent("ItemChecked", this.data, true, true));
                else
                    dispatchEvent(new ListItemEvent("ItemUnchecked", this.data, true, true));
            }
        ]]»
    </mx:Script> />
    <mx:Label width="100%" text="{displayName}" click="flipSelectedSwitch();" />

    <mx:CheckBox id="checkbox" change="onSelectedChange();" paddingRight="10" />
</mx:HBox>

Comments

Sam

Sam wrote on 08/14/09 6:34 AM

I had to do something similar today; thanks for this post for nudging me in the right direction.

Instead of rewriting the selection code, I decided to leverage the existing code. This has the added advantage of working with keyboard input as well.

BetterList.as:

package my.views.components
{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;

import mx.controls.List;

public class BetterList extends List
{
//when true, simply clicking on the list renderer will toggle selected
public var selectMultipleOnClick:Boolean;

public function BetterList()
{
super();

selectMultipleOnClick = false;
}

override protected function keyDownHandler(event:KeyboardEvent):void
{
if (selectMultipleOnClick)
event.ctrlKey = true;

super.keyDownHandler(event);
}

override protected function mouseDownHandler(event:MouseEvent):void
{
if (selectMultipleOnClick)
event.ctrlKey = true;

super.mouseDownHandler(event);
}
}
}

To use,

dataProvider="{data.importAccounts}"
allowMultipleSelection="true" selectMultipleOnClick="true">




import mx.events.ListEvent;
import mx.controls.List;

private function creationComplete():void
{
var list:List = this.parent.parent as List;
//keep the list renderer in sync
list.addEventListener(ListEvent.CHANGE, listChanged, false, 0, true);
}

private function listChanged(e:ListEvent=null):void
{
var list:List = this.parent.parent as List;
this.selected = (list.selectedItems.indexOf(data) > -1);
}

override protected function clickHandler(event:MouseEvent):void
{
//stop the click handler for the checkbox otherwise it will override the selected
//the checkbox will be updated via the listChanged method
//also stops the keyboard handler (that just calls this)
event.stopImmediatePropagation();
event.updateAfterEvent();
}

override public function set data(value:Object):void
{
super.data = value;
//needed because otherwise when list is reloaded, checkboxes are selected as per
//previous list, and not in the current list (out of sync with selectedItems)
listChanged();
}
]]>






Hope the code formats ok, and that it works - had to extract the code from a bigger MXML file.
Sam

Sam wrote on 08/14/09 6:38 AM

Hmm... the angle brackets got removed. To use it, just use the BetterList component instead of the standard mx:List component, and add the following property to it:

selectMultipleOnClick="true"

The other bits in the code above were to get the list to render a checkbox as well.
dsarkarraj

dsarkarraj wrote on 09/10/09 5:13 AM

hey how to show selected items sub items .. i have dynamically drawn lists which are loading data from an xml which has elements and sub elements of some elements and sub elements of some sub elements ?? please reply ....
Car Wheels

Car Wheels wrote on 09/19/09 7:59 AM

For sale remanufactured OEM wheels, wheel covers and aftermarket wheels for wholesale and retail customers. We offer custom wheels and discount rims, Car Wheelss, wheel rim, alloy wheel, Truck Wheels, steel wheel, and Alloy wheels, steel wheels.

Thanks

wheel covers

http://www.wheelx.com
Amrit

Amrit wrote on 10/26/09 10:33 AM

hey.. this is what i am exactly looking for.. thanks for the post... keep posting your good work ....

Write your comment



(it will not be displayed)



Subscribe to this comment thread