How to get Scrollbar thumb position?

Leonardo 91 Reputation points
2022-04-16T19:56:11.247+00:00

I'm working on creating a 'dummy' ScrollBar to replace the original Listbox Scrollbar, then I can customize its shape/colors, etc, for this, I need the information about each item index relative to the thumb position.

I'm calling GetScrollbarInfo and SetScrollInfo in a loop that moves the thumb around and get its position:

   While (I <= M) {  
      NumPut(I, SI, 20, "Int") ; nPos  
      DllCall("SetScrollInfo", "Ptr", hWnd, "UInt", 1, "Ptr", &SI, "UInt", 0, "Int")  
      DllCall("GetScrollBarInfo", "Ptr", hWnd, "Int", 0xFFFFFFFB, "Ptr", &SBI)  
      VSB[I, "THT"] := NumGet(SBI, 24, "Int") ; thumb top  
      VSB[I, "THB"] := NumGet(SBI, 28, "Int") ; thumb bottom  
      I++  
   }  

However when there are many items in a Listbox, GetScrollBarInfo starts returning a different top position than where it currently is:

TopIndex 100 reports Thumb Top at 33, but its really at 29 as seen in the image below:

193615-test.png

Also deducting the bottom - top (41-33) we have 8, why 8 if the thumb height is 17?

Why does this happen? when the are just a few items it does report each position correctly.

OBS:
----
The language in the script below is AutoHotkey I tried to get some help in the AutoHotkey forum but its a very specific question related to WinApi.
I'm adding the tag c++ to see if I can target more people and get any kind of help, so answers in c++ are also welcome.

The relevant code is only inside of the function GetVScrollBarPositions and it's all commented.

Text := ""  
Gui, Color, 0  
Loop, 200  
   Text .= A_Index . "A|"  
Gui, +E0x02000000 +E0x00080000  
Gui, Add, Listbox, w100 r5 +VScroll hWndhWnd, %Text%  
GuiControlGet, P, Pos, %hWnd%  
Gui, Add, Text, xm, ListBox Height: %Ph%  
Gui, Show, w300 h356, Test  
Return  
  
GuiClose:  
ExitApp  
  
F2::  
S := A_TickCount  
VSB := GetVScrollBarPositions(hWnd)  
T := A_TickCount - S  
  
Gui, Result:New, +Owner1  
Gui, Add, Text, xm w250, Range Min:  
Gui, Add, Text, x+0 yp w100, % VSB.Min  
Gui, Add, Text, xm w250, Range Max:  
Gui, Add, Text, x+0 yp w100, % VSB.Max  
Gui, Add, Text, xm w250, Page Size:  
Gui, Add, Text, x+0 yp w100, % VSB.Page  
Gui, Add, Text, xm w250, SB Top:  
Gui, Add, Text, x+0 yp w100, % VSB.SBT  
Gui, Add, Text, xm w250, SB Bottom:  
Gui, Add, Text, x+0 yp w100, % VSB.SBB  
Gui, Add, Text, xm w250, Arrow Height:  
Gui, Add, Text, x+0 yp w100, % VSB.ABH  
Gui, Add, ListView, xm w350 r10, Item Index|Thumb Top|Thumb Bottom  
I := VSB.Min  
M := VSB.Max  
While (I <= M) {  
   LV_Add("", I, VSB[I, "THT"], VSB[I, "THB"])  
   I++  
}  
Gui, Show, , Vertical Scroll Bar Info (%T% ms)  
Return  
  
  
  
GetVScrollBarPositions(hWnd) {  
  
   VarSetCapacity(SI, 28, 0)   ; SCROLLINFO structure  
   NumPut(28, SI, 0, "UInt")   ; cbSize  
   NumPut(0x17, SI, 4, "UInt") ; fMask = SIF_ALL  
   If !DllCall("GetScrollInfo", "Ptr", hWnd, "Int", 1, "Ptr", &SI)  
      Return False  
  
   VSB := {}  
   VSB.Min := NumGet(SI, 8, "Int")   ; range min  
   VSB.Max := NumGet(SI, 12, "Int")  ; range max  
   VSB.Page := NumGet(SI, 16, "Int") ; page size  
   Pos := NumGet(SI, 20, "Int")      ; current pos  
  
   VarSetCapacity(SBI, 60, 0)  
   NumPut(60, SBI, 0, "Int")  
   DllCall("GetScrollBarInfo", "Ptr", hWnd, "Int", 0xFFFFFFFB, "Ptr", &SBI)  
   VSB.SBT := NumGet(SBI, 8, "Int")  ; scroll bar top  
   VSB.SBB := NumGet(SBI, 16, "Int") ; scroll bar bottom  
   VSB.ABH := NumGet(SBI, 20, "Int") ; arrow button height  
     
   NumPut(4, SI,  4, "Int") ; fMask = SIF_POS  
   I := VSB.Min  
   M := VSB.Max  
  
   While (I <= M) {  
      NumPut(I, SI, 20, "Int") ; nPos  
      DllCall("SetScrollInfo", "Ptr", hWnd, "UInt", 1, "Ptr", &SI, "UInt", 0, "Int")  
      DllCall("GetScrollBarInfo", "Ptr", hWnd, "Int", 0xFFFFFFFB, "Ptr", &SBI)  
      VSB[I, "THT"] := NumGet(SBI, 24, "Int") ; thumb top  
      VSB[I, "THB"] := NumGet(SBI, 28, "Int") ; thumb bottom  
      I++  
   }  
     
   NumPut(Pos, SI, 20, "Int") ; restore the original pos  
   DllCall("SetScrollInfo", "Ptr", hWnd, "UInt", 1, "Ptr", &SI, "UInt", 1, "Int")  
  
   Return VSB  
}  
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,427 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,540 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Junjie Zhu - MSFT 15,056 Reputation points Microsoft Vendor
    2022-05-17T09:30:00.39+00:00

    Hello,
    Welcome to Microsoft Q&A!

    For the convenience of viewing the pictures, I will first post my test process here.

    my calculation formula (floating point calculations):
    BTW, The number of rows in listbox starts from 0.

    ListRowPos = ( ThumbPos - ArrowHeight ) / ( ScrollArea ) * TotalRows;  
    

    **Test 1: **
    **A total of 800 pieces of data, set the maximum range of the scroll bar to 800 **
    Because the floating point number is rounded after calculation, there will be a deviation of one line.
    202703-gif-2022-5-17-17-17-53.gif

    **Test 2: **
    **A total of 800 pieces of data, set the maximum range of the scroll bar to 10 **
    The slider slides smoothly, but the list slides very stiffly.
    202731-gif-2022-5-17-17-25-39.gif

    I hope my formula is helpful to you. My project is just a simple beta version, and the constant variables in it are all directly assigned. If you need, I can share the code in the txt document.

    Thank you.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.