Editor's Note A listview is an advanced listbox. The listview control is typically used to display members of a data collection in table format. Each column has its own column header. The listview is far more complex than the ordinary listbox. While the listbox is easily populated from its associated array, the listview requires each item to be individually assigned. This assignment is accomplished with a rather complicated series of structs and API calls.
A listview demo is included in Alyce Watson's (- Alyce) Liberty 4 Companion ebook. Alyce's demo shows how to construct a listview, add and delete items to the listview, and make a listview selection by highlighting an item and then clicking the select button. Eldron Gill takes Alyce's demo one step further by allowing selection from a double mouse click on the listview item itself.
Capturing mouse clicks in a listview requires a callback function, not easily handled even with Liberty BASIC. Fortunately, Dennis McKinney (- DennisMcK) offers the free msghook.dll designed specifically for this purpose. Here is Eldron's modification of Alyce's listview demo to allow selection by a double left mouse click. As stated in the code, msghook.dll must be accessible within the program folder.
It should be mentioned that Eldron sought and received permission from both Alyce and Dennis before submitting this demo. Our thanks to all three for this collaboration and sharing of resources and knowledge.
'This demo requires msghook.dll by Dennis McKinney
'Get it here: http://www.syberden.net/libertybelle/dlls.htm
'Alyce Watson provided the listview code.
'http://www.alycesrestaurant.com
'listview control with double click 10/09/2006 Eldron Gill
nomainwin
dim info$(0,0)
If fileExists(DefaultDir$, "msghook.dll") < 1 then
notice ""+chr$(13)+"This demo requires msghook.dll by Dennis McKinney"+chr$(13)+_
"Download the dll from here : http://www.syberden.net/libertybelle/dlls.htm"+chr$(13)+_
"Place it in the folder with this code."EndEndIf'constants
LVS.NOSORTHEADER = 32768
LVS.REPORT = 1
LVS.SINGLESEL = 4
LVS.SHOWSELALWAYS = 8
LVS.SORTASCENDING = 16
LVS.SORTDESCENDING = 32
LVS.NOLABELWRAP = 128
LVS.AUTOARRANGE = 256
LVS.NOSCROLL = 8192
LVS.ALIGNTOP = 0
LVS.ALIGNLEFT = 2048
LVS.NOCOLUMNHEADER = 16384
LVIF.TEXT = 1
LVIF.STATE = 8
LVIS.UNSELECTED = 0
LVIS.FOCUSED = 1
LVIS.SELECTED = 2
LVM.FIRST = 4096
LVM.SETITEM = 4102
LVM.INSERTITEM = 4103
LVM.INSERTCOLUMN = 4123
LVM.GETITEMCOUNT = 4100
LVM.GETITEMA = 4101
LVM.GETITEMTEXTA = 4141
LVM.GETITEMSTATE = 4138
LVM.SETITEMSTATE = 4139
LVM.DELETEITEM = 4104
LVM.DELETEALLITEMS = 4105
LVCF.WIDTH = 2
LVCF.TEXT = 4
'create structs
Struct LVCOLUMN, _
mask As ulong, _
fmt Aslong, _
cx Aslong, _
pszText$ As ptr, _
cchTextMax Aslong, _
iSubItem Aslong, _
iImage Aslong, _
iOrder Aslong
Struct LVITEM, _
mask As ulong, _
iItem Aslong, _
iSubItem Aslong, _
state As ulong, _
stateMask As ulong, _
pszText$ As ptr, _
cchTextMax Aslong, _
iImage Aslong, _
lParam Aslong, _
iIndent Aslong
struct msg,_
hndl as ulong,_
message aslong,_
wParam aslong,_
lParam aslong,_
LOWORDwparam as word,_
HIWORDwparam as word,_
LOWORDlparam as word,_
HIWORDlparam as word
'initialize common controls:
calldll #comctl32, "InitCommonControls",re as void
' Open a window
WindowWidth = 240: WindowHeight = 200
UpperLeftX = 10: UpperLeftY = 10
button #1.b, "Add",[add],UL,10,10,90,24
button #1.d, "Delete",[delete],UL,120,10,90,24
button #1.GetMsgHookCallback, "", [choice], ul, 0, 0, 0, 0
statictext #1.s, "List count: 2",10,140,100,30
open"Listview Example"for dialog as #1
print #1, "trapclose [quit]"
hwndParent = hwnd(#1)
' Get window instance handle
CallDLL #user32, "GetWindowLongA",_
hwndParent As ulong,_ 'parent window handle
_GWL_HINSTANCE Aslong,_'flag to retrieve instance handle
hInstance As ulong 'instance handle
' Create control
style = _WS_CHILD OR _WS_VISIBLE OR LVS.NOSORTHEADER _
OR LVS.REPORT OR LVS.SINGLESEL OR LVS.SHOWSELALWAYS
calldll #user32, "CreateWindowExA",_
_WS_EX_CLIENTEDGE Aslong,_ ' extended style
"SysListView32"as ptr,_ ' class name
""as ptr,_
style aslong,_ ' style
10 aslong,_ ' left x
50 aslong,_ ' top y
200 aslong,_ ' width
80 aslong,_ ' height
hwndParent as ulong,_ ' parent hWnd
0 aslong,_
hInstance as ulong,_ ' hInstance
""as ptr,_
hwndLV aslong' listview handle
'insert first column:
LVCOLUMN.mask.struct = LVCF.WIDTH OR LVCF.TEXT
LVCOLUMN.cx.struct = 90
LVCOLUMN.pszText$.struct = "Name"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.INSERTCOLUMN Aslong, _
0 Aslong, _ '0 = first column
LVCOLUMN As struct, _
r Aslong'insert second column:
LVCOLUMN.cx.struct = 65
LVCOLUMN.pszText$.struct = "Rank"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.INSERTCOLUMN Aslong, _
1 Aslong, _ '1 = second column
LVCOLUMN As struct, _
r Aslong'insert text for first row, first column
'requires message to insert item
LVITEM.mask.struct = LVIF.TEXT
LVITEM.iItem.struct = 0 'first row
LVITEM.iSubItem.struct = 0 'first column
LVITEM.pszText$.struct = "Carl Gundel"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.INSERTITEM Aslong, _
0 Aslong, _
LVITEM As struct, _
r Aslong'insert text for second column, first row
LVITEM.iItem.struct = 0 'first row
LVITEM.iSubItem.struct = 1 'second column
LVITEM.pszText$.struct = "Expert"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.SETITEM Aslong, _ '
0 Aslong, _
LVITEM As struct, _
r Aslong'insert second row, first column
LVITEM.iItem.struct = 1 'second row
LVITEM.iSubItem.struct = 0 'first column
LVITEM.pszText$.struct = "Bill Gates"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.INSERTITEM Aslong, _
0 Aslong, _
LVITEM As struct, _
r Aslong'add second column to second row
LVITEM.iItem.struct = 1 'second row
LVITEM.iSubItem.struct = 1 'second column
LVITEM.pszText$.struct = "Novice"
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.SETITEM Aslong, _ '
0 Aslong, _
LVITEM As struct, _
r Aslong'full row select
LVM.FIRST = hexdec("1000")
LVM.SETEXTENDEDLISTVIEWSTYLE = LVM.FIRST + 54
LVS.EX.FULLROWSELECT = hexdec("20")
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.SETEXTENDEDLISTVIEWSTYLE Aslong, _ '
LVS.EX.FULLROWSELECT Aslong, _
LVS.EX.FULLROWSELECT Aslong, _
r Aslong' if you want a grid, leave next message uncommented...
CallDll #user32, "SendMessageA" , hwndLV as uLong, 4150 asLong,_
1 AsLong, 1 AsLong, re asLong'
open"MsgHook"for dll as #MsgHook
hMsgProc = hwnd(#1.GetMsgHookCallback)
calldll #MsgHook, "TrapMsgFor",hwndLV as ulong, ret asboolean
calldll #MsgHook, "WatchMsg", hwndLV as ulong, _WM_LBUTTONDBLCLK aslong, ret asboolean
hMsgProc = hwnd(#1.GetMsgHookCallback)
calldll #user32, "GetWindowLongA",hMsgProc as ulong,_GWL_ID as short,callbackID aslong
calldll #MsgHook, "CreateGetMsgProcHook", hwndParent as ulong, callbackID aslong, _
hMsgProc as ulong, hHook as ulong
wait
[choice] 'determine user selection
calldll #MsgHook, "GetMsg", msg as struct, ret as void
'get number of items in list:
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.GETITEMCOUNT Aslong, _
0 Aslong, _ 'always 0
0 Aslong, _ 'always 0
total Aslongfor index = 0 to total-1 'check each row
LVITEM.mask.struct = LVIF.TEXT OR LVIF.STATE
LVITEM.iItem.struct = index 'row
LVITEM.iSubItem.struct = 0 'first column
LVITEM.cchTextMax.struct = 32
LVITEM.pszText$.struct = space$(32)
LVITEM.stateMask.struct = LVIS.SELECTED
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.GETITEMA Aslong, _
index Aslong, _ 'index of row
LVITEM As struct, _
r Aslong
state = LVITEM.state.struct 'selected state of item
if state and LVIS.SELECTED then
txt$=winstring(LVITEM.pszText$.struct)
notice "Selected: ";txt$
exitforendifnextif txt$=""then notice "No selection."
txt$=""
wait
[add]
'make sure no item is in selected state:
LVITEM.stateMask.struct = LVIS.SELECTED 'bit to set
LVITEM.state.struct = LVIS.UNSELECTED
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.SETITEMSTATE Aslong, _
-1 Aslong, _ 'change applies to all items
LVITEM As struct, _
r Aslong
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.GETITEMCOUNT Aslong, _
0 Aslong, _ 'always 0
0 Aslong, _ 'always 0
count Aslong
name$="No Name"
prompt "Name?";name$
if name$=""then name$="No Name"'insert next row, first column
LVITEM.mask.struct = LVIF.TEXT
LVITEM.iItem.struct = count 'next row
LVITEM.iSubItem.struct = 0 'first column
LVITEM.pszText$.struct = name$
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.INSERTITEM Aslong, _
0 Aslong, _
LVITEM As struct, _
r Aslong
level$="No Level"
prompt "Level?";level$
if level$=""then level$="No Level"'add second column to row
LVITEM.iItem.struct = count 'next row
LVITEM.iSubItem.struct = 1 'second column
LVITEM.pszText$.struct = level$
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.SETITEM Aslong, _
0 Aslong, _
LVITEM As struct, _
r Aslongprint #1.s, "List count: ";count+1
wait
[delete]'get user selection and delete
'get number of items in list:
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.GETITEMCOUNT Aslong, _
0 Aslong, _ 'always 0
0 Aslong, _ 'always 0
total Aslongfor index = 0 to total-1 'check each row
LVITEM.mask.struct = LVIF.STATE
LVITEM.iItem.struct = index 'row
LVITEM.iSubItem.struct = 0 'first column
LVITEM.stateMask.struct = LVIS.SELECTED
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.GETITEMA Aslong, _
index Aslong, _ 'index of row
LVITEM As struct, _
r Aslong
state = LVITEM.state.struct 'selected state of item
if state and LVIS.SELECTED then
CallDLL #user32, "SendMessageA", _
hwndLV As ulong, _
LVM.DELETEITEM Aslong, _
index Aslong, _
LVITEM As struct, _
r Aslongexitforendifnextprint #1.s, "List count: ";total-1
wait
[quit]
calldll #MsgHook, "UnhookMsgHook", hHook as ulong, ret as void
close #MsgHook
calldll #user32, "DestroyWindow", _
hwndLV as ulong, re aslong' Close handles.
close #1:end'Function to determine if a file exists
function fileExists(path$, filename$)
files path$, filename$, info$()
fileExists = val(info$(0, 0)) 'non zero is true
endfunction
Trapping a Double Click in a Listview
Demo by Eldron Gill (-
eaglesoar)
Editor's Note
A listview is an advanced listbox. The listview control is typically used to display members of a data collection in table format. Each column has its own column header. The listview is far more complex than the ordinary listbox. While the listbox is easily populated from its associated array, the listview requires each item to be individually assigned. This assignment is accomplished with a rather complicated series of structs and API calls.
A listview demo is included in Alyce Watson's (-
Capturing mouse clicks in a listview requires a callback function, not easily handled even with Liberty BASIC. Fortunately, Dennis McKinney (-
It should be mentioned that Eldron sought and received permission from both Alyce and Dennis before submitting this demo. Our thanks to all three for this collaboration and sharing of resources and knowledge.