Scrolling widgets without a text or canvas wrapper

Donal Fellows (originally in the Bag of Tk algorithms)

  # Make our contents and a list of frame names...
  for {set i 0} {$i<15} {incr i} {
      set w .lb$i
      frame $w
      listbox $w.lb -xscrollcommand "$w.x set" -yscrollcommand "$w.y set"
      scrollbar $w.x -command "$w.lb xview" -takefocus 0 -width 10 \
              -orient horizontal
      scrollbar $w.y -command "$w.lb yview" -takefocus 0 -width 10
      $w.lb insert end   "LISTBOX $i" "A: This is listbox $i" \
              "B: This is listbox $i" "C: This is listbox $i" \
              "D: This is listbox $i" "E: This is listbox $i"
      grid $w.lb $w.y -sticky nsew
      grid $w.x -sticky ew
      grid columnconfigure $w 0 -weight 1
      grid rowconfigure $w 0 -weight 1
      lappend lbs $w
  }

  # How many frames to show at once?
  set width 4

  # Our primary scrollbar
  scrollbar .sb -command "doScroll" -orient horizontal -width 20

  # And set up the static parts of the geometry manager
  grid .sb -row 1 -columnspan $width -sticky ew
  for {set i 0} {$i<$width} {incr i} {
      grid columnconfigure . $i -weight 1
      after idle grid columnconfigure . $i -minsize \[winfo reqwidth .lb0]
  }
  grid rowconfigure . 0 -weight 1
  after idle grid rowconfigure . 0 -minsize \[winfo reqheight .lb0]

  # We start at the left...
  set pos 0

  proc reconf {} {
      global pos lbs width
      .sb set [expr {double($pos)/([llength $lbs])}] \
              [expr {double($pos+$width)/([llength $lbs])}]
      eval grid forget $lbs
      eval grid [lrange $lbs $pos [expr {$pos+$width-1}]] -row 0
  }
  proc Xscroll {n units} {
      global pos width
      switch $units {
          units {incr pos $n}
          pages {incr pos [expr {$n*$width}]}
      }
  }
  proc Xmoveto {fraction} {
      global pos lbs
      set pos [expr {int([llength $lbs]*$fraction)}]
  }
  proc doScroll {args} {
      global pos lbs width
      set oldpos $pos
      set len [expr {[llength $lbs]-$width}]
      eval X$args
      if {$pos<0} {set pos 0} elseif {$pos>$len} {set pos $len}
      if {$pos != $oldpos} {reconf}
  }

  # Set up the scrollbar and frames...
  reconf

Also see Virtual Scrolling for another solution that does not use text or canvas.