« Monitoring the connection against TCP/IP socket endpoints in AIR using JavaScript | Main | Flex skinning with Adobe Illustrator CS3 »

Extending the ComboBox Flex component to support multiselection

Working in a new dashboard Flex project, I've faced with the need to implement a multi selection feature on a ComboBox control. My goal was to extend the ComboBox component so that the selectedItems and selectedIndexes properties return the selected item contained in the dataprovider and the indices.
And here's how to make it happens.

Implementing this feature is very simple because the ComboBox is a subclass of the ListBase class that works as a dropdown to show and select items. And from the ListBase class it inherits the multiselection property. So all we have to do is to set the allowMultipleSelection property to true:

dropdown.allowMultipleSelection = true;

Multiselection works when the user presses the CTRL or CMD button on the keyboard to select more than one item. So we need to set to true the value of allowMultipleSelection property each time this button is pressed. This can be done by overridding the keyUpHandler and the keyDownHandler events. The event handlers have to:

1) Set the global variable ctrlKey.
2) Set the allowMultipleSelection property of the dropdown List.
2) When ctrl is up close the dropdown and dispatch the change event.

Remember to define the ctrlKey as global property of the combobox so that every functions can be accessed:

public class ComboBoxs extends ComboBox

{
private var ctrlKey:Boolean = false;

Let see how to do this:

override protected function keyDownHandler(event:KeyboardEvent) : void

{
super.keyDownHandler( event );

ctrlKey = event.ctrlKey;

if ( ctrlKey )
dropdown.allowMultipleSelection = true;

}

override protected function keyUpHandler(event:KeyboardEvent) : void

{
super.keyUpHandler( event );

ctrlKey = event.ctrlKey;


if ( !ctrlKey )
{
close();
var changeEvent:ListEvent = new ListEvent( ListEvent.CHANGE )

dispatchEvent( changeEvent );
}
}

Now we have a full ComboBox Flex control able to receive multiselection but when we click on an item the dropDown menu closes.We need to stop the closure of the ComboBox control if the Ctrl key pressed.
To do this we override the close() method:

override public function close(trigger:Event=null) : void

{
if ( !ctrlKey )
super.close( trigger );

}

Finally we can expose the selectedItems and selectedIndices property of the ComboBox and set them to bindable when the change event is dispatched by the keyUpHandler .
We need to check with an if() conditional statement if the dropdown (the istance name of the ListBase class) is not null before getting its selectedItems property:

public function set selectedItems( value:Array ) : void

{
if ( dropdown )
dropdown.selectedItems = value;

}

[Bindable("change")]
public function get selectedItems( ) : Array

{
if ( dropdown )
return dropdown.selectedItems;

else
return null;
}

public function set selectedIndices( value:Array ) : void

{
if ( dropdown )
dropdown.selectedIndices = value;

}

[Bindable("change")]
public function get selectedIndices( ) : Array

{
if ( dropdown )
return dropdown.selectedIndices;

else
return null;
}

It's all done! Now you have a fully multi selectable combo box.
Here you can see the full class code:

package
{
import flash.events.Event;
import flash.events.KeyboardEvent;


import mx.controls.ComboBox;
import mx.events.FlexEvent;
import mx.events.ListEvent;

public class ComboBoxs extends ComboBox

{
private var ctrlKey:Boolean = false;

public function ComboBoxs()

{
super();
}

override public function close(trigger:Event=null) : void

{
if ( !ctrlKey )
super.close( trigger );

}

override protected function keyDownHandler(event:KeyboardEvent) : void

{
super.keyDownHandler( event );

ctrlKey = event.ctrlKey;


if ( ctrlKey )
dropdown.allowMultipleSelection = true;

}

override protected function keyUpHandler(event:KeyboardEvent) : void

{
super.keyUpHandler( event );

ctrlKey = event.ctrlKey;


if ( !ctrlKey )
{
close();
var changeEvent:ListEvent = new ListEvent( ListEvent.CHANGE )

dispatchEvent( changeEvent );
}
}

public function set selectedItems( value:Array ) : void

{
if ( dropdown )
dropdown.selectedItems = value;

}

[Bindable("change")]
public function get selectedItems( ) : Array

{
if ( dropdown )
return dropdown.selectedItems;

else
return null;
}

public function set selectedIndices( value:Array ) : void

{
if ( dropdown )
dropdown.selectedIndices = value;

}

[Bindable("change")]
public function get selectedIndices( ) : Array

{
if ( dropdown )
return dropdown.selectedIndices;

else
return null;
}

}
}

TrackBack

TrackBack URL for this entry:
http://blog.comtaste.com/mt-tb.cgi/62

Comments (16)

Phil:

selectedLabel value is always the last selected item in the combobox list :(
How to set it to (for example) as a value "A + B + C" if I selected items A, B & C in the list ??
Thanks

Sean:

This works nicely except for the set selectedItems. There must be something internal to the ComboBox that keeps setting the allowMultipleSelection back to false. Any ideas?

Yes for this reason we have to force the allowMultipleSelection everytime we press the ctrl key. You can see here:

override protected function keyDownHandler(event:KeyboardEvent) : void

{
super.keyDownHandler( event );

ctrlKey = event.ctrlKey;

if ( ctrlKey )
dropdown.allowMultipleSelection = true;

}

If i found any other solution i will let you know

Liviu, thanks for posting this, it works a treat.

@Sean, I'm experiencing the same issue with this component. I'm developing a report builder and needed this multi select combobox so users can select multiple options - but when allowing users to edit a previously saved report BAM I slammed into this issue.

I worked around it by using the CallLater function. As I'm programmatically creating "ComboBoxs" the component is not completely created when I assign selectedIndicies and thus fails the "if (dropdown)" test.

By using CallLater the assignemnt is deffered until it has completed its creation process.

What's not working now is the text (label) field isn't set :(

I'm new to FLEX (6 months) and I'm sure there's a better way.

HTH!!!

achi:

hi,

Multiple selection works fine. but combobox is displaying only one value. i want to display the selected values with comma as a seperator in the combo box. any idea how to get it? Please help me here

achi:

hi,

Multiple selection works fine. but combobox is displaying only one value. i want to display the selected values with comma as a seperator in the combo box. any idea how to get it? Please help me here

Vasan:

Hi

Thank you very much for this component.

@ Anthony

can you tell me how to set the selectedItems programatically.. pls send me a code sample.

Thanks
Vasan

Anthony:

@Vasan,

In my app I'm setting the selected items via the selectedIndices function though I don't see why it wouldn't work for the selectedItems function.

Here's the summary of changes I made:

Add this attribute to the class
private var indices:Array;


Replace selectedIndices function with this one
public function set selectedIndices( value:Array ) : void
{
if ( dropdown ) {
dropdown.selectedIndices = value;
} else {
this.indices = value; // Save the value in private array
callLater(this.setIndices); // Request function setIndices be called later
}

Add this function
public function setIndices():void
{
this.selectedIndices = this.indices;
}

As I mentioned in my previous post this change will select the items but it will not display the first selected item in text area :(

Please let us know if it worked for you!

Vasan:

Hi all

Any updates on how to set selectedItems and selectedIndices programatically ?

To show the item's selected you can set the text property of textInput component used by the ComboBox to display the selected value. You can set for example after the commitProperty:

override protected function commitProperties():void
{
super.commitProperties();
textInput.text = "your text";
}

Vasan:

Hi Liviu and Anthony
Thank u very much for the help. the component works fine.

Vasan

kumar:

hi vasan can you send me the complete update code, thanks in advance

prasanth:

Hey ..
My requirement is to highlight the selected items in the ComboBox dropdown initially. I tried setting the selectedIndices using the above code. Initially it highlights the multiple items but when I close the dropdown and reopen it, the multiple highlight is gone and only the 1st one is highlighted.

Anonymous:

Hi, thanks for the code. This is how I solved the label concatenation.

Maybe not really clean but working fine.

override public function itemToLabel(item:Object):String
{
var label : String = "";
if( !selectedItems || selectedItems.length return super.itemToLabel(item);
else
{
for each( var object : Object in selectedItems )
{

label += super.itemToLabel(object) + ", ";
}
}
return label.substring(0,label.length - 2);
}

Hey... great code :)
The only issue i m facing is, it fires multiple change events for multiple selection. any clues?

Jim:

I have more or less the same issue as the one described by Prasanth on 13 May 2009.

I am able to successfully select multiple items in the dropdown, and if I click on the ComboBox after selecting multiple items, they still are all selected SOMETIMES. I have yet to determine what the case is that is causing this component to have only a single item selected after I had already selected multiple items, but multiple item selection is not guaranteed to "stick". When it fails, only the most recently-selected item remains selected.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About

This page contains a single entry from the blog posted on September 19, 2008 5:09 PM.

The previous post in this blog was Monitoring the connection against TCP/IP socket endpoints in AIR using JavaScript.

The next post in this blog is Flex skinning with Adobe Illustrator CS3.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33