DataGrid Footers

Ever notice that the DataGrid has headers but no footers? That’s because footers have to be computed if they are totals or averages or whatever and we wanted to leave that for advanced datagrids in a future release.
Lately, due to some other work we’re doing for 3.0, I’ve been wondering whether footers, headers and locked columns and rows should be part of the chrome instead of the main set of cells. If so, it is possible to create footers in 2.0.1 today by subclassing DataGrid and making a special Border that can put renderers in the border.
Here’s an example that computes the average of the price column. Usual caveats apply.
Download source
Run demo
You can see how one could do column headers that way, or add headers to Tree for a TreeDataGrid.
Now, if the renderers are in the chrome, they are not part of the selection model, and they should probably not renderer data from the dataprovider per-se. So I’d be interested in knowing:
1) If you have used lockedColumns or lockedRows today, what have you used them for?
2) Did you want the lockedColumns or lockedRows to be selectable? Editable?
3) Did you do this to get more header rows/columns or to display data from the dataprovider?

56 Responses to DataGrid Footers

  1. Your post is very timely. I’m actually in the process of building a custom component that allows for fixed, hierarchical headings (such as those you’d see in a pivot table). For example, the column headings could show quarters grouped by years and the rows might be products grouped by categories. So far, I’ve got the row and column header hierarchy rendered in two separate mx:Grids (not DataGrid), taking advantage of rowSpan and colSpan (similar to Excel’s merged cells). The guts of the table uses an mx:DataGrid with some specialized ItemRenderers that delegate rendering based on the data passed in (think: charts and aggregations).
    I’m in the process of synching up the header GridItem dimensions with the corresponding data table cells and listening for scroll events on the main table to move the headers accordingly for the “fixed” effect.
    Any suggestions or further code samples would be much appreciated.
    ————-
    I wish I had more time to keep pumping out examples, but we’re in a busy period right now. Remember that the scrollEvent is propagated from the DataGrid so you can catch it and see what it did. Good luck.

  2. Sumanth says:

    Hi Alex,
    Its a really nice custom control. Is there any way by which, footer columns can also be resized and re-ordered based on the header? What is the best approach to do this?
    Regards,
    Sumanth
    ————
    I think if you call invalidateDisplayLIst on the border it should reset to the new sizes/order.
    -Alex

  3. Sean OToole says:

    I really hope that locked columns, subheads/summary rows,and parent child relationships are considered in a future version of the datagrid. I’ve seen crazy implementations of datagrids within lists, single column rows with custom renderers that use different component depending on data and other crazy things, but they are universally UGLY. Seems to me these items are now minimum requirements for a grid component.
    —————-
    Well, I wouldn’t say minimum, but yes, we are hard at work on those features.
    -Alex

  4. Greg says:

    For some reason the vertical lines are not drawing in the footer for me. They work in your example, but not when I integrate the ideas into my code.
    My hunch is that the overlay object is being obscured by something else, leaving a plain white area.
    I see the labels though in the footer, which leaves me confused.
    I realize it’s hard to answer without looking at how I’ve used your ideas, but do you have any thoughts on what would make the overlay not show the vertical lines?
    ——————–
    These kinds of problems can be hard to debug. I normally break on a mouse event and use the debugger to walk down through the variables and introspect the display objects. For instance, the overlay variable should show how big it is and how many children it has. You can also turn off the visibility of the renderers in case they are hiding the overlay.
    In the example, the line color comes from styles. You might want to debug through or trace out that you got the right color and boolean as to whether to draw them. Good luck.

  5. Greg says:

    I was unable to find out why the vertical lines were not drawing in the footer. I had already done some of the things that you suggested (line color/style, steping through to make sure it draws, etc), but not all of them.
    Ultimately I decided not to use this solution for column aggregates because when the DataGrid has a horizontal scrollbar, the scrollbar appears between the content and the footer.
    Instead I opted for putting the aggregates in a locked first row. Doing so was simple, modulo small behaviors like having to customize the sort functions to keep the aggregate row on top.
    Ultimately, it would be very nice if the DataGrid supported the concept of locked aggregate rows that appeared either on the top or bottom of the grid.
    ————–
    Yeah, it won’t work with horizontal scrollbar. It turns out that we’re refactoring DataGrid for the next release to do something like this, but instead of adding them to the border, we’ll be adding them to sub-“containers” in the DG.
    Good luck,
    -Alex

  6. Shaishav says:

    When can we expect feature of locked initial mutiple rows in Flex. Right now, lockedrow feature is locking the chrome but not the data. Therefore, when we sort new data appear in the locked rows.
    ———————–
    I don’t think we’ve planned for that. It sounds like you want the sort not to affect some number of initial rows? To me, that sounds like the initial rows aren’t really part of the data, but other header like things. One way to handle that is to wrap the original data in another collection that prepends the initial rows. Maybe I’ll get around to an example of that.
    In the next major release, it might be easier to set such a thing up, but it won’t be “automatic”

  7. Vive says:

    Hello,
    I tried to adapt you method to a datagrid I am currently developping, however I have problems when setting a custom renderer for the footer.
    The application goes into an infinite loop on the updateDislplayList method of the border object.
    Did you also encounter that problem ?
    —————
    Gee, it’s been so long I don’t remember. However this is a common problem where the renderer causes the parent to think its size changed. Check your measure functions to make sure they don’t change their mind or set an explicit width. You may also need to trap invalidation calls on the renderer so it doesn’t dirty the parent.

  8. Pedro Varela says:

    Well i’m using this component, but frecuently I get this error message when run the application.
    TypeError: Error #1009: No se puede acceder a una propiedad o a un método de una referencia a un objeto nulo.
    at ArenaComponents.FootDataGrid::FooterBorder/ArenaComponents.FootDataGrid:FooterBorder::updateDisplayList()
    at mx.core::UIComponent/validateDisplayList()
    at mx.managers::LayoutManager/validateClient()
    at mx.core::UIComponent/validateNow()
    at mx.controls.dataGridClasses::DataGridBase/mx.controls.dataGridClasses:DataGridBase::drawItem()
    at mx.controls.dataGridClasses::DataGridBase/mx.controls.dataGridClasses:DataGridBase::makeRowsAndColumns()
    at mx.controls::DataGrid/mx.controls:DataGrid::makeRowsAndColumns()
    at mx.controls.listClasses::ListBase/mx.controls.listClasses:ListBase::updateDisplayList()
    at mx.controls::DataGrid/mx.controls:DataGrid::updateDisplayList()
    at mx.core::UIComponent/validateDisplayList()
    at mx.managers::LayoutManager/::validateDisplayList()
    at mx.managers::LayoutManager/::doPhasedInstantiation()
    at Function/http://adobe.com/AS3/2006/builtin::apply()
    at mx.core::UIComponent/::callLaterDispatcher2()
    at mx.core::UIComponent/::callLaterDispatcher()
    ————
    Like I said, there could be bugs. You’ll have to debug into it and see what went wrong and add code to protect against this error. Good luck,

  9. Fabrice says:

    Hi Alex,
    Thanks for posting your code. I have used it and, as a Flex beginner, have a simple (I guess) question.
    I have set the datagrid to be editable. Now I would like to have the average value to be updated each time any cell is changed by the user. I caught the itemEditEdit(event) method but don’t then what to do 🙁
    Any help/comment would be appreciated if you have a couple of seconds to reply.
    Thanks a lot,
    Fabrice.
    ——————————
    In theory, if you call invalidateDisplayLIst on the footer it should redraw and compute your new values.

  10. Doug says:

    Alex,
    I have the same problem as Fabrice and would like to trigger the labelfunction to refresh itself on the itemEditEnd method. I called the invalidateDisplayList but it does not call the averageFunction. Any other thoughts?
    ————-
    Are you sure you called invalidateDisplayList on the footer? Calling it on the DataGrid won’t have any effect. I don’t have time to try it right now, but that should do it.

  11. Cedric says:

    Hi Alex,
    Thank you very much for this extremely useful code! But may I ask you to be a little more specific on your previous comment? How do we call invalidateDisplayList on the footer? My datagrid displays information which changes depending on the state of a ComboBox, and the footer should be updated accordingly.
    Thank you very much for your answer!
    ——————–
    it should be border.invalidateDisplayList()
    Border is protected so you have to be in the FooterDataGrid code, or add a method to FooterDataGrid so you can call it from the outside.

  12. Hadi says:

    Hi Alex,
    Thanks for all your great inputs.
    I’m trying to crate 2 DataGrids that affect each other’s selectedItems.
    The dataProvider is the same and presorted. The user select lines in one datagrid and this affects the selected items in the second DataGrid.
    I’ve tried to use data binding on selectedItems, and on change (or click) event change the binding Array. This worked only in one direction. Any thoughts?
    ——————
    I would not use binding. Simply listen for change events from each DG and update the selectedIndex of the other DG.

  13. Alex Woods says:

    Do you know of any way of having row headers in the same way there are column headers? Without putting the value in the dataprovider….
    —————
    In the next release this is supported so you may want to get the beta and try it out.
    For now, adding a dummy column with a labelFunction should allow you to have row headers.

  14. David says:

    The FooterDataGrid produces the error message “Property col1 not found on FooterDataGridColumn and there is no default value” when complex components such as checkboxes are used in an editable datagrid. The error message is being generated in SPSFooterBorder:updateDisplayList() method.
    —————–
    CheckBox and lots of other components are not valid header renderers without some subclassing. You can see how I modified ComboBox in another blog post to be a header renderer and work from there.

  15. David Grabell says:

    Let me rephrase my question with regard to complex components in a FooterDataGrid.
    I would like to create a DataGrid that has two columns:
    1. An editable column with CheckBoxes.
    2. A not editable column consisting of numbers.
    I would like to add a Footer that does not have any complex components. The first column would be blank and the second column has the total of all the numbers.
    Please advise on how I could tweak the FooterDataGrid.
    ————
    I would put a labelFunction that returns ” ” for the checkbox column.

  16. Karl says:

    Hi,
    I’d like to see a locked row that can be editable.
    It would be nice to add a row of filters.
    How can I do that? Thanks.
    ——————–
    Are you saying the footer should be editable? You can certainly make the header be a ComboBox or TextInput by borrowing from the CheckBoxHeader example.
    If you want the footer to be editable, it is doable but much harder. I’d go with headers.

  17. Cedric Schaller says:

    Dear Alex,
    I came across what seems to be a well hidden bug in your code. My Flex application takes the whole window and when I was resizing it, it used to crash with the following error:
    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at FooterBorder/FooterBorder::updateDisplayList()[C:\dev\spg\tamsDashboard\dev\flex\Dashboard;;FooterBorder.as:113]
    After some time, I figured out that this was due to a rounding error: in the while statement of line 83, xx was 424.999999999994 and w – wv.right 425. At that point, the while loop should actually have stopped, but it wouldn’t because of the rounding error. I fixed it by writing:
    while (int(xx + 0.5)

  18. Paul says:

    I like what you have done.
    I tried a number of things prior to posting, but haven’t solved the problem. The grid I have is populated from an external data set like so;
    grdVendorSummary.dataProvider = acVendorSummary;
    The user has the ability to select a new set of records, and re-execute this line. The totals in the footer do not change, I am using the a labelFunction to populate them.
    What would you recommend to resolve this?

  19. Paul says:

    Alex:
    I must be dumb as dirt cause I can’t figure out where/how to put the “border.invalidateDisplayList();” code into my app. I have tried putting it directly in the app, adding a function called refereshBorder() to FooterDataGrid, and calling it from my app, etc.
    Based on your previous comments, I assumed the latter way would be correct, but I get a compile time error 1061 on invalidateDisplayList through IFlexObject.
    If you would revisit this one more time I would appreciate it.
    I need to be able to refresh some totals when they are calculated.
    Paul

  20. Paul says:

    Alex:
    Well, perhaps I am at least as smart as a good sandy loam. I was able to solve the problem, but I would like to believe there is a better approach. I created a function;
    public function refreshBorder():void
    {
    this.updateDisplayList(0,0);
    }
    and put it into the FooterDataGrid class, and I call it whenever I need to refresh the data.
    I don’t really like the “this.updateDisplayList(0,0);” bit, and hope someone can tell me the right way of doing it.
    Paul

  21. Paul says:

    After flailing about for a while longer, I realized that the code I posted just previously, while synchronizing the footer charmingly, seems to make the header disappear. I think I have resolved that problem as well, but it still looks nasty to me. I present it here hoping that someone will show me the right way to do it;
    public function refreshBorder():void
    {
    if (this.measuredWidth + this.measuredHeight > 0)
    this.updateDisplayList(this.measuredWidth,this.measuredHeight);
    }
    Paul
    —————-
    I think it should just be:
    public function refreshBorder():void
    {
    this.invalidateDisplayList();
    }

  22. Paul says:

    I thought I had tried that during my flailing about, but just to be sure, I tried again. I commented out my two lines;
    if (this.measuredWidth + this.measuredHeight > 0)
    this.updateDisplayList(this.measuredWidth,this.measuredHeight);
    and replace it with;
    this.invalidateDisplayList();
    And while it did not give any errors, it didn’t seem to work either.
    I put my code back and it seems to work every time.
    One question, is there a way to force this function to execute whenever the datagrid’s data is updated?
    Paul
    ————
    strange. A call to invalidateDisplayList should result in a call to updateDisplayList.
    I would try hooking up a call to the border refresh in an override of invalidateDisplayList on the DataGrid

  23. sonu says:

    Worked like a charm. Keep up the great work.
    Thanks!

  24. Stefan Wisskirchen says:

    Hello,
    i use actionscript to generate a DataGrid:
    var tableMain:DataGrid = new DataGrid();
    var fieldsDataGrid:Array = new Array();
    for ( var j:uint = 0; j

  25. Stefan Wisskirchen says:

    Following code doesn’t work?
    […]
    fieldsDataGrid[j] = new DataGridColumn();
    […]
    fieldsFooterDataGrid[j] = new FooterDataGridColumn();
    fieldsFooterDataGrid[j].footerColumn = fieldsDataGrid[j];
    […]
    tableMain.columns = fieldsFooterDataGrid;
    But I don’t know why?
    Thank you for your help
    ———–
    It’s hard to tell from this w/o knowing what error you are getting. Please post your questions on FlexCoders as a few others have used this successfully and may be able to help as well. Post more code and what error you are getting.

  26. Jon says:

    You mentioned in response to an earlier post that Row Headers in a dataGrid are supported in Flex 3. I have downloaded the beta, but can not find how to enable Row Headers in a dataGrid. Can you please explain?
    —————
    It should be in AdvancedDataGrid

  27. Paddy says:

    There’s been a couple of comments relating to problems dynamically using FooterDataGrid with AS. As far as I can see, the problem is that in your example you’ve wrapped DataGridColumns in FooterDataGridColumns – something they haven’t replicated in the AS, and indeed cannot (? – note I’m fairly new to Flex) because we can’t do .addChild on the FooterDataGridColumn.
    My solution involved adding a footerLabelFunction property to the FooterDataGridColumn and editing FooterBorder to utilise this. Then the DataGridColumn’s can be left out.
    ————–
    You can do anything in AS that you can do in MXML, so that can’t be the issue. I think it just isn’t clear how to get the footer to update. It should just be a call to border.invalidateDisplayList().

  28. Paddy says:

    – actually I left footerColumn in so as to not have to play around with the renderer.
    public class FooterDataGridColumn extends DataGridColumn
    {
    public function FooterDataGridColumn()
    {
    super();
    footerColumn = new DataGridColumn();
    footerColumn.headerText = this.headerText;
    footerColumn.dataField = this.dataField;
    footerColumn.labelFunction = footerLabelFunction;
    }
    public var footerColumn:DataGridColumn;
    public var footerLabelFunction:Function;
    }

  29. Daniela says:

    Alex, thanks for this nice example. Unfortunately, I have a datagrid that needs to be scrolled horizontally.
    I saw that Greg mentioned another approach that sounds like the way to go for me right now. But I don’t know how to get in touch with Greg. Do you?
    I am also currently looking into the AdvancedDataGrid in Flex3Beta2 and are disappointed that the summary row can only be used on hierarchical data grouping. I have a straight forward flat datagrid that needs to display aggregates for some columns.
    I hope that the simple case (flat data) with aggregates will be implemented as well..
    Thanks for any help you might be able to provide me .
    ———————-
    In theory, you can add a footer ListBaseContentHolder to a subclass of DG in 3.0, but I haven’t proven it out.

  30. takiz says:

    the datagrid footer headertext can’t be dynamically change after first initialized, even using data binding also wont be work!
    —————–
    It should be a matter of calling invalidateDisplayList on the border

  31. António Inácio says:

    Thanks for the code!
    I belive i can contribute with something too: Footer resizing.
    Apply this changes to FooterDataGrid class
    import mx.core.UIComponent;

    private function footerColumnStretch(ev:DataGridEvent):void
    {
    (border as UIComponent).invalidateDisplayList();
    }
    public function FooterDataGrid()
    {
    super();
    this.addEventListener(DataGridEvent.COLUMN_STRETCH, footerColumnStretch);
    }
    BTW: Not quite shure if the cast to UIComponent is the best choice but it works for me.
    Regards, António Inácio

  32. takiz says:

    i modified it and used it onto a AdvancedDataGrid (flex3), everything is fine but except i attempts to refresh the footer border, althought i have call invalidateDisplayList to refresh it. doesn’t it worked fine on ADG?
    ————-
    Haven’t tried it on ADG. I would implement something completely different in Flex 3. I haven’t had time to create the example yet.

  33. Pranoti Patil says:

    i had one DropDown on form.On it’s selectedIndexchanged i have to fill the dropdown in footer of the grid control
    if any one had the solution for it then told me
    ——————
    Find a way so the dropdown in the footer knows about the one in the form. A subclass could have an extra property that gets filled in by the property bag on the classFactory that references the dropdown in the form.

  34. i have used the footer. it really nice and help me lot. but when ever i define any renderer in datagridcolumn then code seem not to be working. Niether it give error message nor screen. it seem that code goes in an infinite loop. what is reason for that.
    —————-
    Not sure. You’ll have to debug into it and see what is going on.

  35. Srikanth says:

    I have applied the footer to the ADG and it works great, besides some minor issues. (resize the columns so that you see only part of the last column, the vertical lines or the footer text are rendered outside the datagrid boundaries)
    But i am curious about your comment “I would implement something completely different in Flex 3. I haven’t had time to create the example yet.”
    Why and how would you do this ?
    —————
    In Flex 3, we refactored DG to have separate containers for the locked columns, rows, etc. This should make it easier to add a footer container which would then show up correctly when the horizontalScrollBar is used.

  36. Charles says:

    I tried this great work with my own customized ItemRenderer. I set the ItemRenderer for each column of the DataGrid. Unfortunately it doesn’t work. It seems that the value passed to the ItemRenderer is not set correctly in my case. I am sure that the ItemRenderer I use is working correctly before I use the FooterDataGrid.
    So I am wondering has anybody been making the footer work successfully with customized ItemRenderer? Do we need to change any code?
    Thanks. 🙂
    ———————
    A custom renderer in the footer has to understand that the .data property is the FooterDataGridColumn and use that to display the correct information

  37. Bryan says:

    I’m looking to extend the ADG to support a footer row at the bottom that behaves like the headers; I don’t necessarily need to access data from the dataProvider (but I think this sort of functionality might be desirable in many cases). I’d rather not hijack the border if possible. You mention that you’d use a different approach for the ADG in Flex 3, perhaps using ListBaseContentHolder. Any chance you could give some more insight to point me in the right direction?
    Many thanks,
    Bryan
    ————————-
    Alex responds:
    I just posted a way to do this here: http://blogs.adobe.com/aharui/2008/03/flex_3_datagrid_footers.html

  38. Lisa says:

    Hi Alex ,
    nice article
    i am a flex newbie can u tell me how to determine the boundaries of a cell in advance datagrid
    i have been looking for it for quite some time now
    i guess we shud be using local X Y coordinates but am not really sure how to use it
    any help on it will be appreciated a lot
    thanks & Regards,
    Lisa
    ——————————–
    Alex responds:
    It is rare that you need to know the bounds of a cell from outside the cell. You might want to think about your design and approach to the problem. Normally renderers handle any special interaction and they know their bounds. If a subclass needs to help with interaction, usually that is handled in mouseEvent handlers and the event.target is used to determine the bounds.
    You might have better luck posting your question on FlexCoders.

  39. Sean Hess says:

    Footer DataGrid – display a total or average summary footer
    […] Alex Harui deserves big credit for his proof-of-concept post on datagrid footers … However, I found that it did not support multiple rows (I needed total AND average), and I noticed a few bugs when the width or height was set to 100%. Once I got started changing it, I couldn’t stop, and the FooterDataGrid is the result. […]

  40. marco says:

    Hi, i use your source..and it’s fantastic, but i’ve a problem: how can i hide or not the footer column?? There is a way to set the visibility?
    Bye
    Marco
    ————————-
    Try setting it’s visibile=false or maybe height=0 at the same time.

  41. Sunil says:

    var newCols:Array = dataGrid.columns;
    var cols:Array = new Array();
    var visibleCol:Number = 0
    for (visibleCol;visibleColvar cols:Array = datagrid.columns in the DatagridFooter.as

  42. Spoofan says:

    My solution to itemRenderer problem and endless loop:
    override invalidateDisplayList and invalidateSize functions like this
    override public function invalidateDisplayList():void {
    if (!invalidateDisplayListFlag) {
    invalidateDisplayListFlag = true;
    }
    super.invalidateDisplayList();
    }
    ….and
    override public function invalidateSize():void {
    if (!invalidateSizeFlag) {
    invalidateSizeFlag = true;
    }
    super.invalidateSize();
    }
    ….
    then write your own invalidateDisplayList function,
    private function myInvalidateDisplayList():void {
    invalidateDisplayListFlag = false;
    super.invalidateDisplayList();
    }

    function myInvalidateDisplayList() call it in updateDisplayList function of FooterBorder.as after drawing vlines (line 111) and it should work.
    Hope, it will help someone, I spent hours solving this

  43. nert says:

    This is great, it helps me a lot for my project. Thanks for the post Alex. But there’s one more thing I want to achieve. I wanted this footer to have a different background color, I already tried setting the backgroundColor property/style but nothing happen. I am new in Flex, and I don’t know much about styling a flex component, could anyone give a hand here please., thank you in advance.

  44. Alex Harui says:

    You may need to place a colored object behind the footer text widget.

  45. sadanand says:

    hey Alex gr8 stuff, man, but is there any built in in ver 3.x, waiting your kind reply

  46. sana says:

    Can I have an eg of advanced datagrid with grouping and footer,

  47. G says:

    Hi. could you please guide me how to use the DataGridItemRenderer with the SummaryColumn tag’s itemRenderer property. I have created an extended class from DataGridItemRenderer but it doesn’t load.

  48. Robin says:

    I am trying to use the excludeFields in a

    because there are certain fields that will not make sense to sum. How do you use this feature. Or am I trying to use this in the wrong way?

  49. Vic says:

    How do it do MXML part of this example in AS3 ?
    I can not use any MXML in the app im working on , only AS3

    • Alex Harui says:

      You can always see the AS equivalent by using the -keep-generated-actionscript MXMLC option

  50. Joe Quinn says:

    Update the FooterDataGrid class so the constructor is as below and also add the refreshFooter method. Thsi means column resizing will work and also the footer gets updated when the collection changes.

    public function FooterDataGrid()
    {
    super();
    this.addEventListener(CollectionEvent.COLLECTION_CHANGE, refreshFooter);
    this.addEventListener(DataGridEvent.COLUMN_STRETCH, refreshFooter);
    }

    private function refreshFooter(ev:Event):void
    {
    (border as UIComponent).invalidateDisplayList();
    }