Scroll Lock For Two Listboxes

Hi Everyone,

I have two listboxes side by side.

I would like to keep them locked together. If I scroll to row 35 in one, the other should scroll with it, so that both display the same row. The listboxes always have the same number of items.

I know I can use

MyListBox.ScrollPosition = RowToPlaceAtTop

To scroll either listbox once the position of the other changes, but I’m not sure which events to use to detect movement and get the current top row of the listbox being manipulated.

Any ideas?

Thanks!

-Mike

I’d look at least at keydown, mouse down and mouse wheel

one list simply sets its scroll position to the scroll position of the other

Catch it in CellBackgroundPaint.

if row = me.ScrollPosition then
   if column = 0 then
       // only do this for the top/left displayed cell
       otherListBox.ScrollPosition = me.ScrollPosition
   end
end

You’ll also want to set up a short period timer for some of the situations. I did this before and got a pretty good result by doing the following:

  1. Making a subclass of listbox for both of the boxes that will be linked
  2. Making a separate controller class that will be notified whenever either listbox is potentially scrolled (via mouseWheel, mouseDown, keyDown) through a method that passes the changed listbox as a parameter and updates the other listbox. It has the properties of MyLeftBox and MyRightBox that can be setup for each instance.

The code for the controller class I used was:

[code]Sub updateScrolling(boxfrom as PlannerBox, timerEnsure as Boolean=true)
dim boxtoupdate as PlannerBox

if boxfrom=nil then
Return
end if

if boxfrom=MyLeftBox then
boxtoupdate=MyRightBox
elseif boxfrom=MyRightBox then
boxtoupdate=MyLeftBox
end if

if boxtoupdate<>nil then
boxtoupdate.UpdatingFromController=true
boxtoupdate.ScrollPosition=boxfrom.ScrollPosition
boxtoupdate.UpdatingFromController=false
end if

if timerEnsure then
MyUpdateTimer= new TimerScrollUpdater
MyUpdateTimer.MyController=me
MyUpdateTimer.myBox=boxfrom

MyUpdateTimer.Period=5
MyUpdateTimer.Mode=1
MyUpdateTimer.Enabled=true

end if
End Sub
[/code]

This makes it easier by having all of the scrolling go through one method for either box, and it will also create the timer to ensure that the scrolling is updated in situations when the scrollbar is dragged. The timer will simply call the scroll position function again and prevent the timerEnsure:

Sub Action() if MyController<>nil and myBox<>nil then MyController.updateScrolling(myBox,false) end if End Sub

Also as a note there were several other functions for syncing between the two, including keeping the listindex the same between them whenever that was changed. So once you set up the controller class you can do a lot more with it than just the scroll position if you need to.

Edit: nevermind, my suggestion still doesn’t catch live scrolling with the mouse.

I needed to do several other things with it so I just decided to code whole shebang and include the scroll locking within the controller, but it looks like that way is a lot easier and performs better for the scrolling. Thanks!

It does in Cocoa when I tested it just a few minutes ago. Maybe that’s why I didn’t implement something like that before, as I haven’t visited this code since pre-Cocoa RB, and I’m guessing there might be issues on Windows with it as well.

MouseWheel does
Between 2 lists I think I used 6 lines of code total

On Windows, it doesn’t catch the scroll reliably when you grab the thumb with your mouse and scroll. I think I had to resort to a timer way back when I solved this problem. My memory is a little foggy though.

[quote=60561:@Norman Palardy]MouseWheel does
Between 2 lists I think I used 6 lines of code total[/quote]

Right that works fine for mousewheel, keydown, and when clicking around on the scrollbar, but if the user drags the scrollbar they could get out of sync.

Doesn’t

if Listbox1.ScrollPosition<>me.ScrollPosition then Listbox1.ScrollPosition=me.ScrollPosition
in the cellBackgroundPaint of each ListBox work? (“Listbox1” being replaced with the other control)
Similar to Tim’s code, but it checks on every paint and only sets when needed…
I can’t seem to find a miss under Cocoa or Win32… I must be missing something…

1 Like

[quote=60568:@jim mckay]Doesn’t

if Listbox1.ScrollPosition<>me.ScrollPosition then Listbox1.ScrollPosition=me.ScrollPosition
in the cellBackgroundPaint of each ListBox work? (“Listbox1” being replaced with the other control)
Similar to Tim’s code, but it checks on every paint and only sets when needed…
I can’t seem to find a miss under Cocoa or Win32… I must be missing something…[/quote]
Of course !
This is even simpler :slight_smile:
Works perfectly here as well
Good call

Brilliantly simple solution! Thank you all for the replies!

If have several in-house apps where I have done this sort of thing…

The reason to link vertical scroll position (at least for me) , has been to simulate being able to freeze the leftmost columns of a list while horizontal scrolling… The scroll locking part was easy (though the CellBackgroundPaint workaround did not work in Carbon last time I tried it and I had to use a different approach)

The hard part is to get row highlighting synchronized in all situations without a significant visual lag. The change event fires too late and there are so may ways to highlight rows (and it has to work from either listbox), that it’s hard to write code that does it synchronously.

Anybody solve that?

I know this is a old thread but do y’all have any insight on how you could accomplish this with the WebListbox without the user making a listindex change?