Null в Flex ComboBox - PullRequest
       21

Null в Flex ComboBox

3 голосов
/ 04 июня 2009

Как сделать ComboBox, где пользователь может выбрать null?

Если вы просто создаете комбинированный список с null в поставщике данных, значение появляется, но пользователь не может выбрать его:

<mx:ComboBox id="cb" dataProvider="{[null, 'foo', 'bar']}" />

Есть ли способ сделать этот нуль выбираемым?

Обходной путь - добавить в dataProvider элемент, который не является нулевым, но «представляет» нулевой; и затем отображать между нулем и этим объектом каждый раз, когда вы получаете доступ к списку. Но это не совсем элегантное решение; вы всегда должны помнить об этом отображении во всем коде, который обращается к «обнуляемому» списку ...

Редактировать: подробнее, почему мне не нравится обходной путь: Конечно, это можно сделать в подклассе, но я либо ввожу новые атрибуты (например, nullableSelectedItem); но тогда вы должны быть осторожны, чтобы всегда использовать эти атрибуты. Или я перезаписываю ComboBoxes selectedItem; но я боюсь, что это сломает базовый класс: ему может не понравиться что-то, меняющее представление о том, что текущий выбранный элемент изнутри. И даже этот хрупкий хак работает, помимо selectedItem и dataProvider, этот nullItem также должен обрабатываться специальным образом в data и listData для средств визуализации, в labelFunction, а затем он, вероятно, все еще отображается в события, которые отправляет ComboBox ... Это может сработать, но это довольно хакерская задача, просто чтобы решить проблему, которая, если пользователь нажимает на элемент, он не активируется (в остальном ComboBox прекрасно обрабатывает ноль). (Другой альтернативой является делегирование компонента пользовательского интерфейса в ComboBox, но это еще больше кода, чтобы избежать этой маленькой проблемы)

Ответы [ 5 ]

5 голосов
/ 29 июля 2009

Кажется, что единственный способ правильно управлять пустым элементом - это фактически добавить элемент в поставщик данных в выпадающем списке. Следующий подкласс будет обрабатывать это автоматически.

Реализация немного сложна для поддержки изменений в поставщике данных с точки зрения добавления / удаления элементов и полного переназначения самого поставщика данных (просто подумайте о привязках коллекций массивов к ответу удаленных служб).

package {</p> <pre><code> import flash.events.Event; import flash.events.FocusEvent; import mx.collections.ArrayCollection; import mx.collections.ICollectionView; import mx.collections.IList; import mx.containers.FormItem; import mx.controls.ComboBox; import mx.events.CollectionEvent; import mx.events.ListEvent; import mx.validators.Validator; public class EmptyItemComboBox extends ComboBox { protected var _emptyItem:Object = null; protected var _originalDataProvider:ICollectionView = null; public function EmptyItemComboBox() { super(); addEmptyItem(); addEventListener(Event.CHANGE, onChange); } private function onChange(event:Event):void { dispatchEvent(new Event("isEmptySelectedChanged")); } [Bindable] public function get emptyItem():Object { return _emptyItem; } public function set emptyItem(value:Object):void { if (_emptyItem != value) { clearEmptyItem(); _emptyItem = value; addEmptyItem(); } } [Bindable(event="isEmptySelectedChanged")] public function get isEmptySelected():Boolean { return (selectedItem == null || (_emptyItem != null && selectedItem == _emptyItem)); } override public function set selectedItem(value:Object):void { if (value == null && emptyItem != null) { super.selectedItem = emptyItem; } else if (value != selectedItem) { super.selectedItem = value; } dispatchEvent(new Event("isEmptySelectedChanged")); } override public function set dataProvider(value:Object):void { if (_originalDataProvider != null) { _originalDataProvider.removeEventListener( CollectionEvent.COLLECTION_CHANGE, onOriginalCollectionChange); } super.dataProvider = value; _originalDataProvider = (dataProvider as ICollectionView); _originalDataProvider.addEventListener( CollectionEvent.COLLECTION_CHANGE, onOriginalCollectionChange) addEmptyItem(); } private function clearEmptyItem():void { if (emptyItem != null && dataProvider != null && dataProvider is IList) { var dp:IList = dataProvider as IList; var idx:int = dp.getItemIndex(_emptyItem); if (idx >=0) { dp.removeItemAt(idx); } } dispatchEvent(new Event("isEmptySelectedChanged")); } private function addEmptyItem():void { if (emptyItem != null) { if (dataProvider != null && dataProvider is IList) { var dp:IList = dataProvider as IList; var idx:int = dp.getItemIndex(_emptyItem); if (idx == -1) { var newDp:ArrayCollection = new ArrayCollection(dp.toArray().concat()); newDp.addItemAt(_emptyItem, 0); super.dataProvider = newDp; } } } dispatchEvent(new Event("isEmptySelectedChanged")); } private function onOriginalCollectionChange(event:CollectionEvent):void { if (_emptyItem != null) { dataProvider = _originalDataProvider; addEmptyItem(); } } } }

