Removing the vertical separator from the ComboBox control in Flex

The following example shows how you can remove the vertical separator from the Flex ComboBox control by creating a custom ComboBox skin using ActionScript.

Full code after the jump.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/23/removing-the-vertical-separator-from-the-combobox-control-in-flex/ -->
<mx:Application name="ComboBox_skin_test"
        xmlns:mx="http://www.adobe.com/2006/mxml"
        backgroundColor="white">
 
    <mx:ComboBox id="comboBox"
            dataProvider="[The,Quick,Brown,Fox,Jumps,Over,The,Lazy,Dog]"
            skin="skins.CustomComboBoxSkin" />
 
</mx:Application>

The custom ComboBox skin class, skins/CustomComboBoxSkin.as, is as follows:

/**
 * http://blog.flexexamples.com/2009/03/23/removing-the-vertical-separator-from-the-combobox-control-in-flex/
 */
package skins {
    import flash.display.GradientType;
    import flash.display.Graphics;
 
    import mx.skins.halo.ComboBoxArrowSkin;
    import mx.skins.halo.HaloColors;
    import mx.styles.StyleManager;
    import mx.utils.ColorUtil;
 
    public class CustomComboBoxSkin extends ComboBoxArrowSkin {
        private static var cache:Object = {};
 
        public function CustomComboBoxSkin() {
            super();
        }
 
        private static function calcDerivedStyles(themeColor:uint, borderColor:uint, fillColor0:uint, fillColor1:uint):Object {
            var key:String = HaloColors.getCacheKey(themeColor, borderColor, fillColor0, fillColor1);
            if (!cache[key]) {
                var o:Object = cache[key] = {};
                HaloColors.addHaloColors(o, themeColor, fillColor0, fillColor1);
            }
            return cache[key];
        }
 
        override protected function updateDisplayList(w:Number, h:Number):void {
            super.updateDisplayList(w, h);
 
            var arrowColor:uint = getStyle("iconColor");
            var borderColor:uint = getStyle("borderColor");
            var cornerRadius:Number = getStyle("cornerRadius");
            var dropdownBorderColor:Number = getStyle("dropdownBorderColor");
            var fillAlphas:Array = getStyle("fillAlphas");
            var fillColors:Array = getStyle("fillColors");
            StyleManager.getColorNames(fillColors);
            var highlightAlphas:Array = getStyle("highlightAlphas");
            var themeColor:uint = getStyle("themeColor");
 
            // The dropdownBorderColor is currently only used
            // when displaying an error state.
            if (!isNaN(dropdownBorderColor)) {
                borderColor = dropdownBorderColor;
            }
 
            var derStyles:Object = calcDerivedStyles(themeColor, borderColor, fillColors[0], fillColors[1]);
            var borderColorDrk1:Number = ColorUtil.adjustBrightness2(borderColor, -50);
            var themeColorDrk1:Number = ColorUtil.adjustBrightness2(themeColor, -25);
            var cornerRadius1:Number = Math.max(cornerRadius - 1, 0);
            var cr:Object = { tl: 0, tr: cornerRadius, bl: 0, br: cornerRadius };
            var cr1:Object = { tl: 0, tr: cornerRadius1, bl: 0, br: cornerRadius1 };
            var arrowOnly:Boolean = true;
 
            // If our name doesn't include "editable", we are drawing the non-edit
            // skin which spans the entire control
            if (name.indexOf("editable") < 0) {
                arrowOnly = false;
                cr.tl = cr.bl = cornerRadius;
                cr1.tl = cr1.bl = cornerRadius1;
            }
 
            var g:Graphics = graphics;
            g.clear();
 
            // Draw the border and fill.
            switch (name) {
                case "upSkin":
                case "editableUpSkin": {
                       var upFillColors:Array = [ fillColors[0], fillColors[1] ];
                       var upFillAlphas:Array = [ fillAlphas[0], fillAlphas[1] ];
 
                    // border
                    drawRoundRect(0, 0, w, h, cr,
                        [ borderColor, borderColorDrk1 ], 1,
                        verticalGradientMatrix(0, 0, w, h),
                        GradientType.LINEAR, null,
                        { x: 1, y: 1, w: w - 2, h: h - 2, r: cr1 });
 
                    // button fill
                    drawRoundRect(1, 1, w - 2, h - 2, cr1,
                        upFillColors, upFillAlphas,
                        verticalGradientMatrix(1, 1, w - 2, h - 2));
 
                    // top highlight
                    drawRoundRect(1, 1, w - 2, (h - 2) / 2,
                        { tl: cornerRadius1, tr: cornerRadius1, bl: 0, br: 0 },
                        [ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
                        verticalGradientMatrix(1, 1, w - 2, (h - 2) / 2));
 
                    break;
                }
 
                case "overSkin":
                case "editableOverSkin": {
                    var overFillColors:Array;
                    if (fillColors.length > 2) {
                        overFillColors = [ fillColors[2], fillColors[3] ];
                    } else {
                        overFillColors = [ fillColors[0], fillColors[1] ];
                    }
 
                    var overFillAlphas:Array;
                    if (fillAlphas.length > 2) {
                        overFillAlphas = [ fillAlphas[2], fillAlphas[3] ];
                      } else {
                        overFillAlphas = [ fillAlphas[0], fillAlphas[1] ];
                      }
 
                    // border
                    drawRoundRect(0, 0, w, h, cr,
                        [ themeColor, themeColorDrk1 ], 1,
                        verticalGradientMatrix(0, 0, w, h),
                        GradientType.LINEAR, null,
                        { x: 1, y: 1, w: w - 2, h: h - 2, r: cr1 });
 
                    // button fill
                    drawRoundRect(1, 1, w - 2, h - 2, cr1,
                        overFillColors, overFillAlphas,
                        verticalGradientMatrix(1, 1, w - 2, h - 2));
 
                    // top highlight
                    drawRoundRect(1, 1, w - 2, (h - 2) / 2,
                        { tl: cornerRadius1, tr: cornerRadius1, bl: 0, br: 0 },
                        [ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
                        verticalGradientMatrix(0, 0, w - 2, (h - 2) / 2));
 
                    break;
                }
 
                case "downSkin":
                case "editableDownSkin": {
                    // border
                    drawRoundRect(0, 0, w, h, cr,
                        [ themeColor, themeColorDrk1 ], 1,
                        verticalGradientMatrix(0, 0, w, h));
 
                    // button fill
                    drawRoundRect(1, 1, w - 2, h - 2, cr1,
                        [ derStyles.fillColorPress1, derStyles.fillColorPress2 ], 1,
                        verticalGradientMatrix(1, 1, w - 2, h - 2));
 
                    // top highlight
                    drawRoundRect(1, 1, w - 2, (h - 2) / 2,
                        { tl: cornerRadius1, tr: cornerRadius1, bl: 0, br: 0 },
                        [ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
                        verticalGradientMatrix(1, 1, w - 2, (h - 2) / 2));
 
                    break;
                }
 
                case "disabledSkin":
                case "editableDisabledSkin": {
                       var disFillColors:Array = [ fillColors[0], fillColors[1] ];
                    var disFillAlphas:Array = [ Math.max(0, fillAlphas[0] - 0.15), Math.max(0, fillAlphas[1] - 0.15) ];
 
                    // border
                    drawRoundRect(0, 0, w, h, cr,
                        [ borderColor, borderColorDrk1 ], 0.5,
                        verticalGradientMatrix(0, 0, w, h ),
                        GradientType.LINEAR, null,
                        { x: 1, y: 1, w: w - 2, h: h - 2, r: cr1 });
 
                    // button fill
                    drawRoundRect(1, 1, w - 2, h - 2, cr1,
                        disFillColors, disFillAlphas,
                        verticalGradientMatrix(0, 0, w - 2, h - 2));
 
                    arrowColor = getStyle("disabledIconColor");
                    break;
                }
            }
 
            // Draw the triangle.
            g.beginFill(arrowColor);
            g.moveTo(w - 11.5, h / 2 + 3);
            g.lineTo(w - 15, h / 2 - 2);
            g.lineTo(w - 8, h / 2 - 2);
            g.lineTo(w - 11.5, h / 2 + 3);
            g.endFill();
        }
    }
}

[GoogleAdsWide]

You can also set the skin style in an external .CSS file or <Style/> block, as seen in the following example:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/23/removing-the-vertical-separator-from-the-combobox-control-in-flex/ -->
<mx:Application name="ComboBox_skin_test"
        xmlns:mx="http://www.adobe.com/2006/mxml"
        backgroundColor="white">
 
    <mx:Style>
        ComboBox {
            skin: ClassReference("skins.CustomComboBoxSkin");
        }
    </mx:Style>
 
    <mx:ComboBox id="comboBox"
            dataProvider="[The,Quick,Brown,Fox,Jumps,Over,The,Lazy,Dog]" />
 
</mx:Application>

Or, you can set the skin style using ActionScript, as seen in the following example:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/23/removing-the-vertical-separator-from-the-combobox-control-in-flex/ -->
<mx:Application name="ComboBox_skin_test"
        xmlns:mx="http://www.adobe.com/2006/mxml"
        backgroundColor="white">
 
    <mx:Script>
        <![CDATA[
            import skins.CustomComboBoxSkin;
 
            protected function btn_click(evt:MouseEvent):void {
                comboBox.setStyle("skin", CustomComboBoxSkin);
            }
        ]]>
    </mx:Script>
 
    <mx:ApplicationControlBar dock="true">
        <mx:Button id="btn"
                label="Set skin"
                click="btn_click(event);" />
    </mx:ApplicationControlBar>
 
    <mx:ComboBox id="comboBox"
            dataProvider="[The,Quick,Brown,Fox,Jumps,Over,The,Lazy,Dog]" />
 
</mx:Application>

6 thoughts on “Removing the vertical separator from the ComboBox control in Flex

  1. Thanks for this example. I spent a little bit working on a similar problem: how to remove the gradient border from the border of a combobox. In this process I found that this is a copy of the ComboBoxArrowSkin with a couple minor changes. What I ended up doing was just copy/pasting the ComboBoxArrowSkin class in flex_trunk/frameworks/projects/framework/src/mx/skins/halo and modifying it so that it didn’t apply a gradient to the border. But I wouldn’t have thought of doing that if it wasn’t for this example. Cheers!

  2. I think in one of the Adobe TV clips they showed that if you create a folder structure like this in your project’s src folder: src/mx/skins/halo and put your classes in there, then you wouldn’t have to modify the framework directly. Don’t hold me to that, though.

  3. Nice post, solved my problem and got me on the path to creating a totally custom combobox skin which I needed for my current project.

  4. This is excellent!

    What if I only want to change the background gradient or border color? But want to leave the seperator? (Which part of this code removes the seperator?)

    Thanks! :)

  5. I found out how to make the changes I wanted after all. I knew there should be a VERY SIMPLE way to do it, but scouring the net did not give me the answers until I stumbled across it in your code. Thanks!

    The answer, in my case (change only border color and background-gradient) was to assign a themeColor to the components, which I did in the CSS, like this:
    global {
    theme-color: #7ab800;
    }

Comments are closed.