Add Icon support to ComboBox

2006:12:20

Ever wanted icons to appear in the dropdown of a ComboBox? This article shows you how!

The Problem – No iconField/iconFunction support for ComboBox

Unlike the List component and all of its child classes, the ComboBox does *not* natively support an iconField/iconFunction capability to let you dynamically assign an icon for each element that it contains.

For some time I have wanted to display elements in a ComboBox with an associated icon to indicate state, like ‘this element is active’ vs. ‘this element is not active’. Well I invested the time in understanding ComboBox and have a fairly clean way of adding iconField/iconFunction support to ComboBox.

The solution lies in the fact that the actual dropdown you see when opening a ComboBox is really an instance of a List component. Since List supports the iconField/iconFunction property, all I needed to do was get a handle to the dropdown and assign my own iconField/iconFunction values. The best way to demontrate this is to look at the example code.

Example Code – The ComboBoxUtil Class

There are a number of approaches you could take to add icon support behavior to a ComboBox. I opted for a stand-alone utility class thats lets you modify any existing ComboBox instance at runtime. Feel free to glean what you need from my class below to implement your own preferred pattern.

/**
 * ComboBoxUtil
 *
 * Utility class for working with ComboBoxes
 * @version $Id: $
 * @author Mitch Coopet
 */
class utils.ComboBoxUtil {
    /**
     * Adds iconField support to a ComboBox
     * @param cb The ComboBox reference you want to modify
     * @param iconFunction A reference to the icon function you want to use
     */
    public static function setIconField(cb:ComboBox, iconField:String) {
        _setIcon(cb,iconField);
    }

    /**
     * Adds iconFunction support to a ComboBox
     * @param cb The ComboBox reference you want to modify
     * @param iconFunction A reference to the icon function you want to use
     */
    public static function setIconFunction(cb:ComboBox, iconFunction:Function) : Void {
        _setIcon(cb,iconFunction);
    }

    /**
     * Internal helper function to perform actual assignment of icon field/function
     * support to a ComboBox
     * @param cb The ComboBox reference you want to modify
     * @param icon A String representing the icon field OR a Function representing
     * the icon function to use.
     */
    private static function _setIcon(cb:ComboBox,icon) : Void {
        // We use a delegate to perform the assignment of the icon
	// functionality because the combobox may/may not be rendering
	var delegate = new Object();
	delegate.createIcon = function () {
	    // This is the secret sauce - we get a handle
	    // to the actual dropdown instance.  The dropdown is a
	    // List that appears when you 'open' a ComboBox.
	    // Since the List component supports iconField/iconFunction,
       	    // all we need to do is set them on the dropdown instance!
	    var dropDown = cb.getDropdown();
	    if (icon instanceof Function) {
	        dropDown.iconFunction = icon;
	    } else {
	        dropDown.iconField = icon;
	    }
        };

	// Some bullet-proofing -
	// If the ComboBox is 'initializing' the dropdown will be undefined.
	// So we code around it by adding the delegate to the stack of
        // methods that are called
	// after the ComboBox is done initializing.
	if (cb.initializing) {
	    cb.doLater(delegate,delegate.createIcon);
	} else {
	    delegate.createIcon();
	}
}

Example – Using ComboBoxUtil

For this code example demonstrating using ComboBoxUtil, assume I already have a ComboBox reference called ‘cb’ and 2 symbols in my library called ‘iconOn’ and ‘iconOff’.

// Set the iconFunction
// You could easily just pass in a String denoting the name of a property
// that contains the icon to use, i.e. utils.ComboBoxUtil.setIconField(cb,"myIcon");
utils.ComboBoxUtil.setIconFunction(cb,getIcon);

// Assign the data provider
cb.dataProvider = [
    { label:"element 1 - off", data:0 },
    { label:"element 2 - on", data:1 }
];

/*
 * This icon function returns the appropriate icon based on
 * the 'data' property of an object.  If data == 0, it returns
 * 'iconOff', otherwise it returns 'iconOn'.
 */
function getIcon(item) : String {
  return item.data == 0?'iconOff':'iconOn';
}