Listview without horizontal scroll bar

Share your advanced PureBasic knowledge/code with the community.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Listview without horizontal scroll bar

Post by Justin »

I needed a listview without a horizontal scroll bar for one of my projects, it took me a while to figure it out but it finally works. It uses flat scroll bars, you can also customize them a little with the flat scroll bar api but i haven't tried it.
It's pretty easy to use, just 4 functions to create, resize and add/remove items.

Code: Select all

;Listview without horizontal scrollbar.
;Justin 11/10

enableexplicit

define.i event, margins, winwidth, winheight
define.i lv, i, lvWidth, hwHdr, hdrHeight

structure HDLAYOUT
	*prc.RECT
	*pwpos.WINDOWPOS
endstructure 

procedure lvCreate(id.i, x.i, y.i, width.i, height.i, title.s, titlewidth.i, flags.i=0)
;Creates a listview with the appropiate styles.

	define.i hwLv, r

	r = ListIconGadget(id, x, y, width, height, title, titlewidth, flags|#LVS_NOSCROLL)
	if id=#pb_any
		hwLv = GadgetID(r)
	else
		hwLv = GadgetID(id)
	endif 

 	InitializeFlatSB_(hwLv)
	SendMessage_(hwLv, #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_FLATSB, #LVS_EX_FLATSB)

	procedurereturn r
endprocedure 

procedure lvUpdateVScroll(lv.i)
;Updates the vertical scrollbar.
;Needs to be called when an item is inserted or removed or the listview size changes.

	define.SCROLLINFO si
	define.i hwLv

	hwLv = GadgetID(lv)
	si\cbSize = sizeof(SCROLLINFO)
	si\fMask = #SIF_RANGE | #SIF_PAGE 
 	si\nMin = 1
 	si\nMax = SendMessage_(hwLv, #LVM_GETITEMCOUNT, 0, 0)
 	si\nPage = SendMessage_(hwLv, #LVM_GETCOUNTPERPAGE, 0, 0)

	FlatSB_SetScrollInfo_(hwLv, #SB_VERT, @si, #true)
endprocedure 

procedure lvResizeHeader(lv.i)
;Resizes the header to fit the listview.
;Needs to be called when the listview size changes.

	define.i hwHdr, hwLv
	define.RECT rcLv
	define.HDLAYOUT hdl
	define.WINDOWPOS wpos

	hwLv = GadgetID(lv)
	hwHdr = SendMessage_(hwLv, #LVM_GETHEADER, 0, 0)
	if hwHdr
		GetClientRect_(hwLv, @rcLv)
		hdl\prc = @rcLv
		hdl\pwpos = @wpos
		SendMessage_(hwHdr, #HDM_LAYOUT, 0, @hdl)
		SetWindowPos_(hwHdr, hdl\pwpos\hwndInsertAfter, hdl\pwpos\x, hdl\pwpos\y, hdl\pwpos\cx, hdl\pwpos\cy, hdl\pwpos\flags | #SWP_SHOWWINDOW)
	endif 
endprocedure 

procedure lvResize(lv.i, x.i, y.i, width.i, height.i)
;Resizes the listview and updates the header and vertical scrollbar.

	ResizeGadget(lv, x, y, width, height)
	lvUpdateVScroll(lv)
	lvResizeHeader(lv)
endprocedure 

procedure lvAddItem(lv.i, pos.i, text.s, image.i=0)
	AddGadgetItem(lv, pos, text, image)
	lvUpdateVScroll(lv)
endprocedure 

procedure lvRemoveItem(lv.i, pos.i, text.s, image.i=0)
	RemoveGadgetItem(lv, pos)
	lvUpdateVScroll(lv)
endprocedure 

;- TEST
margins = 5
winwidth = 300
winheight = 200
lvWidth = winwidth - margins*2
If OpenWindow(0, 100, 100, winwidth, winheight, "Listview Flat Scrollbar", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
	lv = lvCreate(#pb_any, margins, margins, lvWidth, winheight - margins*2, "Column 1", lvWidth/2, #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
	AddGadgetColumn(lv, 1, "Column 2", lvWidth/2)
	for i=1 to 20
		lvAddItem(lv, -1, "Item " + str(i) + Chr(10) + "Subitem " + str(i))
	next 

	Repeat
		event = WaitWindowEvent()
		select event
			case #PB_Event_SizeWindow
				lvResize(lv, #pb_ignore, #pb_ignore, WindowWidth(0)- margins*2, WindowHeight(0)- margins*2)
		endselect
	Until event = #PB_Event_CloseWindow
EndIf

Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Listview without horizontal scroll bar

Post by Mistrel »

What are you doing to do if someone drags a column off to the side and it becomes inaccessible? :|
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Re: Listview without horizontal scroll bar

Post by Justin »

I will be handling the header notifications to prevent that, there will be a minimun column width.
But just noticed that the scroll thumb does not have the correct size when resizing the listview, i'm doing something wrong setting the range and max pos. Maybe someone can fix it. Srod?
It happens when you first scroll to the bottom and then resize the window.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Listview without horizontal scroll bar

Post by srod »

Yes you need to scroll the listview yourself to ensure that the top-most visible item keeps pace with the vertical resizing. However, because of your use of #LVS_NOSCROLL, the #LVM_ENSUREVISIBLE message does not appear to work. The alternative is to try #LVM_SCROLL but that will be fiddly and is not guaranteed to work because of the #LVS_NOSCROLL.
I may look like a mule, but I'm not a complete ass.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Re: Listview without horizontal scroll bar

Post by Justin »

I think it works well now, basically scrolls one line up if the page size increases, storing the last page size in gadget data and using #WM_VSCROLL instead of #LVM_SCROLL.
Later i'll try the column margins.

Code: Select all

;Listview without horizontal scrollbar.
;Justin 11/10

enableexplicit

define.i event, margins, winwidth, winheight
define.i lv, i, lvWidth, hwLv

structure HDLAYOUT
	*prc.RECT
	*pwpos.WINDOWPOS
endstructure 

procedure lvCreate(id.i, x.i, y.i, width.i, height.i, title.s, titlewidth.i, flags.i=0)
;Creates a listview with the appropiate styles.

	define.i hwLv, r

	r = ListIconGadget(id, x, y, width, height, title, titlewidth, flags|#LVS_NOSCROLL)
	if id=#pb_any
		hwLv = GadgetID(r)
	else
		hwLv = GadgetID(id)
	endif 

  InitializeFlatSB_(hwLv)
 	SendMessage_(hwLv, #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_FLATSB, #LVS_EX_FLATSB)

	procedurereturn r
endprocedure 

procedure lvUpdateVScroll(lv.i)
;Updates the vertical scrollbar.
;Needs to be called when an item is inserted or removed or the listview size changes.

	define.SCROLLINFO si
	define.i hwLv, items, page

	hwLv = GadgetID(lv)

	si\cbSize = sizeof(SCROLLINFO)
	si\fMask = #SIF_RANGE | #SIF_PAGE 
 	si\nMin = 1
 	si\nMax = SendMessage_(hwLv, #LVM_GETITEMCOUNT, 0, 0) 
 	si\nPage = SendMessage_(hwLv, #LVM_GETCOUNTPERPAGE, 0, 0)
	SetGadgetData(lv, si\nPage)

	FlatSB_SetScrollInfo_(hwLv, #SB_VERT, @si, #true)
endprocedure 

procedure lvResizeHeader(lv.i)
;Resizes the header to fit the listview.
;Needs to be called when the listview size changes.

	define.i hwHdr, hwLv
	define.RECT rcLv
	define.HDLAYOUT hdl
	define.WINDOWPOS wpos

	hwLv = GadgetID(lv)
	hwHdr = SendMessage_(hwLv, #LVM_GETHEADER, 0, 0)
	if hwHdr
		GetClientRect_(hwLv, @rcLv)
		hdl\prc = @rcLv
		hdl\pwpos = @wpos
		SendMessage_(hwHdr, #HDM_LAYOUT, 0, @hdl)
		SetWindowPos_(hwHdr, hdl\pwpos\hwndInsertAfter, hdl\pwpos\x, hdl\pwpos\y, hdl\pwpos\cx, hdl\pwpos\cy, hdl\pwpos\flags | #SWP_SHOWWINDOW)
	endif 
endprocedure 

procedure lvResize(lv.i, x.i, y.i, width.i, height.i)
;Resizes the listview and updates the header and vertical scrollbar.

	define.i hwLv

	hwLv = GadgetID(lv)

	ResizeGadget(lv, x, y, width, height)
	;Scroll up if page size increases
	if SendMessage_(hwLv, #LVM_GETCOUNTPERPAGE, 0, 0)>GetGadgetData(lv) and SendMessage_(hwLv, #LVM_GETTOPINDEX, 0, 0)<>0
		SendMessage_(hwLv, #WM_VSCROLL, #SB_LINEUP, #null)
	endif 

	lvUpdateVScroll(lv)
	lvResizeHeader(lv)
endprocedure 

procedure lvAddItem(lv.i, pos.i, text.s, image.i=0)
	AddGadgetItem(lv, pos, text, image)
	lvUpdateVScroll(lv)
endprocedure 

procedure lvRemoveItem(lv.i, pos.i, text.s, image.i=0)
	RemoveGadgetItem(lv, pos)
	lvUpdateVScroll(lv)
endprocedure 

;- TEST
margins = 5
winwidth = 300
winheight = 200
lvWidth = winwidth - margins*2
If OpenWindow(0, 100, 100, winwidth, winheight, "Listview Flat Scrollbar", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
	lv = lvCreate(#pb_any, margins, margins, lvWidth, winheight - margins*2, "Column 1", lvWidth/2, #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
	hwLv = GadgetID(lv)
	AddGadgetColumn(lv, 1, "Column 2", lvWidth/2)
	for i=1 to 20
		lvAddItem(lv, -1, "Item " + str(i) + Chr(10) + "Subitem " + str(i))
	next 

	Repeat
		event = WaitWindowEvent()
		select event
			case #PB_Event_SizeWindow
				lvResize(lv, #pb_ignore, #pb_ignore, WindowWidth(0)- margins*2, WindowHeight(0)- margins*2)
		endselect
	Until event = #PB_Event_CloseWindow
EndIf

Post Reply