分享
 
 
 

WIN32汇编: 31.列表视图控件

王朝system·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第三十一课 列表视图控件

本课中我们将学习如何创建和使用列表视图控件。

理论:

列表视图控件和树型视图、丰富文本编辑控件一样是通用控件的一种。可能您都已经知道了列表视图控件,只不过是不知道它的确切名字而已。列表视图控件可以用来很好地显示项目。在这方面它和列表框相同,只不过它的性能更强。

有两种方法创建一个列表视图控件。第一种也是最简单的方法是:用资源编辑器来创建它。用该种方法只是不要忘记在您的代码(的任何位置处)加入对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

最后我们在一个消息框中显示标签。

如果想在列表视图控件中显示图标,您可以阅读关于树型视图控件的课程。它们的步骤基本上是一样的。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有