【问题标题】:Datagrid sorting only on click of specific part in headerRendererDatagrid 仅在单击 headerRenderer 中的特定部分时排序
【发布时间】:2011-06-21 09:38:08
【问题描述】:

我有一个带有 HBox 的自定义 FilterColumnHeaderRenderer,在列标题的标签内,用于过滤器文本的 Textinput 和用于清除过滤器的标签。现在我希望仅在单击标题时进行列排序。

目前我对整个 columnHeader 使用 HeaderRelease 上的 sortCompareFunction。

你将如何解决这个要求?

完整代码,SearchableDataGrid:

package components{
import flash.events.TextEvent;

import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.core.ClassFactory;
import mx.events.DataGridEvent;
import mx.formatters.DateFormatter;
import mx.utils.ObjectUtil;

[Event(name="itemsFiltered", type="components.SearchableDataGridFilterEvent")]
public class SearchableDataGrid extends DataGrid{
    private var _searchableDataProvider:ArrayCollection;
    private var _filterStrings:Object = new Object();
    private var _dataTypes:Object = new Object();
    private var _fieldFocus:String = "";
    private var _totalItems:int = 0;
    private var _dataFormatter:DateFormatter = null;
    public var _reloadQuery:Boolean = false;

    public function SearchableDataGrid(){
        super();
        init();
    }

    private function init():void{
        //initialise a standart DateFormatter
        var ft:DateFormatter = new DateFormatter();
        ft.formatString = "YYYY-MM-DD";
        this.dateFormatter = ft;    
    }

    private function numericSortByField(fieldName:String):Function { 
        return function(obj1:Object, obj2:Object):int  {  
            var testFlag1:Boolean = isNaN( Number( obj1[fieldName] ) ); 
            var testFlag2:Boolean = isNaN( Number( obj2[fieldName] ) ); 
            // if one value is not a number => string compare
            if (testFlag1 || testFlag2){
                var value1:String = (obj1[fieldName] == '' || obj1[fieldName] == null) ? null : new String(obj1[fieldName]);
               var value2:String = (obj2[fieldName] == '' || obj2[fieldName]== null) ? null : new String(obj2[fieldName]);
               return ObjectUtil.stringCompare( value1, value2, true );
            }else{
                var value1Number:Number = (obj1[fieldName] == '' || obj1[fieldName] == null) ? null : new Number(obj1[fieldName]);       
                var value2Number:Number = (obj2[fieldName] == '' || obj2[fieldName] == null) ? null : new Number(obj2[fieldName]);      
                return ObjectUtil.numericCompare(value1Number, value2Number);
            } 
        } 
    }

    private function setHeaderRenderer():void{
        var hr:FilterHeaderRendererFactory = new FilterHeaderRendererFactory(this);
        for(var i:int = 0; i < super.columns.length; i++){
            super.columns[i].showDataTips = true;
            super.columns[i].headerRenderer = hr;
            super.columns[i].itemRenderer = new ClassFactory(components.SearchableDGItemRenderer);;
            super.columns[i].sortCompareFunction = this.numericSortByField(super.columns[i].dataField);
        }
    }

    private function dataBindingChanged():void{
        if (_reloadQuery == false){
            _filterStrings = new Object();
        }else{
            Alert.show("You are reloading the same query, your filter strings are reapplied", "Information");
        }
        _dataTypes = new Object();
        _fieldFocus = "";
        setHeaderRenderer();
        prepareDataFilter();
    }


    public function set searchableDataProvider(val:ArrayCollection):void{
        this._searchableDataProvider = val;
        _totalItems = val.length;

        this.dataProvider = this._searchableDataProvider;
        dataBindingChanged();
    }


    private function prepareDataFilter():void{
        this._searchableDataProvider.filterFunction = dataFilter;
        this._searchableDataProvider.refresh();
        var ev:SearchableDataGridFilterEvent = new SearchableDataGridFilterEvent(this._searchableDataProvider.length, this._totalItems, this.filterStrings);
        this.dispatchEvent(ev);
    }

    private function dataFilter(item:Object):Boolean{
            var isMatch:Boolean = true;

            for (var field:String in _filterStrings){
                var sP:String = _filterStrings[field]; //searchpattern
                if(sP == null){
                    continue;
                }

                if(item[field] == null){
                    isMatch = false;
                    return isMatch;
                }

                var pattern:RegExp = new RegExp("^" + sP.toLowerCase().replace(new RegExp(/%/g), ".*"));
                if(item[field] is Date){
                    //special check for date columns
                    if(!pattern.test(this.dateFormatter.format(item[field] as Date)))
                    {
                        isMatch = false;
                        return isMatch;
                    }
                }else{
                    //its not a date column
                    if(!pattern.test((item[field].toString()).toLowerCase()))
                    {
                        isMatch = false;
                        return isMatch;
                    }
                }
            }
            return isMatch;
        }

    public function get filterStrings():Object{
        return this._filterStrings;
    }

    public function setFilterString(fieldName:String, value:String):void{
        this._filterStrings[fieldName] = value;

        if(value == ""){
            delete this._filterStrings[fieldName];
        }
        _fieldFocus = fieldName;
        prepareDataFilter();
    }

    public function getFilterString(fieldName:String):String{
        if(this._filterStrings[fieldName] == null){
            return "";
        }
        return this._filterStrings[fieldName];
    }

    public function setDataType(fieldName:String, value:String):void{
        this._dataTypes[fieldName] = value;
    }

    public function getDataType(fieldName:String):String{
        if(this._filterStrings[fieldName] == null){
            return "";
        }
        return this._filterStrings[fieldName];
    }

    public function getFieldFocus():String{
        return this._fieldFocus;
    }

    public function set dateFormatter(val:DateFormatter):void{
        this._dataFormatter = val;
    }

    public function get dateFormatter():DateFormatter{
        return this._dataFormatter;
    }
}

}