Некоторые заметки о классе:

  • Он автоматически вставит пустой объект в список. Это было серьезным требованием для моего сценария: поставщики данных были возвращены удаленными службами, и они не могли включать дополнительные элементы только для поддержки пользовательского интерфейса Flex, ни Я мог вручную наблюдать за каждой коллекцией, чтобы создавать пустые элементы при каждом обновлении коллекции.

  • Так как он должен работать с содержимым коллекции, он внутренне создаст копию оригинального экземпляра с теми же экземплярами элементов И пустым, так что экземпляр оригинальной коллекции не будет затронут и может быть повторно использован в другом контексте.

  • Он будет прослушивать изменения в оригинальном поставщике данных, позволяя работать с ним и даже назначать совершенно новый: пустой элемент будет воссоздан автоматически.

  • Вы управляете предоставлением фактического экземпляра для использования в качестве «пустого элемента» с атрибутом emptyItem: это позволяет поддерживать согласованный тип данных с остальной частью коллекции (если вы используете типизированные объекты), или определить собственную метку на нем.

Пример кода, использующего его ...

</p> <pre><code> <mx:Script> <![CDATA[ import mx.controls.Alert; ]]> </mx:Script> <mx:ArrayCollection id="myDP"> <mx:source> <mx:Array> <mx:Object value="1" label="First"/> <mx:Object value="2" label="Second"/> <mx:Object value="3" label="Third"/> </mx:Array> </mx:source> </mx:ArrayCollection> <local:EmptyItemComboBox id="comboBox" dataProvider="{myDP}" labelField="label"> <local:emptyItem> <mx:Object value="{null}" label="(select an item)"/> </local:emptyItem> </local:EmptyItemComboBox> <mx:Button label="Show selected item" click="Alert.show(comboBox.selectedItem.value)"/> <mx:Button label="Clear DP" click="myDP.removeAll()"/> <mx:Button label="Add item to DP" click="myDP.addItem({value: '4', label: 'Fourth'})"/> <mx:Button label="Reset DP" click="myDP = new ArrayCollection([{value: '1', label: 'First'}])"/> <mx:Label text="{'comboBox.isEmptySelected = ' + comboBox.isEmptySelected}"/> </mx:Application>

3 голосов
/ 04 июня 2009

Следующее решение, вероятно, самое простое:

<mx:ComboBox id="cb" dataProvider="{[{label:"", data:null}, {label:'foo', data:'foo'}, {label:'bar', data:'bar'}]}" />

и получить доступ к данным, используя cb.selectedItem.data

Однако это простое решение не является обязательным для исполнения, как упомянул Вутер.

Итак, вот более хитрое решение, которое позволит выбирать нулевые объекты :

<mx:ComboBox id="cb" dataProvider="{[null, 'foo', 'bar']}" dropdownFactory="{new ClassFactory(NullList)}" />

Где NullList - следующий класс:

package test
{
import mx.controls.List;

public class NullList extends List
{
    public function NullList()
    {
        super();
    }

    override public function isItemSelectable(data:Object):Boolean
    {
        if (!selectable)
            return false;
        return true;
    }

}
}
1 голос
/ 04 июня 2009

К сожалению, это невозможно. Однако хорошее решение, которое не заставит вас «помнить об этом отображении», - это создать класс, наследуемый от ComboBox, со своим собственным свойством DataProvider.

этот установщик свойств будет обрабатывать нулевые значения и иметь представление для него в классе Super ComboBox.

0 голосов
/ 10 июня 2014

requireSelection = "false" разрешит пустое значение, а запрос позволяет вам ввести текст, используемый для этого пустого значения, если хотите.

0 голосов
/ 08 июня 2009

Очень простое, но очень ограниченное решение - добавить атрибут prompt = "" ..

Это не даст ComboBox автоматически выбрать первый элемент в dataProvider, но как только пользователь выберет элемент, пустая строка больше не будет отображаться.

...