Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

How to properly add a horizontal scrollbar to a listbox


  • Please log in to reply
3 replies to this topic
TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
While I was working on a project (which I'll hopefully also post here when I'm done), I found myself needing an horizontal scrollbar for my listboxes, because of very long items. No problem I thought. I'll simply use the GuiControl +HScroll command. The only problem is that the only way to use this command is to specify how many pixels you want the scrollbar to scroll. That's not practical because the longest item in the listbox might be less or more than the specified scrolling limit.

So then I made some research, and found this, straight from Microsoft. It tells you in details how to add a horizontal scrollbar to the listbox sensitive to items' width. All I did was adapt the steps from the article to AutoHotkey with the help of this topic and that one (thanks to Sean, majkinetor and dncarac).

And this is what you get: a function that adds an horizontal scrollbar to your listbox, which will scroll only up to what you need, that is, up to the longest item (and also taking into consideration the font used in the listbox).

How to use/add to your code
All you need from the code below is the ListBoxAdjustHSB function and the GetListBoxItem function. I only added a GUI as an example. Everytime an item is added/removed from the listbox, call the ListBoxAdjustHSB function to adjust the scrollbar extent (or remove it if not needed). Make sure you have the +HScroll option when adding your listbox, otherwise it won't work.

Gui, Add, ListBox, h100 hwndhLB +HScroll, This is a short list box item.|This is a very very very very very very very very very very very very very very long list box item.
Gui, Show

;Add Horizontal Bar
ListBoxAdjustHSB(hLB)

Return

GuiClose:
ExitApp
Return

ListBoxAdjustHSB(hLB) {
	
	;Declare variables (for clarity's sake)
	dwExtent := 0
	dwMaxExtent := 0
	hDCListBox := 0
	hFontOld := 0
	hFontNew := 0
	VarSetCapacity(lptm, 53)
	
	;Use GetDC to retrieve handle to the display context for the list box and store it in hDCListBox
	hDCListBox := DllCall("GetDC", "Uint", hLB)

	;Send the list box a WM_GETFONT message to retrieve the handle to the font that the list box is using, and store this handle in hFontNew
	SendMessage 49, 0, 0,, ahk_id %hLB%
	hFontNew := ErrorLevel

	;Use SelectObject to select the font into the display context. Retain the return value from the SelectObject call in hFontOld
	hFontOld := DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontNew)

	;Call GetTextMetrics to get additional information about the font being used (eg. to get tmAveCharWidth's value)
	DllCall("GetTextMetrics", "Uint", hDCListBox, "Uint", &lptm)
	tmAveCharWidth := NumGet(lptm, 20)

	;Get item count using LB_GETCOUNT
	SendMessage 395, 0, 0,, ahk_id %hLB%

	;Loop through the items
	Loop %ErrorLevel% {

		;Get list box item text
		s := GetListBoxItem(hLB, A_Index - 1)

		;For each string, the value of the extent to be used is calculated as follows:
		DllCall("GetTextExtentPoint32", "Uint", hDCListBox, "str", s, "int", StrLen(s), "int64P", nSize)
		dwExtent := (nSize & 0xFFFFFFFF) + tmAveCharWidth

		;Keep if it's the highest to date
		If (dwExtent > dwMaxExtent)
			dwMaxExtent := dwExtent
		
	}
	
	;After all the extents have been calculated, select the old font back into hDCListBox and then release it:
	DllCall("SelectObject", "Uint", hDCListBox, "Uint", hFontOld)
	DllCall("ReleaseDC", "Uint", hLB, "Uint", hDCListBox)
	
	;Adjust the horizontal bar using LB_SETHORIZONTALEXTENT
	SendMessage 404, dwMaxExtent, 0,, ahk_id %hLB%

}

GetListBoxItem(hLB, i) {
		
	;Get length of item. 394 = LB_GETTEXTLEN
	SendMessage 394, %i%, 0,, ahk_id %hLB%
	
	;Check for error
	If (ErrorLevel = 0xFFFFFFFF)
		Return ""
	
	;Prepare variable
	VarSetCapacity(sText, ErrorLevel, 0)
	
	;Retrieve item. 393 = LB_GETTEXT
	SendMessage 393, %i%, &sText,, ahk_id %hLB%
	
	;Check for error
	If (ErrorLevel = 0xFFFFFFFF)
		Return ""
	
	;Done
	Return sText

}


Let me know if you find bugs or see places where I could improve.
Enjoy!

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006
You've done an incredibly clear job of explaining & documenting your function. FANTASTIC!
Joyce Jamce

jballi
  • Members
  • 1029 posts
  • Last active:
  • Joined: 01 Oct 2005
This looks useful. I just need to remember that it's available the next time I create a ListBox that needs a horizontal scroll bar. Thanks for sharing. :)

TheGood
  • Members
  • 589 posts
  • Last active: Mar 22 2014 03:22 PM
  • Joined: 30 Jul 2007
I'm glad you guys like it :D
I'll see if I can put it in Jon's stickied wiki. lol I wonder if "stickied" is even a word.