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;
}
}
}
Comments (16)
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
Posted by Phil | October 3, 2008 1:22 AM
Posted on October 3, 2008 01:22
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?
Posted by Sean | October 10, 2008 4:17 AM
Posted on October 10, 2008 04:17
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
Posted by Liviu Stoica | October 15, 2008 5:38 PM
Posted on October 15, 2008 17:38
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!!!
Posted by Anthony | November 21, 2008 3:02 PM
Posted on November 21, 2008 15:02
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
Posted by achi | December 1, 2008 3:01 PM
Posted on December 1, 2008 15:01
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
Posted by achi | December 1, 2008 3:03 PM
Posted on December 1, 2008 15:03
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
Posted by Vasan | December 5, 2008 8:01 AM
Posted on December 5, 2008 08:01
@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!
Posted by Anthony | December 7, 2008 5:25 PM
Posted on December 7, 2008 17:25
Hi all
Any updates on how to set selectedItems and selectedIndices programatically ?
Posted by Vasan | December 8, 2008 7:19 AM
Posted on December 8, 2008 07:19
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";
}
Posted by Liviu Stoica | December 9, 2008 10:59 AM
Posted on December 9, 2008 10:59
Hi Liviu and Anthony
Thank u very much for the help. the component works fine.
Vasan
Posted by Vasan | December 11, 2008 4:06 PM
Posted on December 11, 2008 16:06
hi vasan can you send me the complete update code, thanks in advance
Posted by kumar | April 14, 2009 11:35 PM
Posted on April 14, 2009 23:35
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.
Posted by prasanth | May 13, 2009 12:08 PM
Posted on May 13, 2009 12:08
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);
}
Posted by Anonymous | July 14, 2009 10:47 AM
Posted on July 14, 2009 10:47
Hey... great code :)
The only issue i m facing is, it fires multiple change events for multiple selection. any clues?
Posted by Siraj | July 28, 2009 6:18 AM
Posted on July 28, 2009 06:18
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.
Posted by Jim | August 3, 2009 10:12 PM
Posted on August 3, 2009 22:12