Jump to content

Scroll a listview item to the center of the window


Recommended Posts

_GUICtrlListView_EnsureVisible() ensures a selected item to be visible, but the item is usually at the bottom or top of the listview window. The following code places the selected item at the center of the window. One thing I'm not certain is the header height and line height. 26(2+24) and 19 pixels are based on Korean Windows 10. They may vary depending on OS, default language and font. I would expect to have comments on this from someone who knows.

#include <GUIConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>

Local $idListview, $idLabel, $idInput
Local $iViewHeight= 237, $iItemCount = 100, $iItemToSelect
Local Const $iHeaderHeight = 26, $iLineHeight = 19  ; based on Korean Win 10, may vary depending on OS, default language and font
GUICreate("ListView Scroll", 400, 300)
$idListview = GUICtrlCreateListView("", 2, 2, 394, $iViewHeight)
$idLabel = GUICtrlCreateLabel("Input a number between 1 and " & $iItemCount & " and press Enter:", 20, 260, 300, 20)
$idInput = GUICtrlCreateInput("", 325, 256, 30, 18, $ES_CENTER + $ES_NUMBER)
GUICtrlSetState($idInput, $GUI_FOCUS)
GUISetState(@SW_SHOW)

_GUICtrlListView_AddColumn($idListview, "Items", 100)

For $iI = 1 To 500
    _GUICtrlListView_AddItem($idListview, "Item " & $iI)
Next
$iItemCount = _GUICtrlListView_GetItemCount($idListview)
GUICtrlSetData($idLabel, "Input a number between 1 and " & $iItemCount & " and press Enter:")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $idInput
            $iItemToSelect = GUICtrlRead($idInput)
            If $iItemToSelect > 0 And $iItemToSelect <= $iItemCount Then
                _Scroll()
            Else
                MsgBox(0, "Listview Scroll", "The item number entered is invalid.")
            EndIf
            GUICtrlSetState($idListview, $GUI_FOCUS)
    EndSwitch
WEnd
GUIDelete()

Func _Scroll()
    ; Scroll $iItemToSelect to the center of listview window
    _GUICtrlListView_SetItemSelected($idListview, $iItemToSelect-1, True, True)
    _GUICtrlListView_Scroll($idListview, 0, $iItemCount*-$iLineHeight)
    _GUICtrlListView_Scroll($idListview, 0, ($iItemToSelect-($iViewHeight-$iHeaderHeight)/$iLineHeight/2)*$iLineHeight)
EndFunc

 

Edited by CYCho
Link to post
Share on other sites

Actually you could calculate the line width and the header height.  It would make your script independent of OS, styles, theme and Region.

#include <WinAPISysWin.au3>
#include <WinAPIConv.au3>

Example()

Func Example()
  Local $hWnd = GUICreate("test")
  Local $iHeight = _WinAPI_GetClientHeight($hWnd)
  Local $iWidth = _WinAPI_GetClientWidth($hWnd)
  Local $tRECT = _WinAPI_GetWindowRect($hWnd)
  Local $tPoint1 = DllStructCreate($tagPOINT)
  $tPoint1.X = 0
  $tPoint1.Y = 0
  _WinAPI_ClientToScreen($hWnd, $tPoint1)
  Local $tPoint2 = DllStructCreate($tagPOINT)
  $tPoint2.X = $iWidth
  $tPoint2.Y = $iHeight
  _WinAPI_ClientToScreen($hWnd, $tPoint2)
  ConsoleWrite("Actual " & $tRECT.left & "/" & $tRECT.top & "/" & $tRECT.right & "/" & $tRECT.bottom & @CRLF)
  ConsoleWrite("Client " & $tPoint1.X & "/" & $tPoint1.Y & "/" & $tPoint2.X & "/" & $tPoint2.Y & @CRLF)
EndFunc   ;==>Example

 

Edited by Nine
corrected a small bug
Link to post
Share on other sites

Oh, I thought you wanted to calculate window coordinates.  But I believe it doesn't matter.  You can use the exact same code with the handle of the list view (instead of the handle of the window) to get the same information. For individual item, you can use _GUICtrlListView_GetItemRect to get coordinates of that item.

Link to post
Share on other sites

Hi CYCho,
These 8 lines are directly extracted from the code of my CSV file editor, with variables renamed to match your script.

#include <GUIConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>

Local $idListview, $idLabel, $idInput
Local $iViewHeight= 237, $iItemCount = 100, $iItemToSelect
; Local Const $iHeaderHeight = 28, $iLineHeight = 19  ; based on Korean Win 10, may vary depending on OS, default language and font
GUICreate("ListView Scroll", 400, 300)
$idListview = GUICtrlCreateListView("", 2, 2, 394, $iViewHeight)
$idLabel = GUICtrlCreateLabel("Input a number between 1 and " & $iItemCount & " and press Enter:", 20, 260, 300, 20)
$idInput = GUICtrlCreateInput("", 325, 256, 30, 18, $ES_CENTER + $ES_NUMBER)
GUICtrlSetState($idInput, $GUI_FOCUS)

; =======================================================
; Calculate item height (item height depends on each computer, windows theme fonts, scaling...)
$hHeader = _GUICtrlListView_GetHeader($idListView)
Local $iHeaderHeight = _WinAPI_GetWindowHeight($hHeader)
Local $iNbItemsPerPage = _GUICtrlListView_GetCounterPage($idListView)
$iLineHeight = ($iViewHeight - $iHeaderHeight) / $iNbItemsPerPage
ConsoleWrite("$iHeaderHeight   = " & $iHeaderHeight & @crlf & _
             "$iNbItemsPerPage = " & $iNbItemsPerPage & @crlf & _
             "$iLineHeight     = " & $iLineHeight & @crlf)
; =======================================================

GUISetState(@SW_SHOW)

_GUICtrlListView_AddColumn($idListview, "Items", 100)

For $iI = 1 To 500
    _GUICtrlListView_AddItem($idListview, "Item " & $iI)
Next
$iItemCount = _GUICtrlListView_GetItemCount($idListview)
GUICtrlSetData($idLabel, "Input a number between 1 and " & $iItemCount & " and press Enter:")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $idInput
            $iItemToSelect = GUICtrlRead($idInput)
            If $iItemToSelect > 0 And $iItemToSelect <= $iItemCount Then
                _Scroll()
            Else
                MsgBox(0, "Listview Scroll", "The item number entered is invalid.")
            EndIf
            GUICtrlSetState($idListview, $GUI_FOCUS)
    EndSwitch
WEnd
GUIDelete()

Func _Scroll()
    ; Scroll $iItemToSelect to the center of listview window
    _GUICtrlListView_SetItemSelected($idListview, $iItemToSelect-1, True, True)
    _GUICtrlListView_Scroll($idListview, 0, $iItemCount*-$iLineHeight)
    _GUICtrlListView_Scroll($idListview, 0, ($iItemToSelect-($iViewHeight-$iHeaderHeight)/$iLineHeight/2)*$iLineHeight)
EndFunc

This is what the console shows and a pic of your LV on my computer (real size) :

$iHeaderHeight   = 20
$iNbItemsPerPage = 14
$iLineHeight     = 15.5

LV.png.6fdab5f345f412573de327c8d9f6a551.png

But then when I input a number, your scroll function doesn't place the desired line exactly in the middle of the LV, so there's something to be a bit reworked :
* or in my code
* or in your scroll code

Let's wait & see :)

 

Edited by pixelsearch
Just re-checked this: (14 rows x 15.5 pixels height) + header 20 pixels = 237 pixels (your LV height)
Link to post
Share on other sites

Same results (almost) :

#include <WinAPISysWin.au3>
#include <WinAPIConv.au3>
#include <GUIConstants.au3>
#include <GuiListView.au3>

Example2()

