Creating a custom sort on a DataGrid control in Flex

by Peter deHaan on April 9, 2008

The following example shows how you can create a custom sort on a Flex DataGrid control by using the SortField class and creating a custom compare function.

Full code after the jump.

View MXML

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2008/04/09/creating-a-custom-sort-on-a-datagrid-control-in-flex/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Script>
        <![CDATA[
            import mx.collections.SortField;
            import mx.collections.Sort;
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.utils.ObjectUtil;

            private function sortOld():void {
                var sortField:SortField = new SortField();
                sortField.compareFunction = test2_compareFunc;
                sortField.descending = checkBox.selected;

                var oldSort:Sort = new Sort();
                oldSort.fields = [sortField];

                xmlListColl.sort = oldSort;
                xmlListColl.refresh();
            }

            private function sortNew():void {
                var sortField:SortField = new SortField();
                sortField.compareFunction = test3_compareFunc;
                sortField.descending = checkBox.selected;

                var sort:Sort = new Sort();
                sort.fields = [sortField];

                xmlListColl.sort = sort;
                xmlListColl.refresh();
            }

            private function resetSort():void {
                xmlListColl.sort = null;
                xmlListColl.refresh();
            }

            private function dataGridCol_labelFunc(item:XML, col:DataGridColumn):String {
                return item.*.(@name == col.dataField).text();
            }

            private function test2_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test2.text();
                var valueB:String = itemB.test2.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }

            private function test3_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test3.text();
                var valueB:String = itemB.test3.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }
        ]]>
    </mx:Script>

    <mx:XML id="tests" source="tests.xml" />
    <mx:XMLListCollection id="xmlListColl" source="{tests.test}" />

    <mx:ApplicationControlBar dock="true">
        <mx:CheckBox id="checkBox" label="descending:" labelPlacement="left" />

        <mx:Button label="Sort 'old'"
                click="sortOld();" />

        <mx:Button label="Sort 'new'"
                click="sortNew();" />

        <mx:Button label="Reset sort"
                click="resetSort();" />
    </mx:ApplicationControlBar>

    <mx:DataGrid id="dataGrid"
            dataProvider="{xmlListColl}"
            width="300"
            verticalScrollPolicy="on"
            sortableColumns="true">
        <mx:columns>
            <mx:DataGridColumn dataField="old"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test2_compareFunc" />
            <mx:DataGridColumn dataField="new"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test3_compareFunc"  />
        </mx:columns>
    </mx:DataGrid>

</mx:Application>

tests.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2008/04/09/creating-a-custom-sort-on-a-datagrid-control-in-flex/ -->
<tests>
    <test name="group1">
        <test2 name="old">One (old)</test2>
        <test3 name="new">Un (new)</test3>
    </test>
    <test name="group2">
        <test2 name="old">Two (old)</test2>
        <test3 name="new">Deux (new)</test3>
    </test>

    <test name="group8">
        <test2 name="old">Eight (old)</test2>
        <test3 name="new">Huit (new)</test3>
    </test>
</tests>

View source is enabled in the following example.

{ 5 comments… read them below or add one }

Dave September 24, 2008 at 2:58 am

Thanks! this is very useful info!!
How can sorting a datagrid be accomplished using a ComboBox?

Dave

Reply

peterd October 3, 2008 at 9:06 am

Dave,

A bit crude, but this should work:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Script>
        <![CDATA[
            import mx.events.ListEvent;
            import mx.collections.SortField;
            import mx.collections.Sort;
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.utils.ObjectUtil;

            private function comboBox_change(evt:ListEvent):void {
                var field:String = comboBox.selectedItem.sortField;
                var desc:Boolean = (comboBox.selectedItem.descending);

                var sort:Sort = new Sort();
                var sortField:SortField = new SortField();
                sortField.descending = desc;
                switch (field) {
                    case "old":
                        sortField.compareFunction = test2_compareFunc;
                        break;
                    case "new":
                        sortField.compareFunction = test3_compareFunc;
                        break;
                }
                sort.fields = [sortField];
                xmlListColl.sort = sort;
                xmlListColl.refresh();
            }

            private function resetSort():void {
                xmlListColl.sort = null;
                xmlListColl.refresh();
            }

            private function dataGridCol_labelFunc(item:XML, col:DataGridColumn):String {
                return item.*.(@name == col.dataField).text();
            }

            private function test2_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test2.text();
                var valueB:String = itemB.test2.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }

            private function test3_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test3.text();
                var valueB:String = itemB.test3.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }
        ]]>
    </mx:Script>

    <mx:XML id="tests" source="tests.xml" />
    <mx:XMLListCollection id="xmlListColl" source="{tests.test}" />

    <mx:Array id="arr">
        <mx:Object label="Sort 'old' ascending"
                sortField="old"
                descending="false" />
        <mx:Object label="Sort 'old' descending"
                sortField="old"
                descending="true" />
        <mx:Object label="Sort 'new' ascending"
                sortField="new"
                descending="false" />
        <mx:Object label="Sort 'new' descending"
                sortField="new"
                descending="true" />
    </mx:Array>

    <mx:ApplicationControlBar dock="true">
        <mx:ComboBox id="comboBox"
                dataProvider="{arr}"
                change="comboBox_change(event);" />

        <mx:Button label="Reset sort"
                click="resetSort();" />
    </mx:ApplicationControlBar>

    <mx:DataGrid id="dataGrid"
            dataProvider="{xmlListColl}"
            width="300"
            verticalScrollPolicy="on"
            sortableColumns="true">
        <mx:columns>
            <mx:DataGridColumn dataField="old"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test2_compareFunc" />
            <mx:DataGridColumn dataField="new"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test3_compareFunc"  />
        </mx:columns>
    </mx:DataGrid>

</mx:Application>

Looking back on the code, you may be able to move the sortField.compareFunction logic into the ComboBox data provider array instead of using a switch statement in the comboBox_change() function. I didn’t think of that the first time. You may also want to set the prompt property on the ComboBox.

Peter

Reply

peterd October 3, 2008 at 9:13 am

Something like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Script>
        <![CDATA[
            import mx.events.ListEvent;
            import mx.collections.SortField;
            import mx.collections.Sort;
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.utils.ObjectUtil;

            private function comboBox_change(evt:ListEvent):void {
                var item:Object = comboBox.selectedItem;

                var sortField:SortField = new SortField();
                sortField.descending = item.descending;
                sortField.compareFunction = item.compareFunc;

                var sort:Sort = new Sort();
                sort.fields = [sortField];

                xmlListColl.sort = sort;
                xmlListColl.refresh();
            }

            private function resetSort():void {
                xmlListColl.sort = null;
                xmlListColl.refresh();
            }

            private function dataGridCol_labelFunc(item:XML,
                        col:DataGridColumn):String {
                return item.*.(@name == col.dataField).text();
            }

            private function test2_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test2.text();
                var valueB:String = itemB.test2.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }

            private function test3_compareFunc(itemA:XML, itemB:XML):int {
                var valueA:String = itemA.test3.text();
                var valueB:String = itemB.test3.text();
                return ObjectUtil.stringCompare(valueA, valueB);
            }
        ]]>
    </mx:Script>

    <mx:XML id="tests" source="tests.xml" />
    <mx:XMLListCollection id="xmlListColl" source="{tests.test}" />

    <mx:Array id="arr">
        <mx:Object label="Sort 'old' ascending"
                sortField="old"
                descending="false"
                compareFunc="{test2_compareFunc}" />
        <mx:Object label="Sort 'old' descending"
                sortField="old"
                descending="true"
                compareFunc="{test2_compareFunc}" />
        <mx:Object label="Sort 'new' ascending"
                sortField="new"
                descending="false"
                compareFunc="{test3_compareFunc}" />
        <mx:Object label="Sort 'new' descending"
                sortField="new"
                descending="true"
                compareFunc="{test3_compareFunc}" />
    </mx:Array>

    <mx:ApplicationControlBar dock="true">
        <mx:ComboBox id="comboBox"
                dataProvider="{arr}"
                prompt="Please select a sort..."
                change="comboBox_change(event);" />

        <mx:Button label="Reset sort"
                click="resetSort();" />
    </mx:ApplicationControlBar>

    <mx:DataGrid id="dataGrid"
            dataProvider="{xmlListColl}"
            width="300"
            verticalScrollPolicy="on"
            sortableColumns="true">
        <mx:columns>
            <mx:DataGridColumn dataField="old"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test2_compareFunc" />
            <mx:DataGridColumn dataField="new"
                    labelFunction="dataGridCol_labelFunc"
                    sortCompareFunction="test3_compareFunc"  />
        </mx:columns>
    </mx:DataGrid>

</mx:Application>

Peter

Reply

Sunny December 1, 2008 at 2:52 am

Thany you for your code.
First I click the DataGrid’s header, there’s a small black triangle to show where it is working on.
Then I click the buttons upside, to resort the DataGrid. Can the small black triangle change the meantime?

Reply

Dave October 22, 2009 at 6:04 pm

Thanks Peter, this is awesome!!

Reply

Leave a Comment

Sorry, this blog is terrible at eating HTML comments.
If you're pasting any HTML/XML/MXML code, you need to convert your < characters to &lt; and your > characters to &gt; .

Anti-Spam Protection by WP-SpamFree

Previous post:

Next post: