views:

25

answers:

2

I need a control in Flex 3 that is like NumericStepper, but that can display arbitrary strings. Does this control exist? If not, what are your suggestions for creating it, or references you would recommend?

For convenience, I'm calling this a TextStepper. I want this as a compact way to display a list of string choices that a user can cycle through by clicking the up/down buttons. Compact means no drop-down or pop-up aspects of the control: the only way to change the selected index is to click the up/down button (which updates the text input value). Value cycling means that I really want to treat the underlying dataProvider as a circular buffer. So up/down clicks modify selectedIndex in modulo fashion.

A: 

The idea is to use valueFormatFunction:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:local="*">

    <local:StringStepper horizontalCenter="0" verticalCenter="0" width="200">
        <local:dataProvider>
            <s:ArrayCollection>
                <fx:String>Hello!</fx:String>
                <fx:String>I love you.</fx:String>
                <fx:String>Won't you tell me your name?</fx:String>
            </s:ArrayCollection>
        </local:dataProvider>
    </local:StringStepper>

</s:Application>

Source for StringStepper:

package
{
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;

import spark.components.NumericStepper;

public class StringStepper extends NumericStepper
{
    public function StringStepper()
    {
        enabled = false;
        valueFormatFunction = defaultValueFormatFunction;
        valueParseFunction = defaultValueParseFunction;
    }

    private var _dataProvider:ArrayCollection;

    public function get dataProvider():ArrayCollection
    {
        return _dataProvider;
    }

    public function set dataProvider(value:ArrayCollection):void
    {
        if (_dataProvider == value)
            return;

        if (_dataProvider)
            _dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,
                dataProvider_collectionChangeHandler);

        _dataProvider = value;
        commitDataProvider();

        if (_dataProvider)
            _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,
                dataProvider_collectionChangeHandler);
    }

    /**
     * Same event as for <code>value</code>.
     */
    [Bindable("valueCommit")]
    public function get selectedItem():Object
    {
        return _dataProvider && value <= _dataProvider.length - 1 ? _dataProvider[value] : null; 
    }

    public function set selectedItem(value:Object):void
    {
        if (!_dataProvider)
            return;

        value = _dataProvider.getItemIndex(value);
    }

    private function defaultValueFormatFunction(value:Number):String
    {
        return _dataProvider && value <= _dataProvider.length - 1 ? _dataProvider[value] : String(value);
    }

    private function defaultValueParseFunction(value:String):Number
    {
        if (!_dataProvider)
            return 0;

        var n:int = _dataProvider.length;
        for (var i:int = 0; i < n; i++)
        {
            var string:String = _dataProvider[i];
            if (string == value)
                return i;
        }
        return 0;
    }

    private function commitDataProvider():void
    {
        if (!_dataProvider)
        {
            minimum = 0;
            maximum = 0;
            enabled = false;
            return;
        }

        enabled = true;
        minimum = 0;
        maximum = _dataProvider.length - 1;
    }

    private function dataProvider_collectionChangeHandler(event:CollectionEvent):void
    {
        commitDataProvider();
    }

}
}
Maxim Kachurovskiy
Maxim, how does this change for Flex 3?
Andrew B.
For Flex 3 the easiest way is described in Robusto's comment. NumericStepper in Flex 3 is not customizable in my way.
Maxim Kachurovskiy
A: 

I created one of these (as an MXML comoponent) by overlaying a TextInput over a NumericStepper (absolutely positioned) so that the TextInput covered the input portion of the NumericStepper.

The dataProvider was an ArrayCollection of strings, and the value of the NumericStepper was used to access an index in the ArrayCollection.

The change event of the NumericStepper changed the text of the TextInput to whatever was at index n of the dataProvider. I gave the component an editable property, which set the TextInput to editable and inserted the new string into the dataProvider at the current index.

Robusto