完整代码,FilterColumnHeaderRenderer:

package components{
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.TextField;
import flash.ui.Keyboard;

import mx.containers.HBox;
import mx.containers.Tile;
import mx.containers.VBox;
import mx.controls.Button;
import mx.controls.Label;
import mx.controls.LinkButton;
import mx.controls.TextInput;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.events.DataGridEvent;
import mx.events.FlexEvent;

public class FilterColumnHeaderRenderer extends VBox{
    private var title:Label;
    private var filter:TextInput;
    private var clearFilter:Label;
    private var filterHBox:HBox;
    private var tfHasFocus:Boolean = false;
    private var dataFieldName:String;
    private var dataObj:DataGridColumn;
    private var _dataGrid:SearchableDataGrid;

    public function FilterColumnHeaderRenderer(){
        super();
        this.verticalScrollPolicy = "off";
        this.horizontalScrollPolicy = "off";

        this.addEventListener(FlexEvent.CREATION_COMPLETE, creationComplete);

        title = new Label();
        filter = new TextInput();
        clearFilter  = new Label();
        filterHBox  = new HBox();

        title.percentWidth = 100;
        filterHBox.percentWidth = 100;

        clearFilter.width = 10;
        clearFilter.toolTip = "Clear the filter for this column"
        clearFilter.htmlText = "x";     

        this.addChildAt(title, 0);
        filterHBox.addChildAt(clearFilter, 0);
        filterHBox.addChildAt(filter, 1);
        this.addChildAt(filterHBox, 1);

        clearFilter.addEventListener(MouseEvent.CLICK, resetFilter);

        filter.addEventListener(FocusEvent.FOCUS_IN, hasFocus);
        filter.addEventListener(FocusEvent.FOCUS_OUT, lostFocus);
        filter.addEventListener(TextEvent.TEXT_INPUT, textChange);
        filter.addEventListener(KeyboardEvent.KEY_DOWN, textKeyDown);

        title.addEventListener(DataGridEvent.HEADER_RELEASE, onHeaderRelease);

    }

    private function onHeaderRelease( event:DataGridEvent ): void {
        var rdr:FilterColumnHeaderRenderer = event.itemRenderer as FilterColumnHeaderRenderer;
        var dataGrid:SearchableDataGrid = SearchableDataGrid(event.target);
        var dataField:String = event.dataField;
        var columnIndex:int = event.columnIndex;
    }

    private function resetFilter(event:MouseEvent):void{
        var tmpFieldName:String = event.currentTarget.parent.parent.fieldName;
        this._dataGrid.setFilterString(dataFieldName, "");
        this.filter.text = "";
    }

    private function textKeyDown(event:KeyboardEvent):void{
        if(this._dataGrid){
            if(event.keyCode == flash.ui.Keyboard.BACKSPACE){
                var actString:String = this.filter.text;
                this._dataGrid.setFilterString(dataFieldName, actString.substr(0, (actString.length > 0 ? actString.length - 1 : 0)));
            }
        }
    }

    private function creationComplete(event:FlexEvent):void{
        if(this._dataGrid != null && this._dataGrid.getFieldFocus() == dataFieldName){
            this.filter.setFocus();
        }
    }

    public function textChange(event:TextEvent):void{
        if(this._dataGrid)
        {
            this._dataGrid.setFilterString(dataFieldName, this._dataGrid.getFilterString(dataFieldName) + event.text);
        }
    }

    private function hasFocus(event:FocusEvent):void{
        tfHasFocus = true;
    }

    private function lostFocus(event:FocusEvent):void{
        tfHasFocus = false;
    }

    override public function set data(value:Object):void{
        dataObj = (value as DataGridColumn);

        dataFieldName = dataObj.dataField;

        this.title.text = dataObj.headerText;
        this.title.toolTip = dataObj.headerText;

        if(this._dataGrid)
        {
            var fs:String = this._dataGrid.filterStrings[dataFieldName];
            if(fs != null){
                this.filter.text = fs;
            }
        }

        this.dispatchEvent(new FilterColumnHeaderRendererCreatedEvent(this, true));
    }


    public function get hasTextFocus():Boolean{
        return this.tfHasFocus;
    }

    public function get fieldName():String{
        return this.dataFieldName;
    }

    public function setText(newText:String):void{
        this.filter.text = newText;
    }

    public function set dataGrid(value:SearchableDataGrid):void{
        this._dataGrid = value;
    }

    public function get dataGrid():SearchableDataGrid{
        return this._dataGrid;
    }   
}

}

