Tutorial 31: ListView Control
We Well Learn How to create and use the listview control in this tutorial.
THEORY:
A listview control is one of the common controls like treeview, richedit etc. You are familiar with it even if you may not know it by its name. For example, the right pane of Windows Explorer is a listview control. A listview control is good for displaying items in this regard, it's like a listbox but with enhanced capabilities.You can create a listview control in two ways The first method is also the easiest one:... create it with a resource editor Just do not forget to call InitCommonControls within your asm source code. The other method is to call CreateWindowEx in your source code. You must specify the correct window class name for the control, ie. SysListView32. The window class "WC_LISTVIEW" is incorrect. There are four methods of viewing data in a listview control:. icon, small icon, list and report views You can see examples of these views by selecting View-> Large Icons (icon view), Small Icons (small icon view), List (list view) and Details (Report View). Views Are Just Data Re presentation methods: they only affect the appearances of data For example, you can have a lot of data in the listview control, but if you want, you can view only some of them Report view is the most informative one while the remaining views.. Give Less Info. You can start a listview control. You can Later Change The View by Calling SetWINDOWLONG, SPECIFYING GWL_STYLE FLAG.
. 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:. Create a listview control with CreateWindowEx, specifying SysListView32 as the class name. You can specify the initial view at this time. (if exists) Create and initialize image lists to be used with the listview items. Insert column (s) into the listview control. This step IS Necessary if The ListView Control Will Use Report View. INSERT ITEMS AND SUBITEMS INTO The ListView Control.
Columns
In report view, there are one or more columns You can think of the arrangement of data in the report view as a table:. The data are arranged in rows and columns You must have at least one column in your listview control (only in. report view). in views other than report, you need not insert a column because there can be one and only one column in those views.You can insert a column by sending LVM_INSERTCOLUMN to the listview control.
LVM_INSERTCOLUMNWPARAM = iCollParam = Pointer to a lv_column structure
Icol is the column number, starting from 0.lv_column contains information about the column to be inserted. It has the folload definition:
LV_COLUMN STRUCT IMASK DD? FMT DD? LX DD? PSZTEXT DD? CCHTEXTMAX DD? ISUBITEM DD? IIMAGE DD? IORDER DD? LV_COLUMN ENDS
Field nameMeaningsimask 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 LVCF_SUBITEM = The iSubItem member is valid. LVCF_TEXT = The pszText member is valid. LVCF_WIDTH = The lx 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 ar e:... LVCFMT_CENTER = Text is centered LVCFMT_LEFT = Text is left-aligned LVCFMT_RIGHT = Text is right-aligned.lxThe width of the column, in pixels You can later change the width of the column with LVM_SETCOLUMNWIDTH.pszTextContains a pointer to the Name of the column if this structure is buy to set the colorn '
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.cchTextMaxThe 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.iSubItemSpecify 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 kn ow 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 ....................................................
So after the listview control is created, you should insert one or more columns into it. Columns are not necessary if you do not 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.LOCAL lvc: LV_COLUMN mov lvc.imask, LVCF_TEXT LVCF_WIDTH mov lvc.pszText, offset Heading1 mov lvc.lx, 150 Invoke SendMessage, HLIST, LVM_INSERTCOLUMN, 0, AddR LVC
The Above Code Snippet Demonstrates The Process. It's TEXT AND ITS WIDTH THEN SEND LVM_INSERTCOLUMN Message to The ListView Control. It's today Simple.
Items and Subitems
Items are the main entries in the listview control. In views other than report view, you will only see items in the listview control. Subitems are details of the items. An item may have one or more associated subitems. For example, if the item is the name of a file, then you can have the file attributes, its size, the date of file creation as subitems. In report view, the leftmost column contains items and the remaining columns contain subitems. you can think of an item and its subitems as a database record The item is the primary key of the record and the subitems are fields in the record At the bare minimum, you need some items in your listview control:.. subitems are not necessary However, if you want to give. the user more information about the items, you can associate items with subitems so the user can see the details in the report view.You insert an item into a listview control by sending LVM_INSERTITEM message to it. you also need to pass the address of an LV_Item Structure To IT IITEM HAS THE FOLLOWING DEFINITION: LV_ITEM STRUCT IMASK DD? IIITEM DD? ISUBITEM DD? STATE DD? STATEMASK DD? PSZTEXT DD? CCHTEXTMAX DD? IIMAGE DD? LPARAM DD? IINDENT DD? LV_Item Ends
Field nameMeaningsimaskA 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.iItemThe 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.iSubItemThe 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 subitem specify a with this item associated, 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, Dependin g 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 Me '
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. stateMaskSince 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.pszTextThe 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.cchTextMaxThis 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.iimagethe Index INTO THE IM agelist containing the icons for the listview control. This index points to the icon to be used with this item.lParamA 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, do not Worry. You'll Learn More About Sorting Later.Let's Summarize The Steps in Inserting An Item / Subitem Into a ListView Control.
Create a variable of type LV_ITEM structure Fill it with necessary information Send LVM_INSERTITEM message to the listview control if you want to insert an item. Or if you want to * insert * a subitem, send LVM_SETITEM instead. This is rather confusing if you don ' t understand the relationship between an item and its subitems. Subitems are considered as properties of an item. Thus you can insert items but not subitems and you can not have a subitem without an associated item. That's why you need to send LVM_SETITEM message to Add A Subitem INSTEAD OF LVM_INSERTITEM.LISTVIEW Messages / Notifications
Now that you know how to create and populate a listview control, the next step is to communicate with it. A listview control communicates with the parent window via messages and notifications. The parent window can control the listview control by sending messages to it. The Listview Control Notifies The Parent of Important / Interesting Events Via WM_Notify Message, Just Like Other Common Controls.
Sorting Items / SubItems
You can specify the default sorting order of a listview control by specifying LVS_SORTASCENDING or LVS_SORTDESCENDING styles in CreateWindowEx. These two styles order the items using their labels only. If you want to sort the items in other ways, you need to send LVM_SORTITEMS message to the Listview control.
LVM_SORTITEMSWPARAM = lparamsortlparam = pcomparefunction
lParamSort is a user-defined value that will be passed to the compare function. You can use this value in any way you want.pCompareFunction is the address of the user-defined function that will decide the outcome of the comparison of items in the listview Control. The Function Has The Following Prototype:
CompareFunc proto lParam1: DWORD, lParam2: DWORD, lParamSort: DWORDlParam1 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.
EXAMPLE:
This example creates a listview control and fills it with the names and sizes of the files in the current folder. The default view is the report one. In the report view, you can click on the column heads and the items will be sorted in ascending .
.386 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/user32.inc include /masm32/include/kernel32.inc include /masm32/include/comctl32.inc includelib / masm32 /lib/comctl32.lib includelib /masm32/lib/user32.lib includelib /masm32/lib/kernel32.lib WinMain proto: DWORD,: DWORD,: DWORD,: DWORD IDM_MAINMENU equ 10000 IDM_ICON equ LVS_ICON IDM_SMALLICON equ LVS_SMALLICON IDM_LIST equ LVS_LIST IDM_REPORT equ LVS_REPORT RGB macro red, green, blue xor eax, eax mov ah, blue shl eax, 8 mov ah, green mov al, red endm .data ClassName db "ListViewWinClass", 0 AppName db "Testing a ListView Control", 0 ListViewClassName DB "syslistView32", 0 Heading1 DB "filename", 0 Heading2 DB "size", 0 filenamepattern DB "*. *", 0 FileNameSortorder DD 0 SIZESORTORTER DD 0 Template DB "% Lu", 0.data? Hinstance Hinstance? HLIST DD? HMENU DD? Code Start: Invoke GetModuleHandle, Null Mov Hinstance, Eax Invoke Winmain, Hinstance, Null, Null, SW_SHOWDEFAULT INVOKE EXITP rocess, eax invoke InitCommonControls WinMain proc hInst: HINSTANCE, hPrevInst: HINSTANCE, CmdLine: LPSTR, CmdShow: DWORD LOCAL wc: WNDCLASSEX LOCAL msg: MSG LOCAL hwnd: HWND mov wc.cbSize, SIZEOF WNDCLASSEX mov wc.style, NULL mov wc. LpfnWndProc, Offset WndProc Mov Wc.cbclsextra, Null Mov Wc.cbwndextra, Null Push Hinstance Pop Wc.hinstance Mov Wc.hbrbackground, Color_Window
1 mov wc.lpszMenuName, IDM_MAINMENU mov wc.lpszClassName, OFFSET ClassName invoke LoadIcon, NULL, IDI_APPLICATION mov wc.hIcon, eax mov wc.hIconSm, eax invoke LoadCursor, NULL, IDC_ARROW mov wc.hCursor, eax invoke RegisterClassEx, addr wc invoke CreateWindowEx, NULL, ADDR ClassName, ADDR AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL mov hwnd, eax invoke ShowWindow, hwnd, SW_SHOWNORMAL invoke UpdateWindow, hwnd .while TRUE invoke GetMessage, ADDR msg, NULL, 0,0 .break .if (! eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endw mov eax, msg.wParam ret WinMain endp InsertColumn proc LOCAL lvc: LV_COLUMN mov lvc.imask, LVCF_TEXT LVCF_WIDTH mov lvc .pszText, offset Heading1 mov lvc.lx, 150 invoke SendMessage, hList, LVM_INSERTCOLUMN, 0, addr lvc or lvc.imask, LVCF_FMT mov lvc.fmt, LVCFMT_RIGHT mov lvc.pszText, offset Heading2 mov lvc.lx, 100 invoke SendMessage, hList, LVM_INSERTCOLUMN, 1, addr lvc ret InsertColumn endp ShowFileInfo proc uses edi row: DWORD, lpFind: DWORD LOCAL lvi: LV_ITEM LOCAL buffer [20]: BYTE mov edi, lpFind assume edi: ptr WIN32_FIND_DATA mov lvi.imask, LVIF_TEXT
LVIF_PARAM push row pop lvi.iItem mov lvi.iSubItem, 0 lea eax, [edi] .cFileName mov lvi.pszText, eax push row pop lvi.lParam invoke SendMessage, hList, LVM_INSERTITEM, 0, addr lvi mov lvi.imask, LVIF_TEXT inc lvi.iSubItem invoke wsprintf, addr buffer, addr template, [edi] .nFileSizeLow lea eax, buffer mov lvi.pszText, eax invoke SendMessage, hList, LVM_SETITEM, 0, addr lvi assume edi: nothing ret ShowFileInfo endp FillFileInfo proc uses edi LOCAL finddata: WIN32_FIND_DATA LOCAL FHandle: DWORD invoke FindFirstFile, addr FileNamePattern, addr finddata .if eax = INVALID_HANDLE_VALUE mov FHandle, eax xor edi, edi .while eax = 0 test finddata.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY .if ZERO invoke ShowFileInfo, edi!!? Addr Finddata Inc Edi .endif Invoke FindnextFile, Fhandle, AddR Finddata .Endw Invoke Findclose, Fhandle .endif Ret Fillfileinfo Endp String2dword Proc Uses EDI EDX ESI STRING: DWORD LOCAL Resul T: DWORD MOV Result, 0 MOV EDI, STRING INVOKE LSTRLEN, STRING .WHILE EAX! = 0 xor Edx, EDX MOV DL, BYTE PTR [EDI] Sub DL, "0" Mov ESI, ESI PUSH EAX MOV EAX, edx push ebx mov ebx, 10 .while esi> 0 mul ebx dec esi .endw pop ebx add Result, eax pop eax inc edi dec eax .endw mov eax, Result ret String2Dword endp compareFunc proc uses edi lParam1: DWORD, lParam2: DWORD , SortType: DWORD LOCAL buffer [256]: BYTE LOCAL buffer1 [256]: BYTE LOCAL lvi: LV_ITEM mov lvi.imask, LVIF_TEXT lea eax, buffer mov lvi.pszText, eax mov lvi.cchTextMax, 256 .if SortType ==
1 mov lvi.iSubItem, 1 invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi invoke String2Dword, addr buffer mov edi, eax invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi invoke String2Dword, addr buffer sub edi, eax mov eax, edi .elseif SortType == 2 mov lvi.iSubItem, 1 invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi invoke String2Dword, addr buffer mov edi, eax invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi invoke String2Dword, addr buffer sub eax, edi .elseif SortType == 3 mov lvi.iSubItem, 0 invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi invoke lstrcpy, addr buffer1, addr buffer invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi invoke lstrcmpi, addr buffer1 , addr buffer .else mov lvi.iSubItem, 0 invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi invoke lstrcpy, addr buffer1, addr buffer invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi invoke lstrcmpi, addr buffer, addr buffer1 .endif ret CompareFunc endp UpdatelParam proc uses edi LOCAL lvi: LV_ITEM invoke SendMessage, hList, LVM_GETITEMCOUNT, 0,0 mov edi, eax mov lvi.imask, LVIF_PARAM mov lvi.iSubItem, 0 mov lvi. IIITEM, 0 .While Edi>
0 push lvi.iItem pop lvi.lParam invoke SendMessage, hList, LVM_SETITEM, 0, addr lvi inc lvi.iItem dec edi .endw ret UpdatelParam endp ShowCurrentFocus proc LOCAL lvi: LV_ITEM LOCAL buffer [256]: BYTE invoke SendMessage, hList, LVM_GETNEXTITEM , -1, LVNI_FOCUSED mov lvi.iItem, eax mov lvi.iSubItem, 0 mov lvi.imask, LVIF_TEXT lea eax, buffer mov lvi.pszText, eax mov lvi.cchTextMax, 256 invoke SendMessage, hList, LVM_GETITEM, 0, addr lvi invoke MessageBox, 0, addr buffer, addr AppName, MB_OK ret ShowCurrentFocus endp WndProc proc hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM .if uMsg == WM_CREATE invoke CreateWindowEx, NULL, addr ListViewClassName, NULL, LVS_REPORT WS_CHILD WS_VISIBLE, 0,0,0,0, hWnd, NULL, hInstance, NULL mov hList, eax invoke InsertColumn invoke FillFileInfo RGB 255,255,255 invoke SendMessage, hList, LVM_SETTEXTCOLOR, 0, eax RGB 0,0,0 invoke SendMessage, hList, LVM_SETBKCOLOR, 0, EAX RGB 0, 0, 0 invoke SendMessage, hList, LVM_SETTEXTBKCOLOR, 0, eax invoke GetMenu, hWnd mov hMenu, eax invoke CheckMenuRadioItem, hMenu, IDM_ICON, IDM_LIST, IDM_REPORT, MF_CHECKED .elseif uMsg == WM_COMMAND .if lParam == 0 invoke GetWindowLong, hList, GWL_STYLE and eax , not LVS_TYPEMASK mov edx, wParam and edx, 0FFFFh push edx or eax, edx invoke SetWindowLong, hList, GWL_STYLE, eax pop edx invoke CheckMenuRadioItem, hMenu, IDM_ICON, IDM_LIST, edx, MF_CHECKED .endif .elseif uMsg == WM_NOTIFY push edi mov EDI, LPARAM Assume EDI: PTR NMHDR MOV EAX, [EDI] .hwndfrom .if Eax == Hlist .IF [EDI] .code ==
LVN_COLUMNCLICK assume edi: ptr NM_LISTVIEW .if [edi] .iSubItem == 1 .if SizeSortOrder == 0 || SizeSortOrder == 2 invoke SendMessage, hList, LVM_SORTITEMS, 1, addr CompareFunc invoke UpdatelParam mov SizeSortOrder, 1 .else invoke SendMessage, hList, LVM_SORTITEMS, 2, addr compareFunc invoke UpdatelParam mov SizeSortOrder, 2 .endif .else .if FileNameSortOrder == 0 || FileNameSortOrder == 4 invoke SendMessage, hList, LVM_SORTITEMS, 3, addr compareFunc invoke UpdatelParam mov FileNameSortOrder, 3 .else invoke SendMessage, hList, LVM_SORTITEMS, 4, addr compareFunc invoke UpdatelParam mov FileNameSortOrder, 4 .endif .endif assume edi: ptr NMHDR .elseif [edi] .code == NM_DBLCLK invoke ShowCurrentFocus .endif .endif pop edi .elseif uMsg == WM_SIZE mov EAX, LP aram mov edx, eax and eax, 0ffffh shr edx, 16 invoke MoveWindow, hList, 0, 0, eax, edx, TRUE .elseif uMsg == WM_DESTROY invoke PostQuitMessage, NULL .else invoke DefWindowProc, hWnd, uMsg, wParam, lParam ret .endif xor Eax, EAX RET WNDPROC ENDS:
........... ..
..................... ..
WE CALL CREATEWINDOWEX, PASSING ITTHE Name of The Window Class "SyslistView32". The default view is the report _ specified by lvs_report style.invoke insertColumn
After The ListView Control Is Created, We INSERT Columns Into IT.
LOCAL LVC: LV_COLUMN MOV LVC.IMASK, LVCF_TEXT LVCF_WIDTH MOV LVC.PSZText, Offset Heading1 MOV LVC.LX, 150 Invoke SendMessage, HLIST, LVM_INSERTCOLUMN, 0, AddR LVC
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 lvc.imask, LVCF_FMT MOV 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 LVC.PSZText, Offset Heading2 MOV LVC.LX, 100 Invoke SendMessage, Hlist, LVM_INSERTCOLUMN, 1, AddR LVC
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.
.
Invoke FillFileinfo
Fillfileinfo Has The Following Code.
FillFileInfo proc uses edi LOCAL finddata: WIN32_FIND_DATA LOCAL FHandle: DWORD invoke FindFirstFile, addr FileNamePattern, addr finddataWe 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).
.................. ..
.IF EAX! = Invalid_Handle_Value Mov Fhandle, EAX XOR EDI, EDI
IF a File Is Found, We Store The Search Handle In a Variable and The Zero Out Edi Which Will Be Used As The Index INTO The Items (Row Number).
.while eax! = 0 Test Finddata.dwfileAttributes, file_attribute_directory .if Zero?
In this tutorial, I do not 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.
Invoke ShowfileInfo, EDI, AddR Finddata Inc Edi .endif Invoke FindnextFile, Fhandle, AddR Finddata .endw
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) .invoke findclose, fhandle .ndif ret pinfileinfo endp
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 Proc Uses Edi Row: DWORD, LPFIND: DWORD LOCAL LVI: LV_Item Local Buffer [20]: BYTE MOV EDI, LPFIND Assume EDI: PTR WIN32_FIND_DATA
Store The Address of Win32_Find_Data Structure In EDI.
MOV LVI.IMASK, LVIF_TEXT LVIF_PARAM PUSH ROW POP LVI.IITEM MOV LVI.ISUBITEM, 0
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).
Lea Eax, [EDI] .cfilename Mov Lvi.psztext, Eax Push Row Pop 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.
Invoke SendMessage, HLIST, LVM_INSERTITEM, 0, AddR LVI
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 lvi.imask, LVIF_TEXT inc lvi.iSubItem invoke wsprintf, addr buffer, addr template, [edi] .nFileSizeLow lea eax, buffer mov lvi.pszText, eaxWe 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 .
Invoke SendMessage, Hlist, LVM_SETITEM, 0, ADDR LVI Assute Edi: Nothing Ret ShowfileInfo Endp
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.
...................
RGB 255,255,255 invoke SendMessage, hList, LVM_SETTEXTCOLOR, 0, eax RGB 0,0,0 invoke SendMessage, hList, LVM_SETBKCOLOR, 0, eax RGB 0,0,0 invoke SendMessage, 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.
invoke GetMenu, hWnd mov hMenu, eax invoke CheckMenuRadioItem, hMenu, IDM_ICON, IDM_LIST, IDM_REPORT, MF_CHECKEDWe 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.
.ELSEIF UMSG == WM_SIZE MOV EAX, LPARAM MOV EDX, EAX AND Eax, 0fffh SHR EDX, 16 Invoke MoveWindow, 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.
.................................
.ELSEIF uMSG == WM_COMMAND .IF LPARAM == 0 Invoke getWindowlong, Hlist, GWL_Style and Eax, Not 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.In designing the menu, I cheat a little. I use the view style constants as The menu IDs.
IDM_ICON EQU LVS_ICON IDM_SMALLICON EQU LVS_SMALLICON IDM_LIST EQU LVS_LIST IDM_REPORT EQU LVS_REPORT
THUS WHEN THEENT WINDOW Receives WM_Command Message, The Desired View Style Is in The Low Word of WParam as The Menu ID.
Mov Edx, WParam and EDX, 0FFFH
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 style (minus the current view style) of the listview control.
Invoke SetWindowlong, Hlist, GWL_Style, EAX
And set the new styles with setwindowl.
Pop Edx Invoke CHECKMENURADIOIOITEM, HMENU, IDM_ICON, IDM_LIST, EDX, MF_CHECKED .enDiF
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.
............................ .Net.
.ELSEIF UMSG == WM_Notify Push EDI MOV EDI, LPARAM Assume EDI: PTR NMHDR MOV EAX, [EDI] .hwndfrom .if Eax == 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 [EDI] .code == lvn_columnclick assume edi: PTR 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.
.IF [EDI] .isubitem == 1 .IF SIZESORTORDER == 0 || Sizesortorder == 2
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 / subsystems in the column area not sorted before, or sorted descending, we set the sorting order to ask.
Invoke SendMessage, Hlist, LVM_SORTITEMS, 1, AddR 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 ....................
CompareFunc proc uses edi lParam1: DWORD, lParam2: DWORD, SortType: DWORD LOCAL buffer [256]: BYTE LOCAL buffer1 [256]: BYTE LOCAL lvi: LV_ITEM mov lvi.imask, LVIF_TEXT lea eax, buffer mov lvi.pszText, eax mov lvi.cchTextMax, 256In 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 SortType == 1 MOV LVI.ISubitem, 1 Invoke SendMessage, Hlist, LVM_GetItemText, LParam1, Addr LVI
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 TOTVIEW Control to Obtain The Label of The SubItem.
Invoke String2dword, AddR Buffer 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 ED.WE Store IT IT IN EDI for Comparison Later.
Invoke SendMessage, Hlist, LVM_GetItemte, LParam2, Addr Lvi Invoke String2dword, Addr Buff EDI, EAX MOV EAX, EDI
Do Likewise with the value in lparam2. When we would the Sizes of the Two Files, We can The Comparison Function is as follows Follows:
If the first item should precede the other, you must return a negative value in eax If the second item should precede the first one, you must return a positive value in eax If both items are equal, you must return zero in eax.In this Case, We want to sort the item.................. ..
.elseif SortType == 3 mov lvi.iSubItem, 0 invoke SendMessage, hList, LVM_GETITEMTEXT, lParam1, addr lvi invoke lstrcpy, addr buffer1, addr buffer invoke SendMessage, hList, LVM_GETITEMTEXT, lParam2, addr lvi invoke lstrcmpi, addr buffer1, addr buffer
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.
................... ..
Invoke UpdatelParam Mov Sizesortorder, 1
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 .
.ELSEIF [EDI] .code == nm_dblclk invoke showcurrentfocus .endifu
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 Proc Lovi: LV_Item Local Buffer [256]: Byte Invoke SendMessage, HLIST, LVM_GETNEXTITEM, -1, LVNI_FOCUSED
How do we know? When an item is copy or double-click, ITS State is set to "focused". Even if Many Items Are Hilited (SELECTED), ONLY ONE. THEM HAS GOT The FOCUS. 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 LVI.IITEM, EAX MOV LVI.ISUBITEM, 0 MOV LVI.IMASUBITEM, 0 MOV LVI.IMASUBITEM, 0 MOV LVI.IMASK, LVIF_TEXT Lea Eax, Buffer Mov Lvi.psztext, Eax Mov Lvi.cchtextmax, 256 Invoke SendMessage, Hlist, Lvm_GetItem, 0, AddR LVI
Weimed to obtain the label by sending lvm_getitym message to the listview control.
Invoke Messagebox, 0, AddR Buffer, Addr Appname, MB_OK
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.