package {
import mx.collections.ICollectionView;
import mx.collections.IList;
import mx.collections.ISort;
import mx.collections.ISortField;
import mx.collections.Sort;
import mx.styles.AdvancedStyleClient;
import spark.components.DataGrid;
import spark.components.Grid;
import spark.components.gridClasses.GridColumn;
import spark.components.gridClasses.GridSortField;
import spark.events.GridEvent;
import spark.events.GridSortEvent;
/**
* Multi-column sortable data grid class.
* Extends spark data grid and override columnHeaderGroup_clickHandler function.
* Provide multi-column sort function when ctrl + left click on column header.
*/
public class MultiColSortableDataGrid extends DataGrid {
/**
* @private
*/
override protected function columnHeaderGroup_clickHandler(event:GridEvent):void {
const column:GridColumn = event.column;
var columnIndices:Vector.<int>;
if (!enabled || !sortableColumns || !column || !column.sortable)
return;
columnIndices = Vector.<int>([column.columnIndex]);
// If the sort isn't cancelled, will also update the columnHeaderGroup
// visibleSortIndiciatorIndices.
sortByMultiColumns(columnIndices, true, event.ctrlKey);
}
/**
* Copy and modify from DataGrid::sortByColumns function.
* Add one more argument multiColSort, decide if continue to use old sort fields.
* Achieve multi-column sort function.
*/
public function sortByMultiColumns(columnIndices:Vector.<int>,
isInteractive:Boolean = false , multiColSort:Boolean = false):Boolean {
const dataProvider:ICollectionView = this.dataProvider as ICollectionView;
if (!dataProvider)
return false;
var sort:ISort = dataProvider.sort;
if (sort) {
sort.compareFunction = null;
} else {
sort = new Sort();
}
var sortFields:Array = createSortFields(columnIndices, sort.fields, isInteractive);
if (!sortFields)
return false;
var oldSortFields:Array = (dataProvider.sort) ? dataProvider.sort.fields : null;
// implements multi-column sort function.
if (multiColSort && oldSortFields) {
if (columnHeaderGroup) {
var colSortIndices:Vector.<int> = columnHeaderGroup.visibleSortIndicatorIndices;
for (var j:int = colSortIndices.length - 1 ; j >= 0 ; --j) {
var idx:int = colSortIndices[j];
if (columnIndices.indexOf(idx) < 0) {
columnIndices.unshift(idx);
}
}
}
outer: for (var i:int = oldSortFields.length - 1 ; i >= 0 ; --i) {
var oldField:ISortField = oldSortFields[i] as ISortField;
for (var k:int = 0 ; k < sortFields.length ; ++k) {
var newField:ISortField = sortFields[k] as ISortField;
if (newField.name == oldField.name) {
continue outer;
}
}
sortFields.unshift(oldField);
}
}
// Dispatch the "changing" event. If preventDefault() is called
// on this event, the sort operation will be cancelled. If columnIndices or
// sortFields are changed, the new values will be used.
if (isInteractive) {
// This is a shallow copy which means only the pointers to the ISortField objects
// are copied to the new Array, not the ISortField objects themselves.
if (oldSortFields)
oldSortFields = oldSortFields.concat();
if (hasEventListener(GridSortEvent.SORT_CHANGING)) {
const changingEvent:GridSortEvent =
new GridSortEvent(GridSortEvent.SORT_CHANGING,
false, true,
columnIndices,
oldSortFields, /* intended to be read-only but no way to enforce this */
sortFields);
// The event was cancelled so don't sort.
if (!dispatchEvent(changingEvent))
return false;
// Update the sort columns since they might have changed.
columnIndices = changingEvent.columnIndices;
if (!columnIndices)
return false;
// Update the new sort fields since they might have changed.
sortFields = changingEvent.newSortFields;
if (!sortFields)
return false;
}
}
// Remove each old SortField that's not a member of the new sortFields Array
// as a "styleClient" of this DataGrid.
if (oldSortFields) {
for each (var oldSortField:ISortField in oldSortFields) {
var oldASC:AdvancedStyleClient = oldSortField as AdvancedStyleClient;
if (!oldASC || (oldASC.styleParent != this) || (sortFields.indexOf(oldASC) != -1))
continue;
removeStyleClient(oldASC);
}
}
// Add new SortFields as "styleClients" of this DataGrid so that they
// inherit this DataGrid's locale style.
for each (var newSortField:ISortField in sortFields) {
var newASC:AdvancedStyleClient = newSortField as AdvancedStyleClient;
if (!newASC || (newASC.styleParent == this))
continue;
addStyleClient(newASC);
}
sort.fields = sortFields;
dataProvider.sort = sort;
dataProvider.refresh();
if (isInteractive) {
// Dispatch the "change" event.
if (hasEventListener(GridSortEvent.SORT_CHANGE)) {
const changeEvent:GridSortEvent =
new GridSortEvent(GridSortEvent.SORT_CHANGE,
false, true,
columnIndices,
oldSortFields, sortFields);
dispatchEvent(changeEvent);
}
// Update the visible sort indicators.
if (columnHeaderGroup)
columnHeaderGroup.visibleSortIndicatorIndices = columnIndices;
}
return true;
}
/**
* @private
* Copy from DataGrid::createSortFields function.
*/
private function createSortFields(columnIndices:Vector.<int>,
previousFields:Array, preservePrevious:Boolean):Array {
if (columnIndices.length == 0)
return null;
var fields:Array = new Array();
for each (var columnIndex:int in columnIndices) {
var col:GridColumn = this.getColumnAt(columnIndex);
if (!col)
return null;
var dataField:String = col.dataField;
// columns all must have a dataField or a labelFunction or a sortCompareFunction
if (dataField == null && col.labelFunction == null && col.sortCompareFunction == null)
return null;
const isComplexDataField:Boolean = (dataField && (dataField.indexOf(".") != -1));
var sortField:ISortField = null;
var sortDescending:Boolean = col.sortDescending;
// Check if we just sorted this column.
sortField = findSortField(dataField, fields, isComplexDataField);
// If we haven't sorted by this column yet, check if
// we've sorted by this column in the previous sort.
if (!sortField && previousFields)
sortField = findSortField(dataField, previousFields, isComplexDataField);
else
preservePrevious = false;
// Previously sorted column, so flip sortDescending.
if (sortField)
sortDescending = !sortField.descending;
// Create a SortField from the column. If the sortField was found in the
// previousFields and we need to preserve them, create a new sort field for the column.
if (!sortField || preservePrevious)
sortField = col.sortField;
col.sortDescending = sortDescending;
sortField.descending = sortDescending;
fields.push(sortField);
}
return fields;
}
/**
* @private
* Copy from DataGrid::getColumnAt function.
*/
private function getColumnAt(columnIndex:int):GridColumn {
const grid:Grid = grid;
if (!grid || !grid.columns)
return null;
const columns:IList = grid.columns;
return ((columnIndex >= 0) && (columnIndex < columns.length)) ?
columns.getItemAt(columnIndex) as GridColumn : null;
}
/**
* @private
* Copy from DataGrid::findSortField function.
*
* Finds a SortField using the provided dataField and returns it.
* If the dataField is complex, it tries to find a GridSortField
* with a matching dataFieldPath.
*
* @param dataField The dataField of the column.
* @param fields The array of SortFields to search through.
* @param isComplexDataField true if the dataField is a path.
*/
private static function findSortField(dataField:String, fields:Array, isComplexDataField:Boolean):ISortField {
if (dataField == null)
return null;
for each (var field:ISortField in fields) {
var name:String = field.name;
if (isComplexDataField && (field is GridSortField)) {
name = GridSortField(field).dataFieldPath;
}
if (name == dataField)
return field;
}
return null;
}
}
}
原文:http://blog.richmediaplus.com/2011/09/spark-multi-column-sortable-datagrid/