完整代码,FilterHeaderRendererFactory:

package components{
import mx.controls.DataGrid;
import mx.core.ClassFactory;
import mx.core.IFactory;

public class FilterHeaderRendererFactory implements IFactory{
    private var _base_factory:ClassFactory;
    private var _grid:SearchableDataGrid;


    public function FilterHeaderRendererFactory(grid:SearchableDataGrid){
        _base_factory = new ClassFactory(FilterColumnHeaderRenderer);
        _grid = grid;
    }

    public function newInstance():*{
        var o:FilterColumnHeaderRenderer = _base_factory.newInstance() as FilterColumnHeaderRenderer;
        o.dataGrid = _grid;
        return o;
    }
}

}

【问题讨论】:

    标签: apache-flex events sorting datagrid header


    【解决方案1】:

    从概念上讲;我会这样做:

    从您的自定义标头调度自定义事件。确保它起泡。在 DataGrid 上侦听该事件,然后直接在 sort your dataProvider 上侦听,而无需使用内置的 DataGrid 功能。

    【讨论】:

    • 我明白了。如果我直接对 dataProvider 进行排序,如何重新实现显示排序方向的小三角形,更重要的是重用自定义 sortCompareFunction,它能够将数字排序为数字而不是字符串?
    • 我不确定三角形的事情。 sortCompareFunction 本质上应该是可重用的。您在尝试这样做时遇到问题吗?
    • 我能够重用我的自定义排序函数:sortField.compareFunction = numericSortByField(dataField);关于三角形的任何想法?让我谷歌一下;)
    【解决方案2】:

    我终于实现了这个解决方案:

    • 在 HeaderRenderer 中,我已将组件的类型从标签更改为按钮。
    • 因此我能够添加事件侦听器,以便在此按钮上聚焦和聚焦。
    • 由于事件的焦点发生在标头释放事件之前,我可以设置一个布尔值来检查标头释放事件(排序)是否应该发生

    请随时询问更准确的信息。这里是相关的源代码:

    完整代码,FilterColumnHeaderRenderer:

                    package components
    {
        import flash.events.Event;
        import flash.events.FocusEvent;
        import flash.events.KeyboardEvent;
        import flash.events.MouseEvent;
        import flash.events.TextEvent;
        import flash.text.TextField;
        import flash.ui.Keyboard;
    
        import mx.containers.HBox;
        import mx.containers.Tile;
        import mx.containers.VBox;
        import mx.controls.Button;
        import mx.controls.Label;
        import mx.controls.LinkButton;
        import mx.controls.TextInput;
        import mx.controls.dataGridClasses.DataGridColumn;
        import mx.events.DataGridEvent;
        import mx.events.FlexEvent;
    
    
        public class FilterColumnHeaderRenderer extends VBox{
            private var title:Label;
            private var filter:TextInput;
            private var clearFilter:Button;
            private var filterHBox:HBox;
            private var tfHasFocus:Boolean = false;
            private var dataFieldName:String;
            private var dataObj:DataGridColumn;
            private var _dataGrid:SearchableDataGrid;
    
            [ Embed ( source = "/icons/icon-x.gif" ) ]
            public var iconDelete:Class; 
    
    
            public function FilterColumnHeaderRenderer(){
                super();
                this.verticalScrollPolicy = "off";
                this.horizontalScrollPolicy = "off";
    
                this.addEventListener(FlexEvent.CREATION_COMPLETE, creationComplete);
    
                title = new Label();
                filter = new TextInput();
                clearFilter  = new Button();
                filterHBox  = new HBox();
    
                title.percentWidth = 100;
                filterHBox.percentWidth = 100;
                filterHBox.setStyle( "horizontalGap", 5 );
    
                clearFilter.toolTip = "Clear the filter for this column"
                clearFilter.width = 15;
                clearFilter.height = 20;
                clearFilter.label = "x";    
                clearFilter.setStyle ( "fontWeight", "normal" ) ;
                clearFilter.setStyle ( "textPadding", 0 ) ;
                clearFilter.setStyle ( "paddingLeft", 0 ) ;
                clearFilter.setStyle ( "paddingRight", 0 ) ;
                clearFilter.setStyle ( "paddingTop", 0 ) ;
                clearFilter.setStyle ( "paddingBottom", 0 ) ;
                clearFilter.addEventListener(FocusEvent.FOCUS_IN, hasFocus);
                clearFilter.addEventListener(FocusEvent.FOCUS_OUT, lostFocus);
    
    
                filter.addEventListener(FocusEvent.FOCUS_IN, hasFocus);
                filter.addEventListener(FocusEvent.FOCUS_OUT, lostFocus);
                filter.addEventListener(TextEvent.TEXT_INPUT, textChange);
                filter.addEventListener(KeyboardEvent.KEY_DOWN, textKeyDown);
    
                clearFilter.addEventListener(MouseEvent.CLICK, resetFilter);
    
                this.addChildAt(title, 0);
                filterHBox.addChildAt(clearFilter, 0);
                filterHBox.addChildAt(filter, 1);
                this.addChildAt(filterHBox, 1);
            }
    
            private function resetFilter(event:MouseEvent):void{
                var tmpFieldName:String = event.currentTarget.parent.parent.fieldName;
                this._dataGrid.setFilterString(dataFieldName, "");
                this.filter.text = "";
            }
    
            private function textKeyDown(event:KeyboardEvent):void{
                if(this._dataGrid){
                    if(event.keyCode == flash.ui.Keyboard.BACKSPACE){
                        var actString:String = this.filter.text;
                        this._dataGrid.setFilterString(dataFieldName, actString.substr(0, (actString.length > 0 ? actString.length - 1 : 0)));
                    }
                }
            }
    
            private function creationComplete(event:FlexEvent):void{
                if(this._dataGrid != null && this._dataGrid.getFieldFocus() == dataFieldName){
                    this.filter.setFocus();
                }
            }
    
            public function textChange(event:TextEvent):void{
                if(this._dataGrid)
                {
                    this._dataGrid.setFilterString(dataFieldName, this._dataGrid.getFilterString(dataFieldName) + event.text);
                }
            }
    
            private function hasFocus(event:FocusEvent):void{
                tfHasFocus = true;
            }
    
            private function lostFocus(event:FocusEvent):void{
                tfHasFocus = false;
            }
    
            override public function set data(value:Object):void{
                dataObj = (value as DataGridColumn);
    
                dataFieldName = dataObj.dataField;
    
                this.title.text = dataObj.headerText;
                this.title.toolTip = dataObj.headerText;
    
                if(this._dataGrid)
                {
                    var fs:String = this._dataGrid.filterStrings[dataFieldName];
                    if(fs != null){
                        this.filter.text = fs;
                    }
                }
                this.dispatchEvent(new FilterColumnHeaderRendererCreatedEvent(this, true));
            }
    
    
            public function get hasTextFocus():Boolean{
                return this.tfHasFocus;
            }
    
            public function get fieldName():String{
                return this.dataFieldName;
            }
    
            public function setText(newText:String):void{
                this.filter.text = newText;
            }
    
            public function set dataGrid(value:SearchableDataGrid):void{
                this._dataGrid = value;
            }
    
            public function get dataGrid():SearchableDataGrid{
                return this._dataGrid;
            }   }}
    

    searchableDataGrid的相关代码:

    package components{
    import flash.events.TextEvent;
    
    import mx.collections.*;
    import mx.controls.Alert;
    import mx.controls.DataGrid;
    import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
    import mx.controls.dataGridClasses.DataGridColumn;
    import mx.core.ClassFactory;
    import mx.events.DataGridEvent;
    import mx.formatters.DateFormatter;
    import mx.utils.ObjectUtil;
    
    [Event(name="itemsFiltered", type="components.SearchableDataGridFilterEvent")]
    public class SearchableDataGrid extends DataGrid{
        private var _searchableDataProvider:ArrayCollection;
        private var _filterStrings:Object = new Object();
        private var _dataTypes:Object = new Object();
        private var _fieldFocus:String = "";
        private var _totalItems:int = 0;
        private var _dataFormatter:DateFormatter = null;
        public var _reloadQuery:Boolean = false;
    
        public function SearchableDataGrid(){
            super();
            init();
        }
    
        private function init():void{
            //initialise a standart DateFormatter
            var ft:DateFormatter = new DateFormatter();
            ft.formatString = "YYYY-MM-DD";
            this.dateFormatter = ft;
            this.addEventListener(DataGridEvent.HEADER_RELEASE, onHeaderRelease);
        }
        private function onHeaderRelease( event:DataGridEvent ): void {
            var dataGrid:SearchableDataGrid = SearchableDataGrid(event.target);
            var dataField:String = event.dataField;
            var columnIndex:int = event.columnIndex;
    
            var rdr:FilterColumnHeaderRenderer = event.itemRenderer as FilterColumnHeaderRenderer;
            if(rdr.hasTextFocus){
                event.preventDefault();
            }
        }
    (...)
    

    【讨论】:

      【解决方案3】:

      感谢这里的所有其他 cmets,我有了一个更优雅的解决方案。

      1. 我在数据网格中附加了一个 headerRelease 事件监听器

        &lt;mx:Datagrid headerRelease="onHeaderRelease(event)"&gt;

      2. 在事件监听器中,我检查了哪个 InteractiveObject 具有焦点

      3. 如果焦点对象不是数据网格,我停止了事件

        protected function onHeaderRelease(e:DataGridEvent):void {
            var interactive:InteractiveObject = focusManager.getFocus() as InteractiveObject
            var hasFocus:String = flash.utils.getQualifiedClassName(interactive).toString()
        
             if (hasFocus != "mx.controls::DataGrid") { // Can also be AdvancedDataGrid
                 e.stopImmediatePropagation();  // Use this as needed
                 e.preventDefault(); // Mandatory
            }
        }
        

      我希望这对发现自己在这里的下一个可怜的灵魂有所帮助。

      【讨论】:

        猜你喜欢
        • 2013-05-21
        • 2023-04-11
        • 1970-01-01
        • 2013-10-29
        • 2015-04-06
        • 2014-10-09
        • 1970-01-01
        • 1970-01-01
        • 2013-02-11
        相关资源
        最近更新 更多