views:

3288

answers:

2

I've noticed that the default behaviour for a DataGrid's vertical scroll bar is to scroll one row at a time. This is all well and good when the rows are all uniform and small (e.g. displaying a single line of text), but gets really ugly as soon as you have rows with variable heights.

I'm curious, is there a way to make DataGrid scrolling "smooth"? For instance, is there a way to have the DataGrid scroll by a set number of pixels, lines of text, etc. rather than scrolling one row at a time?

So far, the only solution I've managed to come up with is to place the DataGrid in a Canvas and have the Canvas do the scrolling instead of the DataGrid. The issue with this approach, though, is that as soon as the Canvas scrolls far enough, the DataGrid headers scroll off-screen. Ideally, I'd like to get the smooth-scrolling nature of the Canvas, but also keep the DataGrid headers visible. Is that possible?

+1  A: 

The way that ItemRenderer's work in Flex 3 makes smooth scrolling difficult to achieve. Basically Flex recycles item renderers scrolled off of the top of the list as the display objects used for new data at the bottom of the list. Adobe's implementation of most list components in Flex 3 creates and adds these items as they come on to the screen rather than just off the screen, so they "pop in" and smooth scrolling isn't available. I'm not sure why they couldn't have done it in a similar manner for items +/- one position above or below the current scroll pane, but they didn't, and we're stuck with sticky scrolling by default.

Work-arounds do exist, though the one you've noted (dropping the datagrid into a canvas) negates the display-object saving intention of item renderers and incurs a performance cost. This will be fixed for most list-based Flex components in Flex 4, though it won't be fixed immediately for DataGrid. The DataGrid / AdvancedDataGrid component is maintained by a separate team based in India, last time I heard, and so it tends to be a bit behind the rest of the SDK.

I'd recommend trying something similar to this implementation of a smooth-scrolling list by Alex Harui. I'm not sure exactly how well it'd work for DataGrid or AdvancedDataGrid, but this is the most intuitive technique I can think of for making the list scroll correctly.

RJ Owen
Very informative answer, thank you! It's too bad this can't be done currently, though I understand why (reusing itemRenderers is very handy!). Sadly, Alex's example won't work for me as my DataGrid does have variable row heights. I guess I'll have to wait for a future release of Flex to get this functionality.
Dan
In fact most platforms use the same technique of reusing cells rather than creating all the cells for the whole data grid. E.g. iPhone sdk follows the same logic, you only have the certain number of cells in memory, enough to fill the area that's currently displayed. However smooth scrolling works just fine by having a couple of extra cells rendered off the screen and bringing them smoothly into the user's view as we scroll. Also hoping to get that kind of scrolling from DataGrid soon *fingers crossed*
Nick
A: 

Try this... It's still based on Alex's code that was mentioned above. His should still be a great start for removing the snap-to-row behavior. Original source: http://blogs.adobe.com/aharui/2008/03/smooth_scrolling_list.html

Alex's original some code for smooth vertical scrolling but that was not an issue I had with the DataGrid. It was smooth scrolling horizontally that I needed. I am using the DataGrid in an unorthodox manner for analyzing plain text reports output by our database (great way of providing visual feedback on a document). The code below allows content to go off screen and the user can scroll without that snap-to-column behavior.

You can adapt this to use the same math routines for vertical scrolling and then it will make scrolling possible and ignore the snap to row behavior. In particular switch the usage of the listContent.move method to move the contents vertically and use a inverse of the rounded pixel value you calculate from the vertical scroll bar (as opposed to my using the horizontal).

This method is bit simpler than Alex's method from the link above - a lot less code so try adapting and see how it works.

override protected function scrollHandler(event:Event):void
        {           
            // Override the default scroll behavior to provide smooth horizontal scrolling and not the usual "snap-to-column" behavior
            var scrEvt:ScrollEvent = event as ScrollEvent;
            if(scrEvt.direction == ScrollEventDirection.HORIZONTAL) {
                // Get individual components of a scroll bar for measuring and get a horizontal position to use
                var scrDownArrow:DisplayObject = horizontalScrollBar.getChildAt(3);
                var sctThumb:DisplayObject = horizontalScrollBar.getChildAt(2);     
                // I replaced maxHorizontalScrollPosition in Alex's code with "1300" to fix my exact application. In other situations you may finding using some property or different value is more appropriate. Don't rely on my choice.
                var hPos:Number = Math.round((sctThumb.y - scrDownArrow.height) / (scrDownArrow.y - sctThumb.height - scrDownArrow.height) * 1300);     

                // Inverse the position to scroll the content to the left for large reports
                listContent.move(hPos * -1, listContent.y);
            }
            // Go ahead and use the default handler for vertical scrolling
            else {
                super.scrollHandler(event);
            }
        }
Matt R.