The random utterances of David Arno

Flex developer tip: initialising a datagrid with a sorted column

The Basic Datagrid
If you use Flex, then you are likely – sooner rather than later – to find yourself using the datagrid component. On the whole, it’s a really powerful and fairly easy to use component. There is one caveat to this though and that is the way column sorting occurs. Consider the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                layout="vertical" 
                backgroundGradientColors="[#efefef, #E8e8e8]" 
                width="450" height="350" 
                horizontalAlign="left"
                borderColor="0"
                borderStyle="solid">
  <mx:Script>
    <![CDATA[
      import mx.collections.SortField;
      import mx.collections.Sort;
      private function start():void
      {
        var data:Array = 
          [{name:"Red", red:255, green:0, blue:0},
           {name:"Green", red:0, green:255, blue:0},
           {name:"Blue", red:0, green:0, blue:255},
           {name:"Yellow", red:255, green:255, blue:0},
           {name:"Magenta", red:255, green:0, blue:255},
           {name:"Cyan", red:0, green:255, blue:255},
           {name:"White", red:255, green:255, blue:255},
           {name:"Black", red:0, green:0, blue:0}];
 
        dataGrid.dataProvider = data;
      }
    ]]>
  </mx:Script>
 
  <mx:Button label="Start" click="start()"/>
  <mx:DataGrid id="dataGrid" width="100%" height="100%">
    <mx:columns>
      <mx:DataGridColumn headerText="Colour" 
      	                 dataField="name"/>
      <mx:DataGridColumn headerText="Red" 
      	                 dataField="red"/>
      <mx:DataGridColumn headerText="Green" 
      	                 dataField="green"/>
      <mx:DataGridColumn headerText="Blue" 
      	                 dataField="blue"/>
    </mx:columns>
  </mx:DataGrid>
</mx:Application>

This results in the following SWF:

This movie requires Flash Player 9

As you can see, the data is presented in the datagrid in the same order as the code defines it. Click on one of the column headers and it sorts that column. Click again and it reverse sorts the column. This is great, unless you want the datagrid to start up with on of the columns sorted. That’s when the fun starts.

An Automatically Sorted Datagrid
If you read through the API documentation for the datagrid, it is really vague on how sorting occurs and there is no obvious way of initialising a datagrid in a sorted state. In the past, I have tried all sorts of ways of ways, including simulating a click event on the column header to force a sort and even creating a datagrid subclass that overrides a lot of the sort arrow placement code to force a sort arrow to appear at start up. Such techniques though are messy, unreliable and there was always this nagging feeling that I’d missed something obvious.

Recently I applied a bit of lateral thinking – and a lot of googling – to the problem and I discovered the proper way to solve this problem. It wasn’t obvious, but it was far simpler than anything else I’d tried and it works properly. The key to it is realising that when one clicks on a column header, a sort is applied to the data source and – this is the non obvious bit – that sort is used to determine where the sort arrow appears and whether its an up-pointing or down-pointing arrow. So the solution to having a datagrid start up in a sorted state is to apply a sort to the datagrid’s data source at start up. Told you it wasn’t obvious!

Consider therefore the new version of the code. The key part is the contents of start(), which is responsible for initialising the datagrid with a sort.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                layout="vertical" 
                backgroundGradientColors="[#efefef, #E8e8e8]" 
                width="450" height="350" 
                horizontalAlign="left"
                borderColor="0"
                borderStyle="solid">
  <mx:Script>
    <![CDATA[
      import mx.collections.SortField;
      import mx.collections.Sort;
      private function start(column:String, 
                             descending:Boolean):void
      {
        var sort:Sort = new Sort();
        var sortField:SortField = 
          new SortField(column, true, descending);
 
        var data:Array = 
          [{name:"Red", red:255, green:0, blue:0},
           {name:"Green", red:0, green:255, blue:0},
           {name:"Blue", red:0, green:0, blue:255},
           {name:"Yellow", red:255, green:255, blue:0},
           {name:"Magenta", red:255, green:0, blue:255},
           {name:"Cyan", red:0, green:255, blue:255},
           {name:"White", red:255, green:255, blue:255},
           {name:"Black", red:0, green:0, blue:0}];
 
        sort.fields = [sortField];         
        dataGrid.dataProvider = data;
        dataGrid.dataProvider.sort = sort;
        dataGrid.dataProvider.refresh();
      }
    ]]>
  </mx:Script>
 
  <mx:HBox width="100%" horizontalAlign="center">
    <mx:Button label="Start Colour Desc" 
               click="start('name', true)"/>
    <mx:Button label="Start Red Asc" 
               click="start('red', false)"/>
    <mx:Button label="Start Red Desc" 
               click="start('red', true)"/>
  </mx:HBox>
  <mx:DataGrid id="dataGrid" width="100%" height="100%">
    <mx:columns>
      <mx:DataGridColumn headerText="Colour" 
                         dataField="name"/>
      <mx:DataGridColumn headerText="Red" 
                         dataField="red"/>
      <mx:DataGridColumn headerText="Green" 
                         dataField="green"/>
      <mx:DataGridColumn headerText="Blue" 
                         dataField="blue"/>
    </mx:columns>
  </mx:DataGrid>
</mx:Application>

This results in the following SWF:

This movie requires Flash Player 9

Applying a Compare Function to an Auto-Sort Datagrid
There is one final problem with initialising a datagrid in a sorted state and that is when columns have their own custom compare functions. When the titlebar is clicked, the custom function is applied to the sort. When we initialise the datagrid in a sorted state, we are supplying it with a presorted data source with a Sort object attached. Therefore the custom function is not applied. All is not lost though, as the function cane be manually attached to the Sort object. The following code demonstrates this with a function that sorts first the colour number, then the name for the red green and blue fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                layout="vertical" 
                backgroundGradientColors="[#efefef, #E8e8e8]" 
                width="450" height="350" 
                horizontalAlign="left"
                borderColor="0"
                borderStyle="solid">
  <mx:Script>
    <![CDATA[
      import mx.collections.SortField;
      import mx.collections.Sort;
 
      private function start(column:String, 
                             descending:Boolean,
                             compareFunction:Function):void
      {
        var sort:Sort = new Sort();
        var sortField:SortField = 
          new SortField(column, true, descending);
        var data:Array = 
          [{name:"Red", red:255, green:0, blue:0},
           {name:"Green", red:0, green:255, blue:0},
           {name:"Blue", red:0, green:0, blue:255},
           {name:"Yellow", red:255, green:255, blue:0},
           {name:"Magenta", red:255, green:0, blue:255},
           {name:"Cyan", red:0, green:255, blue:255},
           {name:"White", red:255, green:255, blue:255},
           {name:"Black", red:0, green:0, blue:0}];
 
        sortField.compareFunction = compareFunction;
        sort.fields = [sortField];
 
        dataGrid.dataProvider = data;
        dataGrid.dataProvider.sort = sort;
        dataGrid.dataProvider.refresh();
      }
 
      private function sortColour(field:String, 
                                  obj1:Object, 
                                  obj2:Object):int
      {
        var i1:int = obj1[field] as int;
        var i2:int = obj2[field] as int;
 
        var n1:String = obj1.name;        
        var n2:String = obj2.name;        
 
        return i1 > i2 ? 1 : i2 > i1 ? -1 : 
               n1 > n2 ? 1 : n2 > n1 ? -1 : 0;
      }
 
      private function sortRed(obj1:Object, 
                               obj2:Object):int 
      {
        return sortColour("red", obj1, obj2);
      }
 
      private function sortGreen(obj1:Object, 
                                 obj2:Object):int 
      {
        return sortColour("green", obj1, obj2);
      }
 
      private function sortBlue(obj1:Object, 
                                obj2:Object):int 
      {
        return sortColour("blue", obj1, obj2);
      }
    ]]>
  </mx:Script>
 
  <mx:HBox width="100%" horizontalAlign="center">
    <mx:Button label="Start Colour Desc" 
               click="start('name', true, null)"/>
    <mx:Button label="Start Green Asc" 
               click="start('green', false, sortGreen)"/>
    <mx:Button label="Start Green Desc" 
               click="start('green', true, sortGreen)"/>
  </mx:HBox>
  <mx:DataGrid id="dataGrid" width="100%" height="100%">
    <mx:columns>
      <mx:DataGridColumn headerText="Colour" 
                           dataField="name"/>
      <mx:DataGridColumn headerText="Red" 
                           dataField="red" 
                           sortCompareFunction="sortRed"/>
      <mx:DataGridColumn headerText="Green" 
                           dataField="green" 
                           sortCompareFunction="sortGreen"/>
      <mx:DataGridColumn headerText="Blue" 
                           dataField="blue" 
                           sortCompareFunction="sortBlue"/>
    </mx:columns>
  </mx:DataGrid>
</mx:Application>

And the two-column sorting behaviour can be observed in the resultant SWF below:

This movie requires Flash Player 9

And there you have it. Sorting a datagrid at start up may not be intuitive, but it’s fairly straighforward once you know how.


Share This Post...
1 comment so far, click here to read it or add another

1 Comment so far

  1. Anonymous Dodd November 11th, 2009 16:03

    Feels a bit like moving your cars wheels to get the steering wheel in the right position.

Leave a reply

Close