Faster DataGrid Horizontal Scrolling

Lately, I’ve heard many complaints about horizontal scrolling performance in DataGrid. Vertical scrolling has been optimized, but horizontal has not. I found a bit of time to put together how to subclass DataGrid and try to optimize horizontal scrolling. I’m sure there’s bugs and I don’t know if I’ll have time to fix them and the usual caveats apply.
Hope it helps. It should work with any Flex 3 SDK version.
Download Source
In the example, the top DataGrid is not optimized, the bottom one is. Make the screen bigger and you’ll start to see and feel the difference.
Run Example

47 Responses to Faster DataGrid Horizontal Scrolling

  1. James says:

    Hi Alex,
    I got the following error while testing the bottom one.
    TypeError: Error #1010: A term is undefined and has no properties.
    at BetterDataGrid/scrollLeftOrRight()
    at BetterDataGrid/set horizontalScrollPosition()
    at mx.controls::DataGrid/scrollHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    at mx.controls.scrollClasses::ScrollBar/http://www.adobe.com/2006/flex/mx/internal::dispatchScrollEvent()
    at mx.controls.scrollClasses::ScrollThumb/mouseMoveHandler()
    ——————–
    Alex responds:
    I couldn’t reproduce this exact stack trace, but I found and fixed a related bug that might fix this one as well. Examples and source were updated around 9:24PM PST

  2. Jamie Badman says:

    There’s a problem with th ebottom one, I think – errors when I vertical scroll…
    Jamie.
    ——————
    Alex responds:
    I found and fixed one problem. The examples and source have been updated around 9:24PM PST

  3. Asbjørn says:

    I sometimes get this error when scrolling in the bottom grid:
    TypeError: Error #1010: A term is undefined and has no properties.
    at BetterDataGrid/sumColumnWidths()
    at BetterDataGrid/scrollLeftOrRight()
    at BetterDataGrid/set horizontalScrollPosition()
    at mx.controls::DataGrid/scrollHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    at mx.controls.scrollClasses::ScrollBar/http://www.adobe.com/2006/flex/mx/internal::dispatchScrollEvent()
    at mx.controls.scrollClasses::ScrollThumb/mouseMoveHandler()
    I’m using FlashPlayer 10 (debug version). I’m not really sure how to reproduce this, though.
    ——————-
    Alex responds:
    OK, I’ll look into it

  4. Marcin Glowacki says:

    Thanks for putting this together. It is much appreciated!

  5. Danny Gold says:

    What would it take to get this working with the AdvancedDataGrid? I think some of the methods referenced are a little different, but I’ve got a fairly massive AdvancedDataGrid that is having some scrolling problems.
    —————————
    Alex responds:
    AdvancedDataGrid is a different team, but I took a quick look at their code and this example might just work as is

  6. Did you find the solutions?
    TypeError: Error #1010: A term is undefined and has no properties.
    at BetterDataGrid/scrollLeftOrRight()
    at BetterDataGrid/set horizontalScrollPosition()
    at mx.controls::DataGrid/scrollHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    ———————-
    Alex responds:
    I posted new code a few days back. If you still have this problem, provide some info as to how you got it.

  7. Hello, Alex!
    Excellent example, thank you.

  8. Sven says:

    Vertical scrolling may have been optimized but I feel there is room for improvement by providing smooth vertical scrolling instead of jumping from one row to the next. Hopefully this is a feature that is getting some attention.
    ———————
    Alex responds:
    Vertical scrolling should be pixel based in Gumbo

  9. Kilew says:

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.controls.dataGridClasses::DataGridBase/addToFreeItemRenderers()
    at BetterDataGrid/shiftColumns()
    at BetterDataGrid/scrollLeftOrRight()
    at BetterDataGrid/set horizontalScrollPosition()
    at mx.controls::DataGrid/scrollHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    at mx.controls.scrollClasses::ScrollBar/http://www.adobe.com/2006/flex/mx/internal::dispatchScrollEvent()
    at mx.controls.scrollClasses::ScrollThumb/mouseMoveHandler()
    How to reproduce:
    —-
    Hold mouse on vertical scroll, and do some scrolling. Then, (Don’t up mouse button, before you’ll be on horizontal scroll bar), try to scroll horizontally. Try to do it so fast as possible 🙂
    There’s 2 exceptions, but I think source is same.
    ————————-
    Alex responds:
    Ok thanks.

  10. nathan rogan says:

    Alex- What approach would you recommend taking in optimizing the AdvancedDataGrid hScrolling?
    ———————
    Alex responds:
    The code might work if you subclass ADG instead of DG. Give it a try and let us know

  11. nathan says:

    Sorry I should have mentioned that I had done that already. There are a few protected methods in the DG that are not available in the ADG
    setupColumnItemRenderer layoutColumnItemRenderer
    Also sub classing the ADG, there is no direct access to the header object.
    I believe the above methods as well as the header object are fully encapsulated before the ADG class. However i am looking into
    override mx_internal function getHeaderInfo(col:AdvancedDataGridColumn):AdvancedDataGridHeaderInfo as a possible way to get the header info yet that still leaves the rendering methods unavailable.
    ———————-
    Alex responds:
    I don’t have time to look into it right now. Post on Sameer Bhatt’s blog and see if he can help you.

  12. Denis says:

    Alex,
    Thanks for this fantastic component. I found a bug that you can easily replicate if you make the number of rows less than the vertical visible area. The “null” rows that fill the remaining area under the rows with data create a problem during the addToFreeItemRenderers call. There was a post above with a similar error from Sven. The error is:
    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.controls.dataGridClasses::DataGridBase/addToFreeItemRenderers()
    at BetterDataGrid/shiftColumns()
    at BetterDataGrid/scrollLeftOrRight()
    at BetterDataGrid/set horizontalScrollPosition()
    at mx.controls::DataGrid/scrollHandler()
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()
    at mx.core::UIComponent/dispatchEvent()
    at mx.controls.scrollClasses::ScrollBar/http://www.adobe.com/2006/flex/mx/internal::dispatchScrollEvent()
    at mx.controls.scrollClasses::ScrollThumb/mouseMoveHandler()
    ——————————
    Alex responds:
    OK, thanks. Not sure when I’ll get to it. Feel free to post your solution.

  13. Xinyu says:

    Alex,
    Thanks for your great work!
    I got a Error #1009 message with your example. When I try to move the vertical scrollbar to the end, and then move the horizontal scrollbar to left. This error message occurred. I found the problem might be caused by the source code BetterDataGrid.as in line 104. I just replaced it with ‘var rowCount:int = rowInfo.length – 1;’. So far, it works fine!
    ——————–
    Alex responds:
    OK, thanks!

  14. Oliver says:

    Hello Alex,
    Thanks for putting this example together, the performance improvements are remarkable. I thought I’d share a number of small fixes I’ve added:
    1. as some previous posters noted, ‘null’ renderers in empty rows cause an error #1009 to be thrown. I fixed this with these handful of lines in scrollLeftOrRight():
    if (scrollUp)
    {
     …
     for (i = 0; i if(numCols == 0) //empty row
       continue;
      …
     }
     …
     for (i = 0; i if(iterator == null || iterator.afterLast || !iteratorValid)
       continue;
      …
     }
    }
    else
    {
     …
     for (i = 0; i if(numCols == 0)
       continue;
      …
     }
     …
     for (i = 0; i if(iterator == null || iterator.afterLast || !iteratorValid)
       continue;
      …
     }
    }
    The solution posted by another reader (changing line 104 to ‘var rowCount:int = rowInfo.length -1;’) gets rid of the errors, but also causes the last row to not be updated correctly when scrolling.
    2. When the first itemRenderer of a row is freed via freeItemRenderer() (which happens when the rows get moved to the left), the listContent.visibleData entry for that row is deleted, which prevents the row highlight from rendering when the mouse cursor moves over a row. To fix this, I modified the shiftColumns() function as follows:
    var uid:String = itemToUID(listItems[rowIndex][0].data);
    for (var i:int = 0; i //rebuild the listContent.visibleData map entry
    listContent.visibleData[uid] = listItems[rowIndex][0];
    3. The horizontal scrollbar doesn’t get updated properly if the table contains variable column widths. This one-liner in set horizontalScrollPosition() fixed this for me:
    // if the flag got tripped run our new technique
    if (canUseScrollH)
    {
     scrollLeftOrRight();
     configureScrollBars();
    }

    4. In some situations where there are columns of varying width, the (updated) value of visibleColumns.length can become smaller than deltaPos by time we get to scrollLeftOrRight(). For example, if you can currently see 10 narrow columns and the previous 10 columns are significantly wider such that only 2 fit at a time, then if you drag the scrollthumb over such that deltaPos = 3, the initial check passes (since deltaPos scrollLeftOrRight():
    if(scrollUp)
    {
     …
    }
    else
    {
     numCols = listItems[0].length;
     if(deltaPos > visibleColumns.length)
      deltaPos = visibleColumns.length;

     moveBlockDistance = sumColumnWidths(deltaPos, false);
     …
    }
    5. This one I’m not actually sure if it’s necessary, but I slightly modified the lines in scrollLeftOrRight() that toss excess columns so that the number of itemRenderers in a row always matches visibleColumns.length:
    if(scrollUp)
    {
     …
     for (i = 0; i while (listItems[i].length > visibleColumns.length) //instead of ‘if’
      {
       addToFreeItemRenderers(listItems[i].pop());
      }
     }
    }
    else
    {
     …
     for (i = 0; i while (listItems[i].length > visibleColumns.length) //instead of ‘if’
      {
       addToFreeItemRenderers(listItems[i].pop());
      }
     }
    }
    It seems to run pretty robustly now. Hope this information is useful!
    ————————–
    Alex responds:
    Thanks!

  15. Dharmendra says:

    Hi ALex,
    I see one strange issue with your components.The issue is data of one column gettting REPLICATED on another column adjacent to it.Please have a look in to it as i am using this components in a critical application and my client was the performace.Now I became dependent on this componets. I will also create a sample to demotrate this defact and post to you.
    To Reroduce it you have to put at least 100 object into dataptovider
    ————————
    Alex responds:
    These examples are unsupported. I cannot guarantee a fix. Others are using this component so maybe they can offer help. Try posting on an Adobe forum or FlexCoders.

  16. Alex,
    Thanks for this very interesting and useful component. Do you have donation count? I want to send you some money.
    —————–
    Alex responds:
    Currently, Adobe does not allow us to make extra money from our blog content. Go spend it on the needy.

  17. Tweety says:

    Hi Alex,
    thx for this example.
    I tried to use your component, but unfortunately it doesen’t work with variable DataGridColumns. I added the methode “configureScrollbars” posted by XinYu. But even this doesen’t work reliable. The ScrollBar changes each time its width. If you scroll to right, it gets shorter and vice versa. I would be glad to get some advice 😉
    ——————————–
    Alex responds:
    Not sure what you mean by “variable” DataGridColumns. It is normal for the scrollBar to changes its thumb width depending on how many visible columns there are on screen.

  18. Puneet says:

    Hi Alex,
    The code you provided works great but if we have locked rows and try to scroll the grid horizontally, the grid messes up. The lockes rows stays still and unlocked rows only move. Can you please tell me how to fix this?

  19. Alex Harui says:

    There is a separate container of locked rows. You’ll have to perform the same operatiosn on that other container.

  20. Puneet says:

    Hi Alex
    Could you please guide me a little bit more w.r.t code. I mean which container i should write the operation etc. I really appreciate your help in this regards.

  21. Alex Harui says:

    The other container should be lockedRowContent

  22. Carlo says:

    Hi Alex,
    First, thanks for this great code. I was able to to do a lot of work using this code as a starting point. Unfortunately, I have run into a problem. I recently upgraded to the 3.4 SDK and now when I run my datagrid, the rightmost column header (usually it is partially visible) is missing its title. I went back and ran your original source and see the same issue when I scroll to the last column. As one final confirmation, I compiled the code in 3.3 and the issue disappeared. Any ideas? Thanks in advance for any time or support you can give with this item.

  23. Alex Harui says:

    In 3.4 header renderers are recycled. This means that you can no longer use the same DataGridColumn in two different DG’s which the example does.

  24. ichanob says:

    Hi Alex,
    I’ve got the same issue as the Carlo’s one.
    To reproduce the issue, from your example:
    1- I’ve just kept the ‘BetterDataGrid’ Datagrid with width=”500″
    2- I modified
    private const NUM_COLS:int = 5;
    3- I added :
    for (j = 0; j DataGridColumn(cols[j]).width = j*100;
    }
    If you run this test and scroll right one time, you will see the ‘col3’ headerText disappear.
    Do you know how I can fix this bug?
    Thanks a lot.

  25. Alex Harui says:

    Try the 3.5 nightly builds.

  26. ichanob says:

    It works fine with the 3.5 nightly builds !! Thanks a lot Alex!
    Do you know if the horizontal scrolling optimization will be implemented in flex4?

  27. Alberto says:

    Hi Alex,
    We’re using 3.2 sdk and we have detected serious performance problems with the Datagrid horizontal scrolling. Profiling the application, we can see a lot more itemRenderers created than needed. This problem is magnified as soon as more columns are added to the datagrid. Of course, itemRenderers are not being recycled correctly by the Datagrid.
    I’ve also downloaded and tried the 3.5 nightly build with similar results.
    So the question is: Is there any way to optimize this horizontal scrolling and reduce the number of item renderers created and make them recycle correctly and in an optimized way? In the profiling, I can see a correct number of DataGridListData instances created but always wrong and non-recyclable item renderers created, even using default of custom blank renderers extending from uicomponent. The point is, there should be equal number of item renderers than DataGridListData.
    Alex, we all need a patch for this because Datagrid is so important in almost every project. I can live with coding custom uicomponents for my item renderers but I need a good recycling strategy on the sdk side.
    What’s your opinion?
    Thanks in advance.

  28. Alex Harui says:

    No changes for horizontal scrolling in DataGrid in Flex 4. After Flex 4, we’ll work on a Spark-based DataGrid and hopefully have better scrolling then.

  29. Alex Harui says:

    There can be more renderers that DataGridListData’s. Some are kept off-screen for measurement or for scrolling. Shouldn’t be more than one or two per column though, so if you’re seeing continued growth, file a bug.
    Make sure the renderers are as lightweight as possible.

  30. Alberto says:

    Hi Alex,
    I believe I big amount of people is waiting for a better and optimized Datagrid control since since years ago. I’ve seen so many bugs and performance complaints on this control that is hard for me to understand why Adobe has not already reacted on this issue.
    It’s not a workaround to code our item renderers from uicomponent and following optimized display rules if this control is not recycling them correctly. The number of renderer instances grows erratically depending on the columns and rows showed and we just can’t do anything to fix it.
    I really encourage you to put a great effort on the new spark components because it’s so important that the core component set is light, secure and performant…
    Thanks

  31. alisveris says:

    It works, no error. Excellent work, thanks alot.

  32. Vikram says:

    Hello Alex,
    Is there any workaround for AdvanceDataGrid??? Please suggest if it can be resolve wit that.
    Thanks,Vikram

  33. Alex Harui says:

    AdvancedDataGrid is developed by a different team, but I would think that the same principles would apply to it.

  34. Vikram says:

    Could you please try once??? I tried to implement that into advancedatagrid but it has issue with few methods, those are not exists into advancedatagrid.
    I have been trying to resolve this issue for more then a month….but still not got success, therefor I request you if you can help me……
    Please forward me to if you find something valuable: vikram.jadoun@gmail.com
    Thanks, Vikram

  35. Vikram says:

    Hi Alex, Can you please try with that solution? I was trying that but not got success.

  36. Shailendra yadav says:

    Hi,
    My app dealing with lot of XML data, I am assigning it to respective TextInput, after filtering.Problem is that now my app got very slow.
    How can I make it fast??
    Thanks,
    Shailendra singh

  37. Jason Goris says:

    Hi Alex, great work on the grid improvements. Is there a way to stop the vertical scrolling at the last element? For example, if you scroll to the bottom of the vertical scrollbar, you get about 1/2 of a row of empty cells. I am using custom renderers and the rows are about 60 pixels high, so when I scroll to the bottom I get my last row, plus about 30 pixels of a “last last row” of no data. If I then do any horizontal scrolling with this last ‘fake’ row showing, I get null pointer type errors. If I use a normal DataGrid control, I don’t see this ‘extra’ half row at the bottom.
    Thanks and great work again!
    Jason

  38. Alex Harui says:

    Unfortunately, I just don’t have the time.

  39. Alex Harui says:

    Use the performance profiler to see what is taking all the time

  40. Alex Harui says:

    The normal DG will also show partial last row if the DG’s space to show an integer number of rows. You can fix that by changing the size of the grid. You could also try mucking with maxVerticalScrollPosition, but I’m not sure that won’t cause other problems.
    Best would be to figure out why you get an error and adjust for that.

  41. nmarton says:

    Hi Alex, Oliver, everyone
    Ive just tried/tested it, after applying(exactly) the 5 fix provided by Oliver it works, and its amazing!
    As an advice to all who are also suffering from datagrid performance issues: use this remarkable sollution combined with UIComponent based, low level implemented, setStyle aware itemrenderers!
    Thank You, Alex!
    Thank You, Oliver!

  42. Sebastien V. says:

    Hi everyone.
    First of all I’d like to thank Alex and Oliver for the implementation and its bug fixes.
    I found a way to reproduce the error #1010:
    Once the grid has been populated with data, but with no vertical scrollbar shown, stretch one of the displayed columns from its right border, as far as you can on the right side of the DataGrid.
    The right border of the stretched column should now be at the same position as the right border of the DataGrid.
    Now try scrolling to the right, and the error will show up, breaking at this line of code:
    curX = listItems[ 0 ][ firstNewColumn – 1 ].x + listItems[ 0 ][ firstNewColumn – 1 ].width;
    (in the scrollLeftOrRight method)
    I looked into it, and found out that the visibleColumns Array then contains the first not visible column, but the listItems does not contain the columns itemRenderers… So the code tries to access the [ n ] element in an Array of length n.
    To fix this, just add the lines:
    if ( listItems[ 0 ].length < firstNewColumn )
    firstNewColumn = listItems[ 0 ].length;
    between the two lines:
    var firstNewColumn :int = lastNumberOfColumns – deltaPos;
    and:
    curX = listItems[ 0 ][ firstNewColumn – 1 ].x + listItems[ 0 ][ firstNewColumn – 1 ].width;
    And you can kiss the #1010 error goodbye…

    • Amit Dhok says:

      Sebastien,

      You deserve a chocolate cake dude……
      Simply awesome….

      Thanks a ton….
      Great catch…

      Thank Alex and Oliver for the implementation and its bug fixes.
      Great work guys…

      Amit

  43. Priya says:

    Hi Alex,

    I was able to implement your idea into ADG by tweaking the SDK..But I don’t see any improved scrolling performance,as compared to that of BetterdataGrid.Is ADG optimized to cache the headerRenderer as in DG.What would be the equivalent of the DataGridHeader class in ADG?

    Thanks..
    Priya

    • Alex Harui says:

      It looks like ADG adds the headers to the same parent and the other renderers. I did notice that there is a getHeaderRenderer method that might be useful for caching the header renderer

  44. Antony says:

    Hi, how can i implement this method in AdvancedDataGrid? AdvancedDataGrid has now setupColumnItemRenderer layoutColumnItemRenderer

    • Alex Harui says:

      Not sure how hard it will be. You can try asking for help on the forums or maybe Sameer Bhatt’s blog