Now that we know how to create a listview control, we will continue on how to use it. I'll focus on report view which can demonstrate many features of listview control. The steps in using a listview control are as follows:
LVM_INSERTCOLUMNiCol is the column number, starting from 0.
wParam = iCol
lParam = pointer to a LV_COLUMN structure
[LV_COLUMN:
imask: D§ ?
fmt: D§ ?
lx: D§ ?
pszText: D§ ?
cchTextMax: D§ ?
iSubItem: D§ ?
iImage: D§ ?
iOrder: D§ ?]
Field name | Meanings |
---|---|
imask | A collection of flags that
governs which members in this structure are valid. The reason behind this
member is that not all members in this structure are used at the same time.
Some members are used in some situations. And this structure is used both
for input and output. Thus it's important that you *mark* the members that
are used in this call to Windows so Windows knows which members are valid.
The available flags are:
LVCF_FMT
= The fmt member is valid.
You can combine the above flags. For example, if you want to specify the text label of the column, you must supply the pointer to the string in pszText member. And you must tell Windows that pszText member contains data by specifying LVCF_TEXT flag in this field else Windows will ignore the value in pszText. |
fmt | Specify the alignment of items/subitems
in the column. The available values are:
LVCFMT_CENTER
= Text is centered.
|
lx | The width of the column, in pixels. You can later change the width of the column with LVM_SETCOLUMNWIDTH. |
pszText | Contains a pointer to the name of the column if this structure is used to set the column's properties. If this structure is used to receive the properties of a column, this field contains a pointer to a buffer large enough to receive the name of the column that will be returned. In that case, you must give the size of the buffer in cchTextMax below. You can ignore cchTextMax if you want to set the name of the column because the name must be an ASCIIZ string which Windows can determine the length. |
cchTextMax | The size, in bytes, of the buffer specified in pszText above. This member is used only when you use this structure to receive info about a column. If you use this structure to set the properties of a column, this field is ignored. |
iSubItem | Specify the index of subitem associated with this column. This value is used as a marker which subitem this column is associated with. If you want, you can specify an absurd number in this field and your listview control will still run like a breeze. The use of this field is best demonstrated when you have the column number and need to know with which subitem this column is associated. You can query the listview control by sending LVM_GETCOLUMN message to it, specifying LVCF_SUBITEM in the imask member. The listview control will fill the iSubItem member with whatever value you specify in this field when the column is inserted. In order for this method to work, you need to input the correct subitem index into this field. |
iImage and iOrder | Used with Internet Explorer 3.0 upwards. I don't have info about them. |
So after the listview control is created, you should insert one or more columns into it. Columns are not necessary if you don't plan to switch the listview control into report view. In order to insert a column, you need to create a LV_COLUMN structure, fill it with necessary information, specify the column number and then send the structure to the listview control with LVM_INSERTCOLUMN message.
The above code snippet demonstrates the process. It specifies the column header text and its width then send LVM_INSERTCOLUMN message to the listview control. It's that simple.
InsertColumn:
mov D§LVC_imask &LVCF_TEXT+&LVCF_WIDTH
mov D§LVC_pszText Heading1
mov D§LVC_lx 150
call 'USER32.SendMessageA' D§hList &LVM_INSERTCOLUMN 0 LV_COLUMN
[LV_ITEM:
imask: D§ ?
iItem: D§ ?
iSubItem: D§ ?
state: D§ ?
stateMask: D§ ?
pszText: D§ ?
cchTextMax: D§ ?
iImage: D§ ?
lParam: D§ ?
iIndent: D§ ?]
Field name | Meanings |
---|---|
imask | A collection of flags indicating which members in this structure are valid for this call. In general, this field is similar to imask member of LV_COLUMN above. Check your win32 api reference for more detail on the available flags. |
iItem | The index of the item this structure refers to. The index is zero-based. You can think of this field as containing the "row" number of a table. |
iSubItem | The index of the subitem associated with the item specified by iItem above. You can think of this field as containing the "column" of a table. For example, if you want to insert an item into a newly created listview control, the value in iItem would be 0 (because this item is the first one), and the value in iSubItem would also be 0 (we want to insert the item into the first column). If you want to specify a subitem associated with this item, the iItem would be the index of the item you want to associate with (in the above example, it's 0), the iSubItem would be 1 or greater, depending on which column you want to insert the subitem into. For example, if your listview control has 4 columns, the first column will contain the items. The remaining 3 columns are for subitems. If you want to insert a subitem into the 4th column, you need to specify the value 3 in iSubItem. |
state | This member contains flags that reflect the status of the item. The state of an item can change because of the user's actions or it can be modified by our program. The state includes whether the item has the focus/is hilited/is selected for cut operation/is selected. In addition to the state flags, It can also contains one-based index into the overlay image/state image for use by the item. |
stateMask | Since the state member above can contain the state flags, overlay image index , and state image index, we need to tell Windows which value we want to set or retrieve. The value in this field is for such use. |
pszText | The address of an ASCIIZ string that will be used as the label of the item in the case we want to set/insert the item. In the case that we use this structure to retrieve the item's property, this member must contain the address of a buffer that will be filled with the label of the item. |
cchTextMax | This field is used only when you use this structure to receive info about an item. In this case, this field contains the size in bytes of the buffer specified in pszText above. |
iImage | The index into the imagelist containing the icons for the listview control. This index points to the icon to be used with this item. |
lParam | A user-defined value that will be used when you sort items in the listview control. In short, when you tell the listview control to sort the items, the listview control will compare the items in pairs. It will send the lParam values of both items to you so you can decide which of the two should be listed first. If you're still hazy about this, don't worry. You'll learn more about sorting later. |
Let's summarize the steps in inserting an item/subitem into a listview control.
LVM_SORTITEMSlParamSort is a user-defined value that will be passed to the compare function. You can use this value in any way you want.
wParam = lParamSort
lParam = pCompareFunction
CompareFunc proto lParam1:DWORD, lParam2:DWORD, lParamSort:DWORD
lParam1
and lParam2 are the values in lParam
member of LV_ITEM that you specify
when you insert the items into the listview control.
lParamSort
is the value in wParam you sent with LVM_SORTITEMS
When the listview control receives LVM_SORTITEMS message, it calls the compare function specified in lParam of the message when it needs to ask us for the result of comparison between two items. In short, the comparison function will decide which of the two items sent to it will precede the other. The rule is simple: if the function returns a negative value, the first item (represented by lParam1) should precede the other. If the function returns a positive value, the second item (represented by lParam2) should precede the first one. If both items are equal, it must return zero.
What makes this method work is the value in lParam of LV_ITEM structure. If you need to sort the items (such as when the user clicks on a column header), you need to think of a sorting scheme that makes use of the values in lParam member. In the example, I put the index of the item in this field so I can obtain other information about the item by sending LVM_GETITEM message. Note that when the items are rearranged, their indexes also change. So when the sorting is done in my example, I need to update the values in lParam to reflect the new indexes. If you want to sort the items when the user clicks on a column header, you need to process LVN_COLUMNCLICK notification message in your window procedure. LVN_COLUMNCLICK is passed to your window proc via WM_NOTIFY message.
_____________________________________________________________________________________
; Equates:
[M00_Menu 2000
M00_View 2001
M00_Icon_View 2002
M00_Report_View 2003
M00_Small_Icon_View 2004 M00_List_View
2005]
_______________________________________________________________________________________
; Data:
[hList: 0 hMenu: 0 FileNameSortOrder:
0 SizeSortOrder: 0
ClassName: 'ListViewWinClass' 0
AppName: 'Testing a ListView
Control' 0
ListViewClassName: 'SysListView32' 0
Heading1: 'Filename' 0
Heading2: 'Size' 0
FileNamePattern: '*.*' 0
template: '%lu' 0]
[WindowHandle: 0 WindowClassName:
B§ 'Anything' 0
WindowCaption: 'Base App' 0]
; Data
____________________________________________________________________________________________
____________________________________________________________________________________________
[FirstMessage: fAdressee: 0 fMessage: 0
fwParam: 0 flParam: 0 0 0 0 0 0 0] ; Win message
[MenuHandle: 0]
[WindowX: 50 WindowY: 50 WindowW:650
WindowH:450]
[WindowClassEx: len
style: 0 lpfnWndProc: MainWindowProc
cbClsExtra: 0 cbWndExtra: 0
hInstance: 0 hIcon: 0 hCursor:
0 hbrBackground: 6 ; COLOR_WINDOW+1
lpszMenuName: M00_Menu lpszClassName:
ClassName hIconSm: 0]
____________________________________________________________________________________________
____________________________________________________________________________________________
Main:
call 'Kernel32.GetModuleHandleA' 0 | mov D§hInstance eax
call 'User32.LoadIconA'
0 &IDI_APPLICATION | mov D§hIcon eax, D§hIconSm eax
call 'User32.LoadCursorA'
0 &IDC_ARROW | mov D§hCursor eax
call 'User32.RegisterClassExA' WindowClassEx
call 'User32.CreateWindowExA'
&NULL ClassName AppName,
&WS_OVERLAPPEDWINDOW,
&CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT,
0 0 D§hInstance 0
mov D§WindowHandle
eax
call 'User32.ShowWindow'
D§WindowHandle &SW_SHOW
call 'User32.UpdateWindow'
D§WindowHandle
L1: call 'User32.GetMessageA' FirstMessage 0
0 0 | cmp eax 0 | je L9>
call
'User32.TranslateMessage' FirstMessage
call
'User32.DispatchMessageA' FirstMessage
jmp L1<
L9: call 'Kernel32.ExitProcess' 0
______________________________________________________________________________________
[LV_COLUMN: LVC_imask: 0
LVC_fmt: 0 LVC_lx: 0 LVC_pszText:
0
LVC_cchTextMax: 0 LVC_iSubItem: 0]
InsertColumn:
mov D§LVC_imask &LVCF_TEXT+&LVCF_WIDTH
mov D§LVC_pszText Heading1
mov D§LVC_lx 150
call 'USER32.SendMessageA'
D§hList &LVM_INSERTCOLUMN 0 LV_COLUMN
or D§LVC_imask &LVCF_FMT
mov D§LVC_fmt &LVCFMT_RIGHT
mov D§LVC_pszText Heading2
mov D§LVC_lx 100
call 'USER32.SendMessageA'
D§hList &LVM_INSERTCOLUMN 1 LV_COLUMN
ret
[Row: 0] [ShowFileBuffer: B§ 0 #200]
ShowFileInfo:
mov D§LVI_imask &LVIF_TEXT+&LVIF_PARAM
push D§row | pop D§LVI_iItem
mov D§LVI_iSubItem 0,
D§LVI_pszText WFD_cFileName
push D§row | pop D§LVI_lParam
call 'USER32.SendMessageA'
D§hList &LVM_INSERTITEM 0 LV_ITEM
mov D§LVI_imask &LVIF_TEXT
| inc D§LVI_iSubItem
call 'USER32.wsprintfA'
ShowFilebuffer template D§WFD_nFileSizeLow | pop eax eax eax
mov D§LVI_pszText ShowFileBuffer
call
'USER32.SendMessageA' D§hList &LVM_SETITEM 0
LV_ITEM
mov D§LVI_pszText Buffer1
ret
_________________________________________________________________________________________
[WIN32_FIND_DATA: WFD_dwFileAttributes:
0 WFD_ftCreationTime: 0 0
WFD_ftLastAccessTime: 0 0 WFD_ftLastWriteTime: 0 0
WFD_nFileSizeHigh: 0 WFD_nFileSizeLow: 0
WFD_dwReserved0: 0 WFD_dwReserved1:
0]
[WFD_cFileName: B§ 0#32] [WFD_cAlternate: B§ 0 #14]
[WFDlen: WFDlen-WIN32_FIND_DATA]
[FHandle: 0]
FillFileInfo:
call 'KERNEL32.FindFirstFileA'
FileNamePattern WIN32_FIND_DATA
.If eax ne &INVALID_HANDLE_VALUE
mov
D§FHandle eax, D§Row 0
.While
eax > 0
mov eax D§WFD_dwFileAttributes | and eax &FILE_ATTRIBUTE_DIRECTORY
If eax = 0
call ShowFileInfo
inc D§Row
End_If
call 'KERNEL32.FindNextFileA' D§FHandle WIN32_FIND_DATA
.End_While
call
'KERNEL32.FindClose' D§FHandle
.End_If
ret
; This translation for Ascii decimal to Binary
vvalue in eax do not need any security test
; because it is an amount of bytes in a file;
so:
String2Dword:
mov eax 0, ecx 0
L0: mov cl B§esi | inc esi
cmp cl 0 | je L9>
mov edx 10 | mul
edx
sub
ecx '0'
add eax ecx | jmp L0<
L9: ret
[SortType: 0 CF_Para1: 0 CF_Para2: 0]
ResetLV_ITEM:
pushad
mov edi LV_ITEM,
eax 0, ecx 9 | rep stosd
mov D§LVI_cchTextMax
256, D§LVI_imask &LVIF_TEXT, D§LVI_pszText Buffer1
popad
ret
; This is the CallBack Proc given in 'LVM_SORTITEMS'
message: ( the direct POPs of
; messages parameters works fine in these simple
cases):
CompareFunc:
pop eax, D§CF_Para1,
D§CF_Para2, D§LVI_iSubItem | push eax
.If D§LVI_iSubItem =
1
mov
D§LVI_iSubItem 1
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
mov
esi Buffer1 | call String2Dword
mov
edi eax
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
esi Buffer1 | call String2Dword
sub
edi eax
mov
eax,edi
.Else_If D§LVI_iSubItem
= 2
mov
D§LVI_iSubItem 1
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
mov
esi Buffer1 | call String2Dword
mov
edi eax
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
esi Buffer1 | call String2Dword
sub
eax edi
.Else_If D§LVI_iSubItem
= 3
mov
D§LVI_iSubItem 0
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
mov
D§LVI_pszText buffer2
call 'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
D§LVI_pszText buffer1
call
'Kernel32.lstrcmpi' buffer2 buffer1
.Else
mov
D§LVI_iSubItem,0
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
mov
D§LVI_pszText buffer2
call 'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
D§LVI_pszText buffer1
call
'Kernel32.lstrcmpi' buffer1 buffer2
.End_If
call ResetLV_ITEM
ret
UpdatelParam:
call 'USER32.SendMessageA'
D§hList &LVM_GETITEMCOUNT 0 0
mov edi eax
mov D§LVI_imask &LVIF_PARAM
mov D§LVI_iItem 0
.While edi a 0
push
D§LVI_iItem | pop D§LVI_lParam
call
'USER32.SendMessageA' D§hList &LVM_SETITEM 0 LV_ITEM
inc
D§LVI_iItem
dec
edi
.End_While
ret
[LV_ITEM: LVI_imask: &LVIF_TEXT
LVI_iItem: 0 LVI_iSubItem: 0 LVI_state:
0
LVI_stateMask: 0 LVI_pszText: Buffer1 LVI_cchTextMax: 256
LVI_iImage: 0
LVI_lParam: 0]
[Buffer1: B§ 0 #256] [Buffer2: B§ 0 #256]
ShowCurrentFocus:
call 'USER32.SendMessageA'
D§hList &LVM_GETNEXTITEM 0-1 &LVNI_FOCUSED
mov D§LVI_iItem eax
mov D§LVI_iMask &LVIF_TEXT
call 'USER32.SendMessageA'
D§hList &LVM_GETITEM 0 LV_ITEM
call 'USER32.MessageBoxA'
0 buffer1 AppName 0
ret
__________________________________________________________________________________
; structure used only to set pointer to parameters in WM_NOTIFY down there:
[NM_LISTVIEW:
NMHDR: NMHDR_hwndFrom: 0 NMHDR_idfrom:
0 NMHDR_code: 0
NML_iItem: 0
NML_iSubItem: 0
NML_uNewState: 0
NML_uOldState: 0
NML_uChanged: 0
NML_ptAction: 0 0
NML_lParam: 0]
Proc MainWindowProc:
Arguments @Adressee, @Message,
@wParam, @lParam
pushad
.If D@Message = &WM_CREATE
call 'Comctl32.InitCommonControls'
call 'USER32.CreateWindowExA'
&NULL ListViewClassName &NULL, ; This Child
&LVS_REPORT+&WS_CHILD+&WS_VISIBLE, ; Window is shown
0 0 0 0,
; up by lower
D@Adressee &NULL D§hInstance &NULL ; WM_SIZE Message case
mov D§hList
eax
call InsertColumn
call FillFileInfo
RGB 255
255 255
call 'USER32.SendMessageA'
D§hList &LVM_SETTEXTCOLOR 0 eax
RGB 0 0
0
call 'USER32.SendMessageA'
D§hList &LVM_SETBKCOLOR 0 eax
RGB 0,0,0
call 'USER32.SendMessageA'
D§hList &LVM_SETTEXTBKCOLOR 0 eax
call 'USER32.GetMenu'
D@Adressee
mov D§hMenu
eax
call 'USER32.CheckMenuRadioItem'
D§hMenu M00_Icon_View M00_Report_View,
M00_Report_View,
&MF_CHECKED
.Else_If D@Message = &WM_COMMAND
If
D@lParam = 0
call 'USER32.GetWindowLongA' D§hList &GWL_STYLE
or eax &LVS_TYPEMASK | xor eax &LVS_TYPEMASK
mov edx D@wParam | and edx 0FFFF
sub edx 2002
; Makes the same as:
;
If edx = M00_Icon_View
; 2002 > 0
;
mov edx LVS_ICON
;
Else_If edx = M00_Small_Icon_View ; 2003 > 2
;
mov edx LVS_SMALLICON
;
Else_If edx = M00_List_View
; 2004 > 3
;
mov edx LVS_LIST
;
Else_If edx = M00_Report_View
; 2005 > 1
;
mov edx LVS_REPORT
;
End_If
push edx
or eax edx
call 'USER32.SetWindowLongA' D§hList &GWL_STYLE eax
pop edx
call 'USER32.CheckMenuRadioItem' D§hMenu M00_Icon_View
M00_Report_View,
edx &MF_CHECKED
End_If
.Else_If D@Message = &WM_NOTIFY
push
edi
mov edi D@lParam | sub edi NMHDR
mov eax D§edi+NMHDR_hwndFrom
..If eax = D§hList
...If D§edi+NMHDR_code = &LVN_COLUMNCLICK
mov edi D@lParam | sub edi NM_LISTVIEW
If D§edi+NML_iSubItem = 1
On D§SizeSortOrder = 0, mov D§SizeSortOrder 2
xor D§SizeSortOrder 00_11
call 'USER32.SendMessageA' D§hList &LVM_SORTITEMS,
D§SizeSortOrder CompareFunc
call UpdatelParam
Else
On D§FileNameSortOrder = 0, mov D§FileNameSortOrder 4
xor D§FileNameSortOrder 00_111
call 'USER32.SendMessageA' D§hList &LVM_SORTITEMS,
D§FileNameSortOrder CompareFunc
call UpdatelParam
End_If
...Else_If D§NMHDR_code = &NM_DBLCLK
call ShowCurrentFocus
...End_If
..End_If
pop edi
.Else_If D@Message = &WM_SIZE
mov
eax D@lParam, edx eax
and
eax 0ffff | shr edx 16
call
'USER32.MoveWindow' D§hList 0 0 eax edx &TRUE
.Else_If D@Message = &WM_DESTROY
call
'USER32.PostQuitMessage' &NULL
.Else
popad
call
'USER32.DefWindowProcA' D@Adressee D@Message D@Wparam D@Lparam
Exit
.endif
popad | mov eax &FALSE
EndP
.If D@Message = &WM_CREATE
call 'Comctl32.InitCommonControls'
call 'USER32.CreateWindowExA'
&NULL ListViewClassName &NULL, ; This Child
&LVS_REPORT+&WS_CHILD+&WS_VISIBLE, ; Window is shown
0 0 0 0,
; up by lower
D@Adressee &NULL D§hInstance &NULL ; WM_SIZE Message case
mov D§hList
eax
We call CreateWindowEx, passing itthe name of the window class "SysListView32". The default view is the report view as specified by LVS_REPORT style.
call InsertColumn
After the listview control
is created, we insert columns into it.
InsertColumn:
mov D§LVC_imask &LVCF_TEXT+&LVCF_WIDTH
mov D§LVC_pszText Heading1
mov D§LVC_lx 150
call 'USER32.SendMessageA'
D§hList &LVM_INSERTCOLUMN 0 LV_COLUMN
We specify the label and the
width of the first column, for storing the names of the files, in LV_COLUMN
structure thus we need to set imask
with LVCF_TEXT and LVCF_WIDTH
flags. We fill pszText with the address
of the label and lx with the width
of the column, in pixels. When all is done, we send LVM_INSERTCOLUMN
message to the listview control, passing the structure to it.
or D§LVC_imask &LVCF_FMT
mov D§LVC_fmt &LVCFMT_RIGHT
When we are done with the insertion
of the first column, we insert another column for storing the sizes of
the files. Since we need the sizes to right-align in the column, we need
to specify a flag in fmt member, LVCFMT_RIGHT.
We must also specify LVCF_FMT flag
in imask, in addition to LVCF_TEXT
and LVCF_WIDTH.
mov D§LVC_pszText Heading2
mov D§LVC_lx 100
call 'USER32.SendMessageA'
D§hList &LVM_INSERTCOLUMN 1 LV_COLUMN
The remaining code is simple. Put the address of the label in pszText and the width in lx. Then send LVM_INSERTCOLUMN message to the listview control, specifying the column number and the address of the structure.
When the columns are inserted, we can fill items in the listview control.
call FillFileInfo
FillFileInfo has the following
code.
FillFileInfo:
call 'KERNEL32.FindFirstFileA'
FileNamePattern WIN32_FIND_DATA
We call FindFirstFile to obtain the information of the first file that matches the search criteria. FindFirstFile has the following prototype:
FindFirstFile proto pFileName:DWORD, pWin32_Find_Data:DWORD
pFileName
is the address of the filename to search for. This string can contain wildcards.
In our example, we use *.*, which amounts to search for all the files in
the current folder.
pWin32_Find_Data
is the address of the WIN32_FIND_DATA
structure that will be filled with information about the file (if found).
This function returns
INVALID_HANDLE_VALUE in eax if no matching file is found. Otherwise
it returns a search handle that will be used in subsequent FindNextFile
calls.
.If eax ne &INVALID_HANDLE_VALUE
mov
D§FHandle eax, D§Row 0
If a file is found, we store
the search handle in a variable and then zero out edi which will be used
as the index into the items (row number).
.While
eax > 0
mov eax D§WFD_dwFileAttributes | and eax &FILE_ATTRIBUTE_DIRECTORY
If eax = 0
In this tutorial, I don't want
to deal with the folders yet so I filter them out by checking dwFileAttributes
for files which have FILE_ATTRIBUTE_DIRECTORY
flag set. If they are found, I skip to call FindNextFile.
call ShowFileInfo
inc D§Row
End_If
call 'KERNEL32.FindNextFileA' D§FHandle WIN32_FIND_DATA
.End_While
We insert the name and size
of the file into the listview control by calling ShowFileInfo function.
Then we increase the current row number in edi. Lastly we proceed to call
FindNextFile
to search for the next file in the current folder until FindNextFile
returns 0 (meaning no more file is found).
call
'KERNEL32.FindClose' D§FHandle
.End_If
When all files in the current folder are enumerated, we must close the search handle.
Now let's look at the ShowFileInfo function. This function accepts two parameters, the index of the item (row number) and the address of WIN32_FIND_DATA structure.
ShowFileInfo:
Store the address of WIN32_FIND_DATA
structure in edi.
mov D§LVI_imask &LVIF_TEXT+&LVIF_PARAM
push D§row | pop D§LVI_iItem
mov D§LVI_iSubItem 0,
D§LVI_pszText WFD_cFileName
We will supply the label of
the item and the value in lParam so we put LVIF_TEXT
and LVIF_PARAM flags into imask. Next
we set the iItem to the row number passed to the function and since this
is the main item, we must filliSubItem with 0 (column 0).
push D§row | pop D§LVI_lParam
Next we put the address of the label, in this case, the name of the file in WIN32_FIND_DATA structure, into pszText. Because we will implement sorting in the listview control, we must fill lParam with a value. I choose to put the row number into this member so I can retrieve the item info by its index.
call 'USER32.SendMessageA'
D§hList &LVM_INSERTITEM 0 LV_ITEM
When all necessary fields in
LV_ITEM
are filled, we send LVM_INSERTITEM
message to the listview control to insert the item into it.
mov D§LVI_imask &LVIF_TEXT
| inc D§LVI_iSubItem
call 'USER32.wsprintfA'
ShowFilebuffer template D§WFD_nFileSizeLow | pop eax eax eax
mov D§LVI_pszText ShowFileBuffer
We will set the subitem associated
with the item just inserted into the second column. A subitem can only
have a label. Thus we specify LVIF_TEXT
in imask. Then we specify the column that the subitem should reside in
iSubItem.
In this case, we set it to 1 by incrementing iSubItem.
The label we will use is the size of the file. However, we must convert
it to a string first by calling wsprintf.
Then we put the address of the string into pszText.
call
'USER32.SendMessageA' D§hList &LVM_SETITEM 0
LV_ITEM
mov D§LVI_pszText Buffer1
ret
When all necessary fields in LV_ITEM are filled, we send LVM_SETITEM message to the listview control, passing to it the address of the LV_ITEM structure. Note that we use LVM_SETITEM, not LVM_INSERTITEM because a subitem is considered as a property of an item. Thus we *set* the property of the item, not inserting a new item.
When all items are inserted
into the listview control, we set the text and background colors of the
listview control.
RGB 255 255
255
call 'USER32.SendMessageA'
D§hList &LVM_SETTEXTCOLOR 0 eax
RGB 0 0
0
call 'USER32.SendMessageA'
D§hList &LVM_SETBKCOLOR 0 eax
RGB 0,0,0
call 'USER32.SendMessageA'
D§hList &LVM_SETTEXTBKCOLOR 0 eax
I use RGB macro to convert
the red, green,blue values into eax and use it to specify the color we
need. We set the foreground and background colors of the text with LVM_SETTEXTCOLOR
and
LVM_SETTEXTBKCOLOR messages. We set
the background color of the listview control by sending LVM_SETBKCOLOR
message to the listview control.
call 'USER32.GetMenu'
D@Adressee
mov D§hMenu
eax
call 'USER32.CheckMenuRadioItem'
D§hMenu M00_Icon_View M00_Report_View,
M00_Report_View,
&MF_CHECKED
We will let the user chooses the views he wants via the menu. Thus we must obtain the menu handle first. To help the user track the current view, we put a radio button system in our menu. The menu item that reflects the current view will be preceded by a radio button. That's why we call CheckMenuRadioItem. This function will put a radio button before a menu item.
Note that we create the listview
control with width and height equal to 0. It will be resized later whenever
the parent window is resized. This way, we can ensure that the size of
the listview control will always match that of the parent window. In our
example, we want the listview control to fill the whole client area of
the parent window.
.Else_If D@Message = &WM_SIZE
mov
eax D@lParam, edx eax
and
eax 0ffff | shr edx 16
call
'USER32.MoveWindow' D§hList 0 0 eax edx &TRUE
When the parent window receives WM_SIZE message, the low word of lParam contains the new width of the client area and the high word the new height. Then we call MoveWindow to resize the listview control to cover the whole client area of the parent window.
When the user selects a view
in the menu. We must change the view in the listview control accordingly.
We accomplish this by setting a new style in the listview control with
SetWindowLong.
.Else_If D@Message = &WM_COMMAND
If
D@lParam = 0
call 'USER32.GetWindowLongA' D§hList &GWL_STYLE
or eax &LVS_TYPEMASK | xor eax &LVS_TYPEMASK
The first thing we do is to
obtain the current styles of the listview control. Then we clear the old
view style from the returned style flags. LVS_TYPEMASK
is a constant that is the combined value of all 4 view style constants
(LVS_ICON+LVS_SMALLICON+LVS_LIST+LVS_REPORT).
Thus when we perform and operation
on the current style flags with the value "not LVS_TYPEMASK", it amounts
to clearing away the current view style.
When the parent window receives
WM_COMMAND message, the desired view style is in the low word of wParam
as the menu ID.
mov edx D@wParam | and edx 0FFFF
We have the desired view style
in the low word of wParam. All we have to do is to zero out the high word.
push edx
or eax edx
And add the desired view style to the existing styles (minus the current view style) of the listview control.
call 'USER32.SetWindowLongA' D§hList &GWL_STYLE eax
And set the new styles with
SetWindowLong.
pop edx
call 'USER32.CheckMenuRadioItem' D§hMenu M00_Icon_View
M00_Report_View,
edx &MF_CHECKED
End_If
We also need to put the radio button in front of the selected view menu item. Thus we call CheckMenuRadioItem, passing the current view style (double as menu ID) to it.
When the user clicks on the
column headers in the report view, we want to sort the items in the listview
control. We must respond to WM_NOTIFY
message.
.Else_If D@Message = &WM_NOTIFY
push
edi
mov edi D@lParam | sub edi NMHDR
mov eax D§edi+NMHDR_hwndFrom
..If eax = D§hList
When we receive WM_NOTIFY message, lParam contains the pointer to an NMHDR structure. We can check if this message is from the listview control by comparing the hwndFrom member of NMHDR to the handle to the listview control. If they match, we can assume that the notification came from the listview control.
...If D§edi+NMHDR_code = &LVN_COLUMNCLICK
mov edi D@lParam | sub edi NM_LISTVIEW
If the notification is from
the listview control, we check if the code is LVN_COLUMNCLICK.
If it is, it means the user clicks on a column header. In the case that
the code is LVN_COLUMNCLICK, we can
assume that lParam contains the pointer to an NM_LISTVIEW
structure which is a superset of the NMHDR
structure. We then need to know on which column header the user clicks.
Examination of iSubItem member reveals
this info. The value in iSubItem can be treated as the column number, starting
from 0.
In the case iSubItem is 1,
it means the user clicks on the second column, size. We use state variables
to keep the current status of the sorting order. 0 means "no sorting yet",
1 means "sort ascending", 2 means "sort descending". If the items/subitems
in the column are not sorted before, or sorted descending, we set the sorting
order to ascending.
call 'USER32.SendMessageA' D§hList &LVM_SORTITEMS,
D§SizeSortOrder CompareFunc
We send LVM_SORTITEMS
message to the listview control, passing 1 in wParam and the address of
our comparison function in lParam. Note that the value in wParam is user-defined,
you can use it in any way you like. I use it as the sorting method in this
example. We will take a look at the comparison function first.
CompareFunc:
pop eax, D§CF_Para1,
D§CF_Para2, D§LVI_iSubItem | push eax
In the comparison function,
the listview control will pass lParams (in LV_ITEM) of the two items it
needs to compare to us in lParam1 and lParam2. You'll recall that we put
the index of the item in lParam. Thus we can obtain information about the
items by querying the listview control using the indexes. The info we need
is the labels of the items/subitems being sorted. Thus we prepare an
LV_ITEM structure for such purpose, specifying LVIF_TEXT
in imask and the address of the buffer
in pszText and the size of the buffer
in cchTextMax.
.If D§LVI_iSubItem = 1
mov
D§LVI_iSubItem 1
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
If the value in SortType is
1 or 2, we know that the size column is clicked. 1 means sort the items
according to their sizes in ascending order. 2 means the reverse. Thus
we specify iSubItem as 1 ( to specify
the size column) and send LVM_GETITEMTEXT
message to the listview control to obtain the label (size string) of the
subitem.
mov
esi Buffer1 | call String2Dword
mov
edi eax
Covert the size string into
a dword value with String2Dword which is the function I wrote. It returns
the dword value in eax. We store it in edi for comparison later.
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
esi Buffer1 | call String2Dword
sub
edi eax
mov
eax,edi
Do likewise with the value
in lParam2. When we have the sizes of the two files, we can then compare
them.
The rule of the comparison
function is as follows:
.Else_If D§LVI_iSubItem
= 2
mov
D§LVI_iSubItem 1
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para1
LV_ITEM
mov
esi Buffer1 | call String2Dword
mov
edi eax
call
'USER32.SendMessageA' D§hList &LVM_GETITEMTEXT D§CF_Para2
LV_ITEM
mov
esi Buffer1 | call String2Dword
sub
eax edi
In case the user clicks the filename column, we must compare the names of the files. We first obtain the filenames and then compare them with lstrcmpi function. We can return the return value of lstrcmpi without any modification since it also uses the same rule of comparison, eg. negative value in eax if the first string is less than the second string.
When the items were sorted, we need to update the lParam values of all items to reflect the new indexes by calling UpdatelParam function.
call UpdatelParam
This function simply enumerates all items in the listview control and updates the values in lParam with the new indexes. We need to do this else the next sort will not work as expected because our assumption is that the value in lParam is the index of the item.
...Else_If D§NMHDR_code = &NM_DBLCLK
call ShowCurrentFocus
When the user double-clicks
at an item, we want to display a message box with the label of the item
on it. We must check if the code in NMHDR
is NM_DBLCLK. If it is, we can proceed
to obtain the label and display it in a message box.
ShowCurrentFocus:
call 'USER32.SendMessageA'
D§hList &LVM_GETNEXTITEM 0-1 &LVNI_FOCUSED
How do we know which item is
double-clicked? When an item is clicked or double-clicked, its state is
set to "focused". Even if many items are hilited (selected), only one of
them has got the focus. Our job than is to find the item that has the focus.
We do this by sending LVM_GETNEXTITEM
message to the listview control, specifying the desired state in lParam.
-1 in wParam means search all items. The index of the item is returned
in eax.
mov D§LVI_iItem eax
mov D§LVI_iMask &LVIF_TEXT
call 'USER32.SendMessageA'
D§hList &LVM_GETITEM 0 LV_ITEM
We then proceed to obtain the label by sending LVM_GETITEM message to the listview control.
invoke MessageBox,0, addr buffer,addr
AppName,MB_OK
call 'USER32.MessageBoxA'
0 buffer1 AppName 0
Lastly, we display the label in a message box.
If you want to know how to
use icons in the listview control, you can read about it in my treeview
tutorial. The steps are just about the same.