Func Example1($hWnd)
  Local $iWidth = _WinAPI_GetClientWidth($hWnd)
  Local $iHeight = _WinAPI_GetClientHeight($hWnd)
  Local $tRECT = _WinAPI_GetWindowRect($hWnd)
  Local $tPoint1 = DllStructCreate($tagPOINT)
  $tPoint1.X = 0
  $tPoint1.Y = 0
  _WinAPI_ClientToScreen($hWnd, $tPoint1)
  Local $tPoint2 = DllStructCreate($tagPOINT)
  $tPoint2.X = $iWidth
  $tPoint2.Y = $iHeight
  _WinAPI_ClientToScreen($hWnd, $tPoint2)
  ConsoleWrite("Actual " & $tRECT.left & "/" & $tRECT.top & "/" & $tRECT.right & "/" & $tRECT.bottom & @CRLF)
  ConsoleWrite("Client " & $tPoint1.X & "/" & $tPoint1.Y & "/" & $tPoint2.X & "/" & $tPoint2.Y & @CRLF)
  $tRECT = _GUICtrlListView_GetItemRectEx($hWnd, 0, 0)
  Local $tPoint1i = DllStructCreate($tagPOINT)
  $tPoint1i.X = $tRECT.left
  $tPoint1i.Y = $tRECT.top
  _WinAPI_ClientToScreen($hWnd, $tPoint1i)
  Local $tPoint2i = DllStructCreate($tagPOINT)
  $tPoint2i.X = $tRECT.right
  $tPoint2i.Y = $tRECT.bottom
  _WinAPI_ClientToScreen($hWnd, $tPoint2i)
  ConsoleWrite("Item0 " & $tPoint1i.X & "/" & $tPoint1i.Y & "/" & $tPoint2i.X & "/" & $tPoint2i.Y & @CRLF)
  ConsoleWrite ("=============== Nine ================" & @CRLF)
  ConsoleWrite ("Header height = " & $tPoint1i.Y - $tPoint1.Y & @CRLF)
  ConsoleWrite ("Item height = " & $tPoint2i.Y - $tPoint1i.Y & @CRLF)
EndFunc   ;==>Example1

Func Example2()
  $hGUI = GUICreate("ListView Scroll", 400, 300)
  $idListview = GUICtrlCreateListView("", 2, 2, 394, 237)
  $hList = GUICtrlGetHandle($idListview)
  GUISetState(@SW_SHOW)

  _GUICtrlListView_AddColumn($idListview, "Items", 100)

  For $iI = 1 To 500
    _GUICtrlListView_AddItem($idListview, "Item " & $iI)
  Next
  $iItemCount = _GUICtrlListView_GetItemCount($idListview)

  Example1($hList)

  ; =======================================================
  ; Calculate item height (item height depends on each computer, windows theme fonts, scaling...)
  $hHeader = _GUICtrlListView_GetHeader($idListview)
  Local $iHeaderHeight = _WinAPI_GetWindowHeight($hHeader)
  Local $iNbItemsPerPage = _GUICtrlListView_GetCounterPage($idListview)
  $iLineHeight = (237 - $iHeaderHeight) / $iNbItemsPerPage
  ConsoleWrite ("============ PixelSearch ============" & @CRLF)
  ConsoleWrite("$iHeaderHeight   = " & $iHeaderHeight & @CRLF & _
      "$iNbItemsPerPage = " & $iNbItemsPerPage & @CRLF & _
      "$iLineHeight     = " & $iLineHeight & @CRLF)
  ; =======================================================

  While 1
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
    EndSwitch
  WEnd
EndFunc   ;==>Example2

Fraction of a pixel is not really possible ;)

Edited by Nine
Link to post
Share on other sites
3 hours ago, Nine said:

Fraction of a pixel is not really possible ;)

Not yet... though after viewing (again) "Interstellar" on TV a few days ago, I'm not sure of anything :)

Gladly in my CSV script, the decimal part didn't do any harm because the concerned variable (named $iLineHeight above) was used "as-is" (no multiplication or division) in 2 parts of the script, to indicate the height of the "en place" edit box as seen in this pic, or the height of the red wanderer "marker for deletion" label in that pic.

I guess AutoIt always rounds it to Integer. Sometimes I comment it in the code (after having tested the result), for example :

WinMove($hGUI2, "", $iParentNewCliW * $nRatioX, $iParentNewCliH * $nRatioY, _
    $iParentNewCliW * $nRatioW, $iParentNewCliH * $nRatioH) ; WinMove will use Int coords

Some other time a serious error may appear (error 10, extended 3) when you got decimals in this kind of line :

$hCloneArea = _GDIPlus_BitmapCloneArea($hImage_Resized, $iCrop_Left2, $iCrop_Top2, $iX_resized, $iY_resized)

The error being fixed with Int()  because both variables had decimals :

$hCloneArea = _GDIPlus_BitmapCloneArea($hImage_Resized, $iCrop_Left2, $iCrop_Top2, Int($iX_resized), Int($iY_resized))

Strange that I like decimals so much :D

Edited by pixelsearch
Link to post
Share on other sites

I did use the EX version.  Well to be honest, I am glad that my EX is very far away :lol:

I agree with you the getHeader is the best way.  But to know all the infos from all angles, I was trying to show how to do it.

Link to post
Share on other sites
Posted (edited)

@Nine and @pixelsearch, You really came up with perfect solution while I as asleep. Once again you made me realize this world is worth living. Thank you. Below is my revised code. In fact I used this logic in my zPlayer and I'll have to reflect your solution in my next update.
 

#include <GUIConstants.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>

Local $idListview, $idLabel, $idInput
Local $iViewHeight= 237, $iItemCount = 100, $iItemToSelect
Local $mainGUI = GUICreate("ListView Scroll", 400, 300)
$idListview = GUICtrlCreateListView("", 2, 2, 394, $iViewHeight)
$idLabel = GUICtrlCreateLabel("Input a number between 1 and " & $iItemCount & " and press Enter:", 20, 260, 300, 20)
$idInput = GUICtrlCreateInput("", 325, 256, 30, 18, $ES_CENTER + $ES_NUMBER)
GUICtrlSetState($idInput, $GUI_FOCUS)
GUISetState(@SW_SHOW)

_GUICtrlListView_AddColumn($idListview, "Items", 100)

For $iI = 1 To 5000
    _GUICtrlListView_AddItem($idListview, "Item " & $iI)
Next
$iItemCount = _GUICtrlListView_GetItemCount($idListview)
GUICtrlSetData($idLabel, "Input a number between 1 and " & $iItemCount & " and press Enter:")
Local $aItemRect = _GUICtrlListView_GetItemRect($idListview, 0, 0)
Local $iItemHeight = $aItemRect[3] - $aItemRect[1]
Local $hHeader = _GUICtrlListView_GetHeader($idListView)
Local $iHeaderHeight = _WinAPI_GetWindowHeight($hHeader)

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $idInput
            $iItemToSelect = GUICtrlRead($idInput)
            If $iItemToSelect > 0 And $iItemToSelect <= $iItemCount Then
                _Scroll()
                GUICtrlSetState($idListview, $GUI_FOCUS)
            Else
                MsgBox(0, "Listview Scroll", "The item number entered is invalid.")
                GUICtrlSetState($idInput, $GUI_FOCUS)
            EndIf
    EndSwitch
WEnd
GUIDelete()

Func _Scroll()
    ; Scroll $iItemToSelect to the center of listview window
    _GUICtrlListView_SetItemSelected($idListview, $iItemToSelect-1, True, True)
    _GUICtrlListView_Scroll($idListview, 0, $iItemCount*-$iItemHeight)
    _GUICtrlListView_Scroll($idListview, 0, ($iItemToSelect-($iViewHeight-$iHeaderHeight)/$iItemHeight/2)*$iItemHeight)
EndFunc

 

ListViewScroll.png

Edited by CYCho
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...