Virtual Scrolling

Last Update: 2020-10-29 (version 2.12)

2018-5-18: Available on SourceForge:

bll 2020-2-15 There seems to be some confusion about the intent of this package. It is not intended to be a replacement for tktable or treectrl or tablelist. It is intended to be a virtual scrolling solution that could be used by one of those type of packages or for any other display of scrolling multiple rows by an application.


  • Set a number of lines as reserved. These lines will stay at the top of the display and not scroll (setReserved).
  • Set the page overlap for the page up/down keys (setPageAdjust).
  • Row 0 is reserved as a heading line.

Since the configuration and population of each row is done within your callback procedures, you can do most anything you want to your display.

In the following image, you can see the heading line, a reserved line (the line in bold text, showing the song's original data), and the scrolling area. The populate row callback changes the highlight depending on which row is selected. Each line includes a radio button, frame (colored square), and labels.


bll 2014-8-23 This is a virtual scrolling solution that does not use a frame wrapper or canvas wrapper. The scrolling frame and scrolling canvas wrapper solutions are all quite memory intensive, as many rows of widgets are generally created ahead of time. Destroying the rows that are no longer displayed is not a solution, as destroying and creating new widgets is very slow. My initial try with a scrolling frame solution failed with more than a couple of thousand rows (due to the large amount of memory needed for the widget display), and I had the need to handle more than 30000 rows.

The code uses two callbacks, one to configure a row with the widgets, and one to populate the data. Each displayed row's widgets are configured with the configuration callback, and each row is populated with the populate callback. When the region is scrolled, the populated data is changed, and the widgets are left in place.

For basic scrolling areas, the configure callback is only executed on initialization and when the window is resized.

The 'reconfigure' and 'reconfWidget' procedures are used for complex displays (e.g. mixed headings and widgets) as in the example ('testsd3.tcl'). The 'grid forget' command is used to avoid slowness associated with destroying and creating new widgets. This requires some extra widget management.

The code handles multiple scrolling regions in a single window.

Row 0 is left available for a heading line (which is not scrolled).

It is also possible to reserve more heading lines.

Scrolling by dragging (moveto) is sped up by using a short delay before redisplaying.

A new set of data can be displayed by simply calling the 'display' procedure again.


It even works with a million entries (just change the number in 'test2.tcl'), which are built up within two seconds. Really good performance! HolgerJ, 2015-12-27

moi 2016-10-25 Not good my man. It sounds good but to bad I cant try it. it says tcl/Tk 8.6 needs it.

bll 2016-10-25 Actually it needs 8.5 (for {*}), so that's a mistake.

moi 2016-10-26 I travel today without my desktop. On my laptop, it says invalid command ::oo::class. I belive errors is here. It wants verson 8.6. Not so?

bll 2016-10-26 You are right. I believe the OO package is available as a separate package for version 8.5. You can use the prior version ( ), which does not require OO. Though it will not have the fix I am applying right now. The OO version is easier to use.

kpv 2016-11-02 This reminds me a bit of Hack-O-Matic. The original design wanted to draw 32k*8 boxes and was taking too long. I posted a redesign where I just drew as many boxes that filled the screen and had the contents change as you scroll.

bll 2016-11-2 Yes. Also Hugelist and Virtuallist. But I did not find those until long after I wrote this package.

 Change History

bll 2020-10-29 (2.12) Code cleanup.

bll 2020-9-28 (2.11) Improved mousewheel handling.

bll 2020-1-10 (2.10) clean up mousewheel binding; spinbox wheel handling.

bll 2018-3-5 (2.8) Bug: Clear the row height cache on a reconfigure.

bll 2017-12-13 Add a check for 'Text' class in the bound handlers.

bll 2017-9-27 Got rid of the popdownWindow call.

bll 2017-8-29 Cleaned up key binding handling.

Added two new methods:

setPageAdjust: pass this a negative number if you want the page up/down to display an overlap of the data.

setReserved: Specify how many lines are reserved at the top. These lines still need to be populated by your populate callback, but this method allows the scrolling routines to know about the reserved lines and adjust the scrolling behaviour appropriately.

bll 2017-6-30 Fixed to use <Configure> and <Visibility> events. Fixed to use bindtags so that user defined bindings will not be modified.

bll 2016-11-11 Fixed an issue for certain display situations.

bll 2016-11-2 More fixes for re-calculating the row heights for certain situations.

bll 2016-10-26 Fixed issues calculating the row height (frames in the row do not have their full height set until fully displayed).

bll 2016-10-24 Various bugfixes. Removed autoscroll. Rewrote to be an object. A little easier to use now, but the API can still use some work and cleanup.

bll 2016-5-1 Fix resize. Added autoscroll capability.

bll 2016-3-3 Generate Leave and Enter events for whatever widget is under the mouse pointer.

bll 2016-2-26 Use max of height,reqheight on resize.

bll 2016-2-21 Use reqheight on a resize.

bll 2016-2-16 Simplified and fixed chkScroll.

bll 2016-1-18 Fixed a problem with propagation being turned off too early.

bll 2015-10-19 Added a check for window existence.

bll 2015-10-13 Fixes for arrow key handling in conjunction with comboboxes.

bll 2015-08-19 Update with fixes for row multiplier and test script for row multiplier. Additional helper routines.

bll 2014-11-24 Fixes for strange windows behaviours and bug fixes in height/row comparisons.

bll 2014-11-21 Rewrite. It now works with windows that are already sized and as the original: do X number of rows for the initial display. Resizing works properly. It is more stable now, but a little bit slower.

bll 2014-11-20 Updated with changes that disallow the resize function until the first display has finished. Added mouse wheel binding routines. Adjusted test routines to make sure windows were children of the scrolling frame.

bll 2014-10-4 removed update, added mapped window checks, don't scroll if maximum is not set up yet.

See also: scrollbar and Scrolling widgets without a text or canvas wrapper.