Datagrid仅在headerRenderer中单击特定部分时进行排序

时间:2011-06-21 09:38:08

标签: flex events sorting datagrid header

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

目前我在HeaderRelease上使用sortCompareFunction作为整个columnHeader。

你会如何解决这个问题?

完整代码,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;
    }
}

}

3 个答案:

答案 0 :(得分:1)

概念上;我这样做:

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

答案 1 :(得分:1)

最后我实施了这个解决方案:

  • 在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();
        }
    }
(...)

答案 2 :(得分:0)

由于此处的所有其他评论,我有一个更优雅的解决方案。

  1. 我将headerRelease事件侦听器附加到datagrid

    <mx:Datagrid headerRelease="onHeaderRelease(event)">

  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
        }
    }
    
  4. 我希望这有助于下一个发现自己的穷人。