代码之家  ›  专栏  ›  技术社区  ›  Wouter Coekaerts

“灵活”组合框中为空

  •  3
  • Wouter Coekaerts  · 技术社区  · 15 年前

    如何制作用户可以选择的组合框 null ?

    如果只创建一个组合框, 无效的 在数据提供程序中,值出现,但用户无法选择它:

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

    有没有一种方法可以使该空值可选?

    解决方法是向数据提供程序中添加一个不为空但“表示”为空的项;然后在每次访问组合框时在空和该对象之间进行映射。但这并不是一个很好的解决方案;在所有访问“nullable”组合框的代码中,您必须始终记住这个映射…

    编辑:扩展我不喜欢变通方法的原因: 当然可以在子类中完成,但是我要么引入新的属性(比如 nullableSelectedItem );但是您必须小心,始终使用这些属性。或者我重写组合框 selectedItem 但是我很担心这会破坏基本类:它可能不喜欢某些东西改变它对当前所选项目来自内部的概念。即使是这个脆弱的黑客也能在 选择项目 dataProvider 这种无效也需要特别处理 data listData 对于渲染器,在 labelFunction ,然后它可能仍然暴露在组合框发送的事件中… 它可能会工作,但它是一个相当大的黑客,只是为了解决这样一个问题:如果用户点击项目,它不会被激活(对于其余的组合框处理空罚款)。 (另一种选择是让一个UI组件委托给一个组合框,但为了避免这个小问题,这甚至是更多的代码)

    5 回复  |  直到 10 年前
        1
  •  3
  •   Hrundik    15 年前

    以下解决方案可能是最简单的解决方案:

    <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;
        }
    
    }
    }
    
        2
  •  5
  •   rtemp    15 年前

    似乎正确管理空项的唯一方法是将项实际添加到组合框的数据提供程序中。下面的子类将自动处理这个问题。

    为了支持对数据提供程序的更改,在添加/删除项和完全重新分配数据提供程序本身方面(只需考虑绑定到远程服务响应的ArrayCollections),实现有点困难。

    package {

        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 UI的附加元素,我也不能手动监视每个集合以在每次集合刷新时创建空项。

    • 因为它必须处理集合的内容,所以它将在内部创建一个具有相同项实例和空实例的原始集合的副本,这样原始集合的实例就不会被触动,也可以在其他上下文中重用。

    • 它将监听原始数据提供程序上的更改,允许对其进行操作,甚至分配一个完全新的:将自动重新创建空项。

    • 您可以使用EmptyItem属性控制提供实际实例作为“空项”:这样可以使数据类型与集合的其余部分保持一致(如果使用类型化对象),或者在集合上定义自定义标签。

    一些使用它的示例代码…

        <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
  •  1
  •   David Salzer    15 年前

    不幸的是,这是不可能的。 然而,一个不会让您“必须记住此映射”的好解决方案是创建一个从ComboBox继承的类,该类具有自己的DataProvider属性。

    此属性设置器将处理空值,并在超级组合框类上对其进行表示。

        4
  •  0
  •   Cosma Colanicchia    15 年前

    一个非常简单但也非常有限的解决方案是添加prompt=“”属性。

    这将阻止组合框自动选择数据提供程序中的第一个项目,但一旦用户选择项目,将不再显示空行。

        5
  •  0
  •   DD84    10 年前

    RequireSelection=“false”将允许空值,如果需要,Prompt允许您输入用于该空值的文本。