本課中我們將學習如何創建和使用列表視圖控件。
理論:
列表視圖控件和樹型視圖、豐富文本編輯控件一樣是通用控件的一種。可能您都已經知道了列表視圖控件,只不過是不知道它的確切名字而已。列表視圖控件可以用來很好地顯示項目。在這方面它和列表框相同,只不過它的性能更強。
有兩種方法創建一個列表視圖控件。第一種也是最簡單的方法是:用資源編輯器來創建它。用該種方法只是不要忘記在您的代碼(的任何位置處)加入對InitCommonControls函數的調用(記得嗎,調用該函數只是為了隱式地加載包含通用控件的DLL)。另一種方法是調用CreateWindowEx函數,這裡您必須指定合適的類名,譬如:SysListView32,WC_LISTVIEW不是正確的類名
在列表視圖種有四種方法來顯示數據:大圖標,小圖標,列表和報告方式。這些方法和在資源管理器種選擇View->Large Icons,Small Icons , List 和 Details 相對應。各種不同的顯示方式只是顯示了不同的外觀而已。譬如,您可能有許多的數據,只是並不想全部顯示。報告方式提供的消息最完全,其它的方式則要少得多。在剛創建一個列表視圖時您可以選擇一種初始顯示方法,隨後您可以調用SetWinodwLong函數並設置GWL_STYLE標志位來改變顯示方式
既然我們已經知道了如何創建列表控件,接下來我們學習如何使用它們。我們將主要集中在報告方式的顯示上,因為該種方式演示了最多的列表控制的特性。使用列表控制的步驟如下:
調用CreateWindowEx函數來創建一個列表控件,指定它的類名為SysListView32。您還可以在此處指定控件初次顯示時的方式。
創建和初始化用在列表控件中顯示項目的圖象列表(如果存在)。
向列表控件中插入列,如果顯示的方式是報告方式這一步是必須的。
向控件中插入項目和自項目。
列:
在報告方式中,有不止一個列。您可以把放入到列表控件中的數據看作是一張表單:這時數據是按行列排列的。在控件中至少有一列。在其它的顯示方式中則無所謂,因為這些顯示方式有僅有一列。
加入列要通過向列表控件發送LVM_INSERTCOLUMN消息來實現。
LVM_INSERTCOLUMN
wParam = iCol
lParam =指向LV_COLUMN型結構體變量的指針
iCol 列數,從0開始編號。
LV_COLUMN 包含了將插入的列的信息。它的定義如下:
LV_COLUMN STRUCT
imask dd ?
fmt dd ?
lx dd ?
pszText dd ?
cchTextMax dd ?
iSubItem dd ?
iImage dd ?
iOrder dd ?
LV_COLUMN ENDS
Field name Meanings
imask 一組標志位,它指示了該結構體中的那些成員變量是有效的。該結構體中的成員變量並不是同時有效的。在某些時候,可能只有某些成員變量是有效的。結構體可以用來輸入和輸出。這樣讓WINDOWS知道那些成員變量是有效的是非常重要的。可能的標志有:
LVCF_FMT = fmt有效
LVCF_SUBITEM = iSubItem有效
LVCF_TEXT = pszText有效.
LVCF_WIDTH = lx有效
您可以一次使用幾個標志。譬如,如果您向指定列的文本標簽(列名),您必須在pszText成員變量中提供列名,然後指定標志LVCF_TEXT告訴WINDOWS成員變量pszText中的值是有效的,否則WINDOWS將忽略掉pszText中的值。
fmt 指定了項目/子項目的對齊方式。可能的值有:
LVCFMT_CENTER = 文本居中
LVCFMT_LEFT = 文本左對齊
LVCFMT_RIGHT = 文本右對齊
lx lx 是列的寬度(以像素點為單位)。以後您可以發送消息LVM_SETCOLUMNWIDTH來改變列的寬度。
pszText 如果用來設定列的屬性時,該成員變量為指向列名的指針。如果是查詢列名,該成員變量指向一個足夠大的緩沖區,用來接收返回的列名,這是您必須在成員cchTextMax中指定緩沖區的大小。如果是設定列名時,可以忽略該變量,因為該指針指向的是一個ASCII碼的字符串,而WINDOWS可以解析出ASCII串的長度。
cchTextMax cchTextMax 以字節計的上面一個成員變量指向的緩沖區的小。該成員變量只在您查詢列的屬性時使用。如果是設定列的屬性,那該變量將被忽略。
iSubItem 指定和該列相連的子項目的索引號。該成員變量的值用來標識和列相連系的子項目。該列的使用最好地說明了如何把列號和子項目相連。要查詢列的屬性時可以發送LVM_GETCOLUMN消息,並在成員變量imask中指定LVCF_SUBITEM標志,列表控件將在iSubItem中返回插入時設定的iSubItem值。為了使用該辦法,您需要在該成員變量中放入正確的值。
iImage and iOrder 為了和IE3.0以上版本兼容。目前我沒有這方面的資料。
在列表視圖控件創建後,您必須至少向其中插入一列。當然如果不打算使用報告方式顯示,那倒是沒有必要插入列。為了插入列,您需要定義一個LV_COLUMN型的結構體變量,給其成員變量賦上正確的值,指定列號,然後向列表視圖控件發送LVM_INSERTCOLUMN消息並把該結構體變量的值傳過去。
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
上面的代碼段顯示了該過程。當發送LVM_INSERTCOLUMN消息時,他指定了列的標題條文本和它的寬度。
項目和子項目
項目是列表視圖中主要的內容。除報告方式顯示的外,在列表視圖您只能看到項目。子項目是項目的詳細信息。一個項目可能有不止一個相關的子項目。舉個例子,譬如項目是文件名,那其相關的子項目可能有文件屬性、大小、創建日期等。在報告方式的視圖中,最左邊一列是項目,其它列是子項目。從數據庫記錄的角度看,項目類似主鍵,子項目類似記錄。
至少您的列表視圖需要一些項目:子項目是可選的。如果您想要給用戶提供更多的信息,可以把子項目和項目相連,然後放到列表視圖中以報告的方式顯示。
您可以通過向列表視圖發送LVM_INSERTITEM消息來向其中添加項目,這時還需要把一個指向LV_ITEM型的結構體的變量的指針放到lParam一同傳給列表視圖。LV_ITEM的定義如下:
LV_ITEM STRUCT
imask dd ?
iItem dd ?
iSubItem dd ?
state dd ?
stateMask dd ?
pszText dd ?
cchTextMax dd ?
iImage dd ?
lParam dd ?
iIndent dd ?
LV_ITEM ENDS
Field name Meanings
imask 一組標志位標明該結構體中那些成員變量中的值有效。它的意義和上面我們提到的LV_COLUMN型結構體中向對應的成員變量基本相同。更詳細的信息,可以查詢WIN32 API 手冊。
iItem 該結構體代表的項目的索引號。索引號是從0開始編號的。該值和表單的“行”類似。
iSubItem 和上一個成員變量指定的項目相連的子項目的索引號。您可以把它當作表單的“列”。譬如您想要把一個項目插入到新創建的列表視圖控件,iItem的值應為0(因為該項目是第一個項目),iSubItem的值也應當為0(我們想把該項目插到第一列)。如果你想指定一個子項目和該項目相連,iItem中應該是您想要相連的項目的索引號,iSubItem的值應當是大於0的值,具體的值取決於您想把該子項目插在那一列。如果你的列表視圖控件一共有4列的化,第一列包含了項目,其余3列是留給子項目的。如果您想把子項目插在第四列,應當指定該值為3。
state 該成員變量包含的標志位反應了項目的狀態。狀態的改變可能是由用戶的操作引起的或是程序改變的。這些狀態包括:是否有焦點/高亮度顯示/被選中(由於被剪切)/被選中等。另外還包括,以1為基數的索引用來代表是否處使用重疊/狀態圖標。
stateMask 由於上面的成員變量包含狀態標志位、重疊的位圖索引號、和狀態位圖的索引號,我們需要告訴WINDOWS我們到底需要設定或查詢那一個值。該成員變量就是用來做這項工作的。
pszText 當我們想設定項目的屬性時,它包含項目名稱的ASCII碼的字符串的地址。當查詢項目的屬性時,該成員變量將用來接收查詢返回的項目的名稱。
cchTextMax 僅當您用來查詢項目的屬性時才需要使用該值,這時它包含上一個成員變量的大小。
iImage 圖標在列表視圖中的圖象鏈表中的索引號。
lParam 用戶定義的值,當您給項目排序時使用。當您告訴列表視圖對項目排序時,列表視圖將成對地比較項目。 它將會把兩個項目的lParam的值傳給您,這樣您就可以進行比較先列出那一個了。如果您現在還不太明白的話,沒有系,我們稍後還要講關於排序的問題。
現在讓我們來總結想列表控件中插入項目/子項目的步驟:
定義一個LV_ITEM型的結構體變量。
給該變量賦給合適的值
如果要插入一個項目,就向列表視圖控件發送LVM_INSERTITEM值。 如果要插入一個子項目,發送LVM_SETITEM。如果您不明白項目和子項目之間的關系的話,可能會有一些疑惑。子項目僅是項目的屬性而已,也就是說您可以插入一個項目但是不能插入一個子項目。所以添加一個子項目十只能發送LVM_SETITEM消息而不能發送LVM_INSERTITEM消息。
列表視圖控件的消息/通知
既然您知道了如何創建和往其中添加內容,下一步就是如何和它通訊。列表視圖控件和它的父窗口之間的通訊是通過消息/通知來進行的。父窗口通過發送消息來控制列表視圖控件,列表視圖控件通過發送WM_NOTIFY消息來通知它的父窗口。這一點和其它的通用控件沒有什麼不同。
排序項目/子項目
您可以在調用CreateWindowEx函數時指定LVS_SORTASCENDING 或 LVS_SORTDESCENDING風格來指定缺省的排序方式。這兩種風格僅僅排序項目的名稱。如果想要排序項目的其它屬性,您可以通過發送LVM_SORTITEMS消息來完成
LVM_SORTITEMS
wParam = lParamSort
lParam = pCompareFunction
lParamSort 用戶定義的值,該值將傳遞給用來比較的函數。
pCompareFunction 用戶定義的用來比較排序的函數的地址。該函數的原型如下:
CompareFunc proto lParam1:DWORD, lParam2:DWORD, lParamSort:DWORD
lParam1 和 lParam2 是 LV_ITEM型的結構體中的成員變量lParam的值。
lParamSort 是發送LVM_SORTITEMS消息時參數wParam中的值
當列表視圖控件接收到LVM_SORTITEMS消息時,當需要比較項目時它會調用在lParam中指定的比較函數。比較函數將決定那一個項目排在前面。方法很簡單:如果函數返回一個負值,由(lParam代表的)第一個項目排在前,如果返回正值,第二個項目排在前。如果相等,必須返回0 。
真正使得該方法能夠運行的是LV_ITEM型結構體中的成員變量lParam值。當您需要排序時(譬如當您點擊列的標題條時),您需要考慮好排序方案。在本例中,我們把項目的索引放到該成員變量中,這樣我們可以通過發送LVM_GETITEM消息來得到項目的其它信息。注意:當項目重排序後,它們的索引也就變了。所以當重排序後,我需要在lParam參數中反應出新的索引。如果您想在用戶點擊列的標題條時重新排序,您需要在您的窗口過程函數中處理LVN_COLUMNCLICK通知消息。LVN_COLUMNCLICK消息是隨同WM_NOTIFY消息一起發送的。
例子:
該例子創建了一個列表視圖控件,並在其中顯示了當前文件夾中的文件大小和文件名。缺省的視圖是報告方式的,如果您點擊列標題條,標題將按升/降序重新排列。您可以通過菜單選擇不同的顯示方式(大圖標、小圖標等)。當您雙擊一個項目時,項目的名稱將顯示在一個對話框中。
.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
SizeSortOrder 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 ExitProcess,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 ecx edi edx esi String:DWORD
LOCAL Result: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,eax
dec 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.iItem,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,lParam
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 endp
end start
分析:
當主窗口創建後要做的第一件事是創建一個列表視圖控件應用程序。
.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
我們調用CreateWindowEx來創建窗口,並把窗口類的名稱“SysListView32”傳給它。缺省的顯示方式是報告方式,因為您指定了LVS_REPORT標志作為它的風格。
invoke InsertColumn
創建列表視圖控件後,我們向其中插入列。
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
我們指定第一列的寬度和列的標題條,為了在該列中顯示文件的名稱,我們需要在LV_COLUMN 型結構體變量的成員變量iMask中設定標志位LVCF_TEXT 或 LVCF_WIDTH。我們設定pszText為列標題條文本字符串的值,lx設定為列的寬度(以像素點為單位)。然後我們發送LVM_INSERTCOLUMN消息給列表視圖控件,並把該結構體變量傳遞給它。
or lvc.imask,LVCF_FMT
mov lvc.fmt,LVCFMT_RIGHT
插入完第一列後,我們再插入第二列,單擊該列的標題條可以按文件的大小排序。因為我們需要右對齊文本,我們需要在成員變量fmt中指定標志位LVCFMT_RIGHT。我們還必須在成員變量iMask中除了標志位LVCF_TEXT 和 LVCF_WIDTH外還需要指定標志位LVCF_FMT。
mov lvc.pszText,offset Heading2
mov lvc.lx,100
invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc
剩余的代碼比較簡單。在pszText中放入文本字符串的地址,在lx中放入列的寬度。然後發送消息LVM_INSERTCOLUMN 給列表視圖控件,在參數中同時傳遞列號和結構體變量的地址。
當插入完列後,我們向列表控件中加入項目。
invoke FillFileInfo
FillFileInfo 的代碼如下:
FillFileInfo proc uses edi
LOCAL finddata:WIN32_FIND_DATA
LOCAL FHandle:DWORD
invoke FindFirstFile,addr FileNamePattern,addr finddata
我們調用FindFirstFile來得到第一個符合搜索標准的的文件的信息。FindFirstFile函數的原型如下
FindFirstFile proto pFileName:DWORD, pWin32_Find_Data:DWORD
pFileName 是用來匹配搜索的文件名的地址。該字符串包含了通配符。在我們的例子中是*.*,這樣會搜索當前文件夾中所有的文件。
pWin32_Find_Data 是WIN32_FIND_DATA 型的結構體變量的地址,WIN32_FIND_DATA型的結構體變量將用來保存返回的文件的信息。
如果沒有找到匹配的文件,該函數將在eax中返回INVALID_HANDLE_VALUE 。否則將返回一個搜索句柄,您可以用該句柄在FindNextFile函數中來搜索下一個符合條件的文件。
.if eax!=INVALID_HANDLE_VALUE
mov FHandle,eax
xor edi,edi
如果找到了一個文件,我們在一個變量中保存搜索句柄,並把寄存器edi清零,該寄存器將用作項目的索引號。
.while eax!=0
test finddata.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
.if ZERO?
在本課中,我們將不處理文件夾,所以我們檢查dwFileAttributes成員變量的值是否有FILE_ATTRIBUTE_DIRECTORY 標志,如果有,我們就忽略掉它,然後調用FindNextFile。
invoke ShowFileInfo,edi, addr finddata
inc edi
.endif
invoke FindNextFile,FHandle,addr finddata
.endw
我們調用ShowFileInfo函數包文件的名稱和大小信息加到列表視圖控件中去。然後讓edi寄存器加一來增加項目的行號。最後我們調用FindNextFile函數在當前文件夾中繼續搜索文件一直到該函數返回0為止(這意味著沒有可供搜索的文件了)。
invoke FindClose,FHandle
.endif
ret
FillFileInfo endp
當前文件夾中的文件枚舉完畢後,我們必須關閉搜索句柄。
先在我們看一下ShowFileInfo函數。該函數由兩個參數,一個是項目的索引號(也即行號),另一個是WIN32_FIND_DATA型結構體變量的地址。
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
把WIN32_FIND_DATA 型結構體變量的值放到寄存器edi中。
mov lvi.imask,LVIF_TEXT+LVIF_PARAM
push row
pop lvi.iItem
mov lvi.iSubItem,0
我們將傳遞項目的名稱和lParam的值,所以我們在iMask中放入標志位LVIF_TEXT 和LVIF_PARAM。接下來我們在iItem中放入傳遞進來的行號,另外由於這是主項目我們必須設置iSubItem的值等於0。
lea eax,[edi].cFileName
mov lvi.pszText,eax
push row
pop lvi.lParam
我們現在要把標簽字符串的地址,在這裡也就是WIN32_FIND_DATA 型結構體變量中的文件的名稱放到pszText中。由於我們要完成對項目的排序,所以必須設置lParam的值,我把它設成行號值,這樣我們可以根據索引值來查詢項目。
invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
設置完所有LV_ITEM型變量中的值後,我們發送LVM_INSERTITEM消息給列表視圖控件來把項目插入到其中。
mov lvi.imask,LVIF_TEXT
inc lvi.iSubItem
invoke wsprintf,addr buffer, addr template,[edi].nFileSizeLow
lea eax,buffer
mov lvi.pszText,eax
我們將把子項目插入到第二列。一個子項目只能有一個標簽。這樣我們在iMask中指定LVIF_TEXT標志位。接著我們指定子項目所在的列,本例中我們通過將iSubItem加一使得該值等於1。標簽值是文件的大小,為了轉換成文本我們調用wsprintf函數,然後把文本的地址放到pszText中去。
invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
assume edi:nothing
ret
ShowFileInfo endp
當LV_ITEM型變量中的值設定好之後,我們向列表視圖控件發送LVM_SETITEM消息,並一同把LV_ITEM變量的地址傳過去。注意:發送的消息是LV_ITEM而不是LVM_INSERTITEM,因為我們插入的是子項目,子項目不是真正的項目而是主項目的屬性。所以我們這時是在設定項目的屬性,而不是加入一個項目。
當所有的項目都插入到列表視圖控件後,我們設定它的文本和背景顏色。
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
我們用RGB(R---Red G---Green B---Blue)來把三色轉換並放到eax中。我們通過發送LVM_SETTEXTCOLOR 和 LVM_SETTEXTBKCOLOR 消息來設定文本的前景和背景色。
invoke GetMenu,hWnd
mov hMenu,eax
invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, IDM_REPORT,MF_CHECKED
我們將讓用戶通過菜單來選擇它想要的顯示方式。這樣我們必須先得到菜單的句柄。我了讓用戶跟蹤當前的視圖,我們在菜單中放入一組單選按鈕。我們可以調用CheckMenuRadioItem函數,該函數將把一個單選按鈕放到一個菜單項前。
注意我們創建列表視圖控件時把它的寬度和高度都設成為0。當父窗口改變大小時,它將同時改變大小。這樣我們可以讓列表視圖總是隨著主窗口改變。子我們的例子中,我們讓列表視圖填充整個客戶區。
.elseif uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
and eax,0ffffh
shr edx,16
invoke MoveWindow,hList, 0, 0, eax,edx,TRUE
當父窗口接收到了WM_SIZE消息後,lParam的底字部分包含了客戶區新的寬和高。讓後我們調用MoveWindow來改變列表視圖控件的大小使得它覆蓋整個的客戶區。
當用戶通過菜單選擇了一種選擇方式,我們必須相應地改變列表視圖中的顯示方式。我們調用SetWindowLong函數來設定新的風格。
.elseif uMsg==WM_COMMAND
.if lParam==0
invoke GetWindowLong,hList,GWL_STYLE
and eax,not LVS_TYPEMASK
首先得到當前的風格,然後清除舊的風格。LVS_TYPEMASK 是LVS_ICON+LVS_SMALLICON+LVS_LIST+LVS_REPORT四種風格的集合。這樣當我們用當前的風格“與”“not LVS_TYPEMASK”就等於清除了當前的顯示風格。
在設計菜單時,我們使用了一些小技巧。我們包顯示風格的常數串當作菜單的ID號。
IDM_ICON equ LVS_ICON
IDM_SMALLICON equ LVS_SMALLICON
IDM_LIST equ LVS_LIST
IDM_REPORT equ LVS_REPORT
這樣當父窗口接收到WM_COMMAND消息時,希望顯示的風格值會當成菜單的ID號傳遞過來。
mov edx,wParam
and edx,0FFFFh
在wParam中的低字部分是欲顯示的風格,我們所需要做的只是把高字部分清0。
push edx
or eax,edx
我們把希望顯示的風格加到列表視圖的風格中去(已經去除了舊的風格)。
invoke SetWindowLong,hList,GWL_STYLE,eax
調用SetWindowLong函數來設定新的風格。
pop edx
invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, edx,MF_CHECKED
.endif
我們需要在被選擇的顯示方式前放入單選按鈕。如果要排序,我們必須處理WM_NOTIFY消息。
.elseif uMsg==WM_NOTIFY
push edi
mov edi,lParam
assume edi:ptr NMHDR
mov eax,[edi].hwndFrom
.if eax==hList
當我們接收到了WM_NOTIFY 消息後,lParam包含了指向NMHDR型結構體變量的指針。我們通過把列表視圖控件的值和NMHDR型結構體變量中的hwndFrom成員變量的值比較來判斷,如果相等的話我們就可以確定消息是列表視圖控件發送的。
.if [edi].code==LVN_COLUMNCLICK
assume edi:ptr NM_LISTVIEW
如果通知消息是列表視圖控件發送的,我們檢測該消息是否是LVN_COLUMNCLICK。如果是,它意味著用戶點擊了列標題條。在接收到LVN_COLUMNCLICK消息後,我們假設lParam參數包含NM_LISTVIEW型結構體變量的指針,NM_LISTVIEW型結構體是NMHDR型結構體的擴展。我們需要知道用戶單擊了那一列,在iSubItem中的值即是列號,列的編號是從0開始的。
.if [edi].iSubItem==1
.if SizeSortOrder==0 || SizeSortOrder==2
在這裡iSubItem的值是1,它表示用戶點擊的是第二列,即文件的大小。我們用狀態變量來保持當前的排序順序。0代表不用排序,1代表升序,2代表降序。如果該列中的項目/子項目以前沒有排序或為降序,我們就把它設成升序。
invoke SendMessage,hList,LVM_SORTITEMS,1,addr CompareFunc
我們發送消息LVM_SORTITEMS給列表視圖控件,在wParam中傳遞1,在lParam中傳遞比較函數的參數。注意wParam中的值是用戶定義的,用戶可以按自己的需要來解釋,這裡我們把它用作排序的方法。我們先來看看比較函數:
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
列表視圖控件將傳遞需要比較的兩個項目的lParam(LV_ITEM型結構體變量的成員變量)比較函數。您還記得嗎?我們在lParam中放置了醒目的索引號。這樣我們利用這些索引號查詢列表視圖來得到項目信息。我們需要的消息是項目/子項目的標簽文本。為此我們准備好LV_ITEM 型結構體變量並在iMask中設置標志位LVIF_TEXT ,在pszText中設置緩沖區的地址,在cchTextMax中設置緩沖區的大小。
.if SortType==1
mov lvi.iSubItem,1
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
如果SortType的值為1或2,我們知道點擊了那一列,1代表根據文件的大小按升序排列所有的項目。2的意思相反。這樣我們指定iSubItem為1(代表文件大小列)然後發送LVM_GETITEMTEXT 消息給列表視圖控件來得到在項目的標簽文本串。
invoke String2Dword,addr buffer
mov edi,eax
調用子定義的String2Dword函數來把字符串轉換成一個DWORD值。它將在eax中返回轉換後的值,我們把它保存在edi中以便以後比較用。
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
invoke String2Dword,addr buffer
sub edi,eax
mov eax,edi
對lParam2 中的值做同樣的操作。當我們得到了兩個文件的大小後,就可以比較它們了。比較的規則如下:
如果第一個項目放在前面,在eax中返回一個負值
如果第二個項目放在前面,在eax中返回一個正值
如果相等,在eax中返回0
在我們這裡,我們想按升序排列,所以我們只要簡單地將第二個項目的文件大小減去第一個項目的文件大小,然後返回放在eax中的值。
.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
當用戶點擊文件名字列時,我們必須比較文件的名字。我們先得到文件的名字,然後調用lstrcmpi函數來比較,然後只要簡單返回lstrcmpi的值,因為該函數比較使用的規則和我們的相同。
當項目排序後,我們調用UpdateParam函數來更新所有項目的lParam的值來反應出最新的改變。
invoke UpdatelParam
mov SizeSortOrder,1
該函數簡單地枚舉列表視圖中所有的項目並且把它們lParam更新成項目的索引號。
.elseif [edi].code==NM_DBLCLK
invoke ShowCurrentFocus
.endif
如果用戶雙擊某個項目時,我們將顯示一個消息框,上面有該項目的有關標簽值。我們必須檢查NMHDR 中的code值是否是 NM_DBLCLK。如果是,我們就得到它的標簽值並顯示在一個消息框中。
ShowCurrentFocus proc
LOCAL lvi:LV_ITEM
LOCAL buffer[256]:BYTE
invoke SendMessage,hList,LVM_GETNEXTITEM,-1, LVNI_FOCUSED
我們是增麼怎麼知道某個項目被雙擊的呢?當單擊或雙擊某個項目時,它的狀態被設成“焦點”。即使有多個項目被選中,也僅有一個項目有焦點。我們的工作就是去找到那個有焦點的項目。我們發送LVM_GETNEXTITEM消息給列表視圖控件,在lParam中指定期望的狀態。如果wParam中時-1的話,表示要搜索所有的項目。有焦點的項目第索引號在eax中返回。
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
發送LVM_GETITEM消息來得到標簽。
invoke MessageBox,0, addr buffer,addr AppName,MB_OK
最後我們在一個消息框中顯示標簽。
如果想在列表視圖控件中顯示圖標,您可以閱讀關於樹型視圖控件的課程。它們的步驟基本上是一樣的。