2011-06-21 22 views
2

我有一个自定义的FilterColumnHeaderRenderer带有一个HBox,在一个标题的列标题,一个Textinput为过滤器文本和Label来清除过滤器。现在我希望只在点击标题时进行列分类。Datagrid排序只在单击headerRenderer中的特定部分

目前我使用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; 
    } 
} 

}

回答

1

概念上;我会这样做:

从您的自定义标题发送自定义事件。确保它泡沫。在DataGrid上监听该事件,然后直接使用sort your dataProvider,而不使用内置的DataGrid功能。

+0

我明白了。如果我直接对dataProvider进行排序,那么如何重新实现显示排序方向的小三角,并且更重要的是重用一个自定义的sortCompareFunction,它可以将数字排序为数字而不是字符串? – Werner

+0

我不确定三角形的事情。 sortCompareFunction应该是固有的可重用的。试图这样做有问题吗? – JeffryHouser

+0

我能够重用我的自定义排序功能:sortField.compareFunction = numericSortByField(dataField); 任何关于三角形的事情?让我谷歌一点;) – Werner

1

最后我实现此解决方案:

  • 所述的HeaderRenderer我已经改变了成分的种类从标签到按钮内。
  • 因此,我能够添加事件侦听器,以便将焦点放在此按钮上并专注于此按钮。
  • 作为事件焦点的头释放事件之前发生,我可以设置一个布尔值,检查如果头释放事件(排序)应该发生或不

请随时要求更精确的信息。这里的相关源代码:

完整的代码,FilterColumnHeaderRenderer:在searchableDataGrid的

   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; 
     } }} 

相关代码:

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

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