Displaying the sort arrow in a Flex DataGrid control without having to click a column

I’ve seen this question come up a few times recently in various forums/lists and even in my blog comments (see “Changing the default sort arrow skin on a Flex DataGrid control”).

The following example shows how you can display the sort arrow in a DataGrid control in Flex without having the user click on a column header in the DataGrid control.

Full code after the jump.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2008/02/28/displaying-the-sort-arrow-in-a-flex-datagrid-control-without-having-to-click-a-column/ -->
<mx:Application name="DataGrid_dataProvider_sort_fields_test">
        xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">
 
    <mx:Script>
        <![CDATA[
            import mx.collections.Sort;
            import mx.collections.SortField;
 
            private function init():void {
                arrColl.sort = new Sort();
                arrColl.sort.fields = [new SortField("idx", false, true)];
                arrColl.refresh();
            }
        ]]>
    </mx:Script>
 
    <mx:ArrayCollection id="arrColl">
        <mx:source>
            <mx:Array>
                <mx:Object idx="1" c1="One.1" c2="One.2" />
                <mx:Object idx="2" c1="Two.1" c2="Two.2" />
                <mx:Object idx="3" c1="Three.1" c2="Three.2" />
                <mx:Object idx="4" c1="Four.1" c2="Four.2" />
                <mx:Object idx="5" c1="Five.1" c2="Five.2" />
                <mx:Object idx="6" c1="Six.1" c2="Six.2" />
                <mx:Object idx="7" c1="Seven.1" c2="Seven.2" />
                <mx:Object idx="8" c1="Eight.1" c2="Eight.2" />
                <mx:Object idx="9" c1="Nine.1" c2="Nine.2" />
                <mx:Object idx="10" c1="Ten.1" c2="Ten.2" />
                <mx:Object idx="11" c1="Eleven.1" c2="Eleven.2" />
                <mx:Object idx="12" c1="Twelve.1" c2="Twelve.2" />
                <mx:Object idx="13" c1="Thirteen.1" c2="Thirteen.2" />
            </mx:Array>
        </mx:source>
    </mx:ArrayCollection>
 
    <mx:DataGrid id="dataGrid"
            dataProvider="{arrColl}"
            creationComplete="init();">
        <mx:columns>
            <mx:DataGridColumn dataField="idx" />
            <mx:DataGridColumn dataField="c1" />
            <mx:DataGridColumn dataField="c2" />
        </mx:columns>
    </mx:DataGrid>
 
</mx:Application>

View source is enabled in the following example.

A big thanks to Rob for the heads up with the solution!

For more information, see http://bugs.adobe.com/jira/browse/SDK-14663.

34 thoughts on “Displaying the sort arrow in a Flex DataGrid control without having to click a column

  1. Thanks for this helpful post, glad to see that it’s not only me that has had this problem, much appreciated, keep up the good work!

  2. The solution is good, but I just want to know is there any way that we can showed the up/down arrow without having to refresh (sort) the grid data provider.

  3. Is there a way to programatically get the last column sort? In otherwords say the user has clicked on a particular column header, how would you store the sort associated with that header, along with its direction?

  4. Bill J,

    Not sure if this is the best solution, but it seems to 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.DataGridEvent;
    
                private function dataGrid_headerRelease(evt:DataGridEvent):void {
                    // Hang out a while, wait a frame.
                    callLater(delayedSet, [evt]);
                }
    
                private function delayedSet(evt:DataGridEvent):void {
                    lbl1.text = evt.dataField;
                    lbl2.text = dataGrid.columns[evt.columnIndex].sortDescending;
                }
            ]]>
        </mx:Script>
    
        <mx:Array id="arr">
            <mx:Object idx="1" c1="One.1" c2="One.2" />
            <mx:Object idx="2" c1="Two.1" c2="Two.2" />
            <mx:Object idx="3" c1="Three.1" c2="Three.2" />
            <mx:Object idx="4" c1="Four.1" c2="Four.2" />
            <mx:Object idx="5" c1="Five.1" c2="Five.2" />
            <mx:Object idx="6" c1="Six.1" c2="Six.2" />
        </mx:Array>
    
        <mx:DataGrid id="dataGrid"
                dataProvider="{arr}"
                headerRelease="dataGrid_headerRelease(event);">
            <mx:columns>
                <mx:DataGridColumn dataField="idx" />
                <mx:DataGridColumn dataField="c1" />
                <mx:DataGridColumn dataField="c2" />
            </mx:columns>
        </mx:DataGrid>
    
        <mx:Form>
            <mx:FormItem label="dataField:">
                <mx:Label id="lbl1" />
            </mx:FormItem>
            <mx:FormItem label="sortDescending:">
                <mx:Label id="lbl2" />
            </mx:FormItem>
        </mx:Form>
    
    </mx:Application>
    

    If you use an ArrayCollection or XMLListCollection as a data provider, you may be able to access the collection’s underlying sort property and determine the sort order as well.

    In fact, here’s what that would look like too:

    <?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.collections.SortField;
                import mx.events.DataGridEvent;
    
                private function dataGrid_headerRelease(evt:DataGridEvent):void {
                    // Hang out a while, wait a frame.
                    callLater(delayedSet, [evt]);
                }
    
                private function delayedSet(evt:DataGridEvent):void {
                    var sortField:SortField = arrColl.sort.fields[0] as SortField;
                    lbl1.text = sortField.name;
                    lbl2.text = sortField.descending.toString();
                }
            ]]>
        </mx:Script>
    
        <mx:ArrayCollection id="arrColl">
            <mx:source>
                <mx:Array>
                    <mx:Object idx="1" c1="One.1" c2="One.2" />
                    <mx:Object idx="2" c1="Two.1" c2="Two.2" />
                    <mx:Object idx="3" c1="Three.1" c2="Three.2" />
                    <mx:Object idx="4" c1="Four.1" c2="Four.2" />
                    <mx:Object idx="5" c1="Five.1" c2="Five.2" />
                    <mx:Object idx="6" c1="Six.1" c2="Six.2" />
                </mx:Array>
            </mx:source>
        </mx:ArrayCollection>
    
        <mx:DataGrid id="dataGrid"
                dataProvider="{arrColl}"
                headerRelease="dataGrid_headerRelease(event);">
            <mx:columns>
                <mx:DataGridColumn dataField="idx" />
                <mx:DataGridColumn dataField="c1" />
                <mx:DataGridColumn dataField="c2" />
            </mx:columns>
        </mx:DataGrid>
    
        <mx:Form>
            <mx:FormItem label="dataField:">
                <mx:Label id="lbl1" />
            </mx:FormItem>
            <mx:FormItem label="sortDescending:">
                <mx:Label id="lbl2" />
            </mx:FormItem>
        </mx:Form>
    
    </mx:Application>
    

    Again, I make no claims this is the “best” approach, just the first thing that came to mind at 7:40am. :)

    Peter

  5. Hieu Lam,

    I don’t believe there is a way to show a sort arrow without refreshing the data provider. Or maybe there is, and I just don’t know about it (very likely).

    If you haven’t found a solution yet (and are still reading comments), I suggest asking on the FlexCoders mailing list and seeing if anybody there has any ideas.

    Peter

  6. Just looking at the DataGrid source I dont think there is a way to update it without refreshing the list.

    There is a private method on the grid, called UpdateSortIndexAndDirection which is private. Internally it sets the sortIndex and last sortIndex (these are mx_internal) which are used to draw the caret in the placeSortArrow method.

    In my case I had results coming back from a server on each click so I had to extend DataGrid and override the headerReleaseHandler method. In there I stopped the default sort from happening and saved the column to be sorted off, when the results were returned from the search I reset the search critera in the sort attribute on the new collection called refresh and all was well.

    But like peterd said, there is probably another way :).

  7. Hi ,
    Thanks for the post.
    Saved me some time.
    Your posts always help me with the right kind of stuff.

    Anil

  8. How do you do this with an HTTPService? I am using SQL and tried to convert it to an ArrayCollection, but no luck.

  9. Hello Jason,
    Could you tell me how you override headerReleaseHandler? I can’t do this… I have an exception “1020: Method marked override must override another method”

    Please…

  10. interesting, seems to imply that the data model is actually being sorted, not the view itself. which would mean you can’t use the same data model to back two different tables (since then sorting one would sort the other)??

    btw, I just noticed in my sample app that when the data provider value changes, my table updates automatically but it also loses whatever sort happened to be set on it. is this a ‘bug’?

  11. How do I do the first part but with a XMLList and not an array? Could you please give one example? Pretty please :)

    1. @bsides,

      How about this?

      <?xml version="1.0" encoding="utf-8"?>
      <!-- http://blog.flexexamples.com/2008/02/28/displaying-the-sort-arrow-in-a-flex-datagrid-control-without-having-to-click-a-column/ -->
      <mx:Application name="DataGrid_dataProvider_sort_fields_test"
              xmlns:mx="http://www.adobe.com/2006/mxml"
              layout="vertical"
              verticalAlign="middle"
              backgroundColor="white">
       
          <mx:Script>
              <![CDATA[
                  import mx.collections.Sort;
                  import mx.collections.SortField;
       
                  private function init():void {
                      xmlListColl.sort = new Sort();
                      xmlListColl.sort.fields = [new SortField("@idx", false, true, true)];
                      xmlListColl.refresh();
                  }
              ]]>
          </mx:Script>
       
          <mx:XMLListCollection id="xmlListColl">
              <mx:source>
                  <mx:XMLList>
                      <node idx="1" c1="One.1" c2="One.2" />
                      <node idx="2" c1="Two.1" c2="Two.2" />
                      <node idx="3" c1="Three.1" c2="Three.2" />
                      <node idx="4" c1="Four.1" c2="Four.2" />
                      <node idx="5" c1="Five.1" c2="Five.2" />
                      <node idx="6" c1="Six.1" c2="Six.2" />
                      <node idx="7" c1="Seven.1" c2="Seven.2" />
                      <node idx="8" c1="Eight.1" c2="Eight.2" />
                      <node idx="9" c1="Nine.1" c2="Nine.2" />
                      <node idx="10" c1="Ten.1" c2="Ten.2" />
                      <node idx="11" c1="Eleven.1" c2="Eleven.2" />
                      <node idx="12" c1="Twelve.1" c2="Twelve.2" />
                      <node idx="13" c1="Thirteen.1" c2="Thirteen.2" />
                  </mx:XMLList>
              </mx:source>
          </mx:XMLListCollection>
       
          <mx:DataGrid id="dataGrid"
                  dataProvider="{xmlListColl}"
                  creationComplete="init();">
              <mx:columns>
                  <mx:DataGridColumn dataField="@idx" />
                  <mx:DataGridColumn dataField="@c1" />
                  <mx:DataGridColumn dataField="@c2" />
              </mx:columns>
          </mx:DataGrid>
       
      </mx:Application>

      Peter

  12. If you just want to display arrows without sorting the model, you can extend the datagrid class and add these 2 methods :

    public function set sortIndex(index:uint):void { mx_internal::sortIndex = index; } // Column index
    public function set sortDirection(direction:String):void { mx_internal::sortDirection = direction; } //ASC or DESC

    you don’t need to refresh the dataprovider…

    Hope that helps
    Alex

  13. I have an interesting challenge with regard to sort arrows. I would like to not show them in order to preserve available label real estate. I have attempting this by setting sortArrowSkin to various things but with no luck. Might you be able to offer me any advise?

    1. @Colin,

      You should be able to set the sortArrowSkin to null, although it would appear that it makes the column unsortable (that may be a bug):

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
       
          <mx:DataGrid sortArrowSkin="{null}">
              <mx:columns>
                  <mx:DataGridColumn dataField="c1" />
                  <mx:DataGridColumn dataField="c2" />
                  <mx:DataGridColumn dataField="c3" />
              </mx:columns>
              <mx:dataProvider>
                  <mx:ArrayCollection>
                      <mx:Object c1="row1, col1" c2="row1, col2" c3="row1, col3" />
                      <mx:Object c1="row2, col1" c2="row2, col2" c3="row2, col3" />
                      <mx:Object c1="row3, col1" c2="row3, col2" c3="row3, col3" />
                      <mx:Object c1="row4, col1" c2="row4, col2" c3="row4, col3" />
                      <mx:Object c1="row5, col1" c2="row5, col2" c3="row5, col3" />
                      <mx:Object c1="row6, col1" c2="row6, col2" c3="row6, col3" />
                      <mx:Object c1="row7, col1" c2="row7, col2" c3="row7, col3" />
                      <mx:Object c1="row8, col1" c2="row8, col2" c3="row8, col3" />
                      <mx:Object c1="row9, col1" c2="row9, col2" c3="row9, col3" />
                  </mx:ArrayCollection>
              </mx:dataProvider>
          </mx:DataGrid>
       
      </mx:Application>

      Peter

      1. Or you could set the sortArrowSkin style via an external .CSS file or <Style> block, as seen in the following example:

        <?xml version="1.0" encoding="utf-8"?>
        <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
         
            <mx:Style>
                DataGrid {
                    sortArrowSkin: ClassReference(null);
                }
            </mx:Style>
         
            <mx:DataGrid>
                <mx:columns>
                    <mx:DataGridColumn dataField="c1" />
                    <mx:DataGridColumn dataField="c2" />
                    <mx:DataGridColumn dataField="c3" />
                </mx:columns>
                <mx:dataProvider>
                    <mx:ArrayCollection>
                        <mx:Object c1="row1, col1" c2="row1, col2" c3="row1, col3" />
                        <mx:Object c1="row2, col1" c2="row2, col2" c3="row2, col3" />
                        <mx:Object c1="row3, col1" c2="row3, col2" c3="row3, col3" />
                        <mx:Object c1="row4, col1" c2="row4, col2" c3="row4, col3" />
                        <mx:Object c1="row5, col1" c2="row5, col2" c3="row5, col3" />
                        <mx:Object c1="row6, col1" c2="row6, col2" c3="row6, col3" />
                        <mx:Object c1="row7, col1" c2="row7, col2" c3="row7, col3" />
                        <mx:Object c1="row8, col1" c2="row8, col2" c3="row8, col3" />
                        <mx:Object c1="row9, col1" c2="row9, col2" c3="row9, col3" />
                    </mx:ArrayCollection>
                </mx:dataProvider>
            </mx:DataGrid>
         
        </mx:Application>

        Peter

      2. And the workaround for the null sort arrow skin not performing a sort is to set the sortArrowSkin style to something like ProgrammaticSkin instead:

        <?xml version="1.0" encoding="utf-8"?>
        <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
         
            <!-- <mx:Style>
                DataGrid {
                    sortArrowSkin: ClassReference("mx.skins.ProgrammaticSkin");
                }
            </mx:Style> -->
         
            <mx:DataGrid sortArrowSkin="mx.skins.ProgrammaticSkin">
                <mx:columns>
                    <mx:DataGridColumn dataField="c1" />
                    <mx:DataGridColumn dataField="c2" />
                    <mx:DataGridColumn dataField="c3" />
                </mx:columns>
                <mx:dataProvider>
                    <mx:ArrayCollection>
                        <mx:Object c1="row1, col1" c2="row1, col2" c3="row1, col3" />
                        <mx:Object c1="row2, col1" c2="row2, col2" c3="row2, col3" />
                        <mx:Object c1="row3, col1" c2="row3, col2" c3="row3, col3" />
                        <mx:Object c1="row4, col1" c2="row4, col2" c3="row4, col3" />
                        <mx:Object c1="row5, col1" c2="row5, col2" c3="row5, col3" />
                        <mx:Object c1="row6, col1" c2="row6, col2" c3="row6, col3" />
                        <mx:Object c1="row7, col1" c2="row7, col2" c3="row7, col3" />
                        <mx:Object c1="row8, col1" c2="row8, col2" c3="row8, col3" />
                        <mx:Object c1="row9, col1" c2="row9, col2" c3="row9, col3" />
                    </mx:ArrayCollection>
                </mx:dataProvider>
            </mx:DataGrid>
         
        </mx:Application>

        I’ll file a bug at http://bugs.adobe.com/flex/ later today and document the workaround and then post the bug number here for anybody interested.

        Peter

  14. Hi,

    Another way to prevent the sort arrow from appearing is to override placeSortArrow to do nothing:

    override protected function placeSortArrow():void{}

    – Rob

  15. We actually have a grid that solves the issue of placing the intial sort arrow as required by the original poster. This is available at http://www.flexicious.com. We also have a solution for the advanced datagrid, where you can programatically add multiple sorts and it will draw the arrows appropriately.

  16. Peter i used the headerrelease function to sort the datagrid, the code is:

    arrcoll is my dataprovider, but when sort the ArrayCollection lose the arrow ASC/DSC.

    private var clickedColumn:String;
    private function dataGrid_headerRelease(evt:DataGridEvent):void {
    var dgrid:DataGrid = DataGrid(evt.currentTarget);
    var column:DataGridColumn = dgrid.columns[evt.columnIndex];
    var tempField:Object = column.dataField;
    clickedColumn = tempField.toString();
    dgPacientes.dataProvider = arrCol;
    }

    i need help.

    bye.

    1. @Paul,

      Not sure where your sorting code is, but I’d guess that you’re losing the sort arrow because you are resetting the data provider each time the header is clicked.

      Peter

  17. sorry about my english… This code has helped me, but i can’t get sort data from down to up, i’m using flash builder 4 and sdk 4. Any help would be very appreciated.

  18. solved: arrColl.lastResult.sort.fields = [new SortField(“idx”, true)];. Easy XD.

  19. Hi All,
    I am working on sortarrow skin for past 4 days and its really frustrating.
    I am trying to show a disabled sort arrow skin on all columns when datagrid loads initially.When a user clicks on particullar column the disabled skin should be removed and changed to a different skin.I extended grid and override updatelist,headerrelease but nothing worked as planned.I am really hopping that someone can help me with it.I am trying to achieve this for past few days but had many prioblems.

    Thanks ……..

  20. Sort and SortField are flawed in the standard Adobe grid. For one thing, they do not respect any custom SortCompareFunctions you may have set on the grid, and while the arrows will appear if you have ONE field set, it will not appear if you have two set.

    I found that most of the requirements in the original posting can be better addressed without extending any classes simply by dispatching a headerRelease Event.

    This actually works FAR better than using sortFields because not only does it cause the arrow to appear correctly, it will also respect any custom sortCompareFunction/s on the DataGridColumns, which the basic Sort/SortField method does not do.

    /**
    * Whenever data or external sort criteria change, call this method. It will
    * resort the pending list by simulating a mouseclick (headRelease event), triggering
    * a sortColumn() call.
    *
    * The normal method of sorting (creating a SortField) does not respect the sortCompareFunction
    * rules and does not pop up the little arrow on the column(s) being sorted; this does.
    * */
    private function setDefaultPendingSort():void {

    // Adobe's "textbook" way to sort columns doesn't respect custom column sorting,
    // and will not display the arrow when sorting on multiple columns:
    //model.sampleList.sort = new Sort();
    //model.sampleList.sort.fields = [new SortField("emp_name", true,false,true), new SortField("emp_id",true,false,true)];
    //model.sampleList.refresh();

    // The MUCH better way simulates a mouseclick on the header...

    var defaultColumn:String = "emp_name"; //"accessionNum";
    var col:DataGridColumn = getColumnByDataField(dgPendingSamples, defaultColumn);
    col.sortDescending = false;

    var dgEvent:DataGridEvent = new DataGridEvent("headerRelease");
    dgEvent.dataField = defaultColumn;
    dgEvent.columnIndex = getColumnIndexByDataField(dgPendingSamples, defaultColumn);
    dgEvent.rowIndex = -1;
    dgPendingSamples.dispatchEvent(dgEvent);

    }

    /**
    * Returns the column based on the column's data field. Returns the first column found that
    * uses the specified dataField, or -1 if none found.
    * */
    private function getColumnIndexByDataField(dg:DataGrid, dfld:String):int {
    for (var i:uint = 0; i < dg.columnCount; i++) {
    var col:DataGridColumn = dg.columns[i];
    if(col.dataField == dfld) {
    return i;
    }
    }
    return -1;
    }

    /**
    * Returns the column based on the header text, allowing
    * columns to be rearranged without fucking up column dependencies.
    * Returns null if none match.
    * */
    private function getColumnByDataField(dg:DataGrid, dfld:String):DataGridColumn {
    for (var i:uint = 0; i < dg.columnCount; i++) {
    var col:DataGridColumn = dg.columns[i];
    if(col.dataField == dfld) {
    return col;
    }
    }
    return null;
    }

  21. hello peter
    in my example I have two datagrids, the first “cltdgg” is sorting and arrow appears in the columns. While nothing in the second. Is there a link to the AsyncListView?
    Thank you for your help :)

    Flex4, FlashBuilder 4.5 for Php

    here is the code exemple :

    private var dataProvider : ArrayCollection = new ArrayCollection(

    [{price:”20″,location:”india”},
    {price:”5″,location:”india”},
    {price:”40″,location:”india”},
    {price:”20″,location:”germeny”},
    {price:”20″,location:”spain”},
    {price:”10″,location:”usa”},
    {price:”30″,location:”Italy”}]);

    protected function cltDg_creationCompleteHandler(event:FlexEvent):void {
    getClientsResult.token = serviceClients.getclients();
    }

  22. If you have multiple columns to sort and you want the sort arrow to show up, extend datagrid and grab these values (assuming they were set already), such as after a headerRelease

    var curSortDirection:String = mx_internal::sortDirection;
    var curSortIndex:int = mx_internal::sortIndex;

    Then after creating the sorts you want and refreshing dataProvider, set the mx_internal values back to what they were and all will be well.

Comments are closed.