列表檢視控制元件(轉)

heying1229發表於2007-07-28
列表檢視控制元件:

  本課中我們將學習如何建立和使用列表檢視控制元件。
理論:
列表檢視控制元件和樹型檢視、豐富文字編輯控制元件一樣是通用控制元件的一種。可能您都已經知道了列表檢視控制元件,只不過是不知道它的確切名字而已。列表檢視控制元件可以用來很好地顯示專案。在這方面它和列表框相同,只不過它的效能更強。
有兩種方法建立一個列表檢視控制元件。第一種也是最簡單的方法是:用資源編輯器來建立它。用該種方法只是不要忘記在您的程式碼(的任何位置處)加入對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 l:LV_COLUMN
mov l.imask,LVCF_TEXT+LVCF_WIDTH
mov l.pszText,offset Heading1
mov l.lx,150
invoke SendMessage,hList, LVM_INSERTCOLUMN,0,addr l

上面的程式碼段顯示了該過程。當傳送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 masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
include masm32includecomctl32.inc
includelib masm32libcomctl32.lib
includelib masm32libuser32.lib
includelib masm32libkernel32.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 l:LV_COLUMN

mov l.imask,LVCF_TEXT+LVCF_WIDTH
mov l.pszText,offset Heading1
mov l.lx,150
invoke SendMessage,hList, LVM_INSERTCOLUMN, 0, addr l
or l.imask,LVCF_FMT
mov l.fmt,LVCFMT_RIGHT
mov l.pszText,offset Heading2
mov l.lx,100
invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr l
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 l:LV_COLUMN

mov l.imask,LVCF_TEXT+LVCF_WIDTH
mov l.pszText,offset Heading1
mov l.lx,150
invoke SendMessage,hList, LVM_INSERTCOLUMN, 0, addr l

我們指定第一列的寬度和列的標題條,為了在該列中顯示檔案的名稱,我們需要在LV_COLUMN 型結構體變數的成員變數iMask中設定標誌位LVCF_TEXT 或 LVCF_WIDTH。我們設定pszText為列標題條文字字串的值,lx設定為列的寬度(以畫素點為單位)。然後我們傳送LVM_INSERTCOLUMN訊息給列表檢視控制元件,並把該結構體變數傳遞給它。

or l.imask,LVCF_FMT
mov l.fmt,LVCFMT_RIGHT

插入完第一列後,我們再插入第二列,單擊該列的標題條可以按檔案的大小排序。因為我們需要右對齊文字,我們需要在成員變數fmt中指定標誌位LVCFMT_RIGHT。我們還必須在成員變數iMask中除了標誌位LVCF_TEXT 和 LVCF_WIDTH外還需要指定標誌位LVCF_FMT。

mov l.pszText,offset Heading2
mov l.lx,100
invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr l

剩餘的程式碼比較簡單。在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

最後我們在一個訊息框中顯示標籤。

如果想在列表檢視控制元件中顯示圖示,您可以閱讀關於樹型檢視控制元件的課程。它們的步驟基本上是一樣的。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928788/,如需轉載,請註明出處,否則將追究法律責任。

相關文章