windows sdk编程系列文章 ----列表视图控件2008-04-29 12:14本课中我们将学习如何创建和使用列表视图控件理论:列表视图控件和树型视图、丰富文本编辑控件一样是通用控件的一种可能您都已经知道了列表视图控件,只不过是不知道它的确切名字而已列表视图控件可以用来很好地显示项目在这方面它和列表框相同,只不过它的性能更强有两种方法创建一个列表视图控件第一种也是最简单的方法是:用资源编辑器来创建它用该种方法只是不要忘记在您的代码(的任何位置处)加入对InitCommonControls函数的调用(记得吗,调用该函数只是为了隐式地加载包含通用控件的DLL)另一种方法是调用CreateWindowEx函数,这里您必须指定合适的类名,譬如:SysListView32,WC_LISTVIEW不是正确的类名在列表视图种有四种方法来显示数据:大图标,小图标,列表和报告方式这些方法和在资源管理器种选择View->Large Icons,Small Icons , List 和 Details 相对应各种不同的显示方式只是显示了不同的外观而已譬如,您可能有许多的数据,只是并不想全部显示报告方式提供的消息最完全,其它的方式则要少得多。
在刚创建一个列表视图时您可以选择一种初始显示方法,随后您可以调用SetWinodwLong函数并设置GWL_STYLE标志位来改变显示方式既然我们已经知道了如何创建列表控件,接下来我们学习如何使用它们我们将主要集中在报告方式的显示上,因为该种方式演示了最多的列表控制的特性使用列表控制的步骤如下:1. 调用CreateWindowEx函数来创建一个列表控件,指定它的类名为SysListView32您还可以在此处指定控件初次显示时的方式 2. 创建和初始化用在列表控件中显示项目的图象列表(如果存在) 3. 向列表控件中插入列,如果显示的方式是报告方式这一步是必须的 4. 向控件中插入项目和自项目 列:在报告方式中,有不止一个列您可以把放入到列表控件中的数据看作是一张表单:这时数据是按行列排列的在控件中至少有一列在其它的显示方式中则无所谓,因为这些显示方式有仅有一列加入列要通过向列表控件发送LVM_INSERTCOLUMN消息来实现LVM_INSERTCOLUMNwParam = iCollParam =指向LV_COLUMN型结构体变量的指针iCol 列数,从0开始编号LV_COLUMN 包含了将插入的列的信息。
它的定义如下:typedef struct _LVCOLUMN { UINT mask; int fmt; int cx; LPTSTR pszText; int cchTextMax; int iSubItem; #if (_WIN32_IE >= 0x0300) int iImage; int iOrder;#endif} LVCOLUMN, *LPLVCOLUMN;Field name Meaningsmask 一组标志位,它指示了该结构体中的那些成员变量是有效的该结构体中的成员变量并不是同时有效的在某些时候,可能只有某些成员变量是有效的结构体可以用来输入和输出这样让WINDOWS知道那些成员变量是有效的是非常重要的可能的标志有:LVCF_FMT = fmt有效LVCF_SUBITEM = iSubItem有效 LVCF_TEXT = pszText有效. LVCF_WIDTH = cx有效您可以一次使用几个标志譬如,如果您向指定列的文本标签(列名),您必须在pszText成员变量中提供列名,然后指定标志LVCF_TEXT告诉WINDOWS成员变量pszText中的值是有效的,否则WINDOWS将忽略掉pszText中的值。
fmt 指定了项目/子项目的对齐方式可能的值有:LVCFMT_CENTER = 文本居中 LVCFMT_LEFT = 文本左对齐 LVCFMT_RIGHT = 文本右对齐cx cx 是列的宽度(以像素点为单位)以后您可以发送消息LVM_SETCOLUMNWIDTH来改变列的宽度pszText 如果用来设定列的属性时,该成员变量为指向列名的指针如果是查询列名,该成员变量指向一个足够大的缓冲区,用来接收返回的列名,这是您必须在成员cchTextMax中指定缓冲区的大小如果是设定列名时,可以忽略该变量,因为该指针指向的是一个ASCII码的字符串,而WINDOWS可以解析出ASCII串的长度cchTextMax cchTextMax 以字节计的上面一个成员变量指向的缓冲区的小该成员变量只在您查询列的属性时使用如果是设定列的属性,那该变量将被忽略iSubItem 指定和该列相连的子项目的索引号该成员变量的值用来标识和列相连系的子项目该列的使用最好地说明了如何把列号和子项目相连要查询列的属性时可以发送LVM_GETCOLUMN消息,并在成员变量mask中指定LVCF_SUBITEM标志,列表控件将在iSubItem中返回插入时设定的iSubItem值。
为了使用该办法,您需要在该成员变量中放入正确的值iImage and iOrder 为了和IE3.0以上版本兼容目前我没有这方面的资料在列表视图控件创建后,您必须至少向其中插入一列当然如果不打算使用报告方式显示,那倒是没有必要插入列为了插入列,您需要定义一个LV_COLUMN型的结构体变量,给其成员变量赋上正确的值,指定列号,然后向列表视图控件发送LVM_INSERTCOLUMN消息并把该结构体变量的值传过去 LV_COLUMN lvc; lvc.imask = LVCF_TEXT| LVCF_WIDTH ; lvc.pszText = Heading1 ; lvc.lx = 150 ; SendMessage(hList, LVM_INSERTCOLUMN,0,&lvc);上面的代码段显示了该过程当发送LVM_INSERTCOLUMN消息时,他指定了列的标题条文本和它的宽度项目和子项目项目是列表视图中主要的内容除报告方式显示的外,在列表视图您只能看到项目子项目是项目的详细信息一个项目可能有不止一个相关的子项目举个例子,譬如项目是文件名,那其相关的子项目可能有文件属性、大小、创建日期等。
在报告方式的视图中,最左边一列是项目,其它列是子项目从数据库记录的角度看,项目类似主键,子项目类似记录至少您的列表视图需要一些项目:子项目是可选的如果您想要给用户提供更多的信息,可以把子项目和项目相连,然后放到列表视图中以报告的方式显示您可以通过向列表视图发送LVM_INSERTITEM消息来向其中添加项目,这时还需要把一个指向LV_ITEM型的结构体的变量的指针放到lParam一同传给列表视图LV_ITEM的定义如下:typedef struct _LVITEM { UINT mask; int iItem; int iSubItem; UINT state; UINT stateMask; LPTSTR pszText; int cchTextMax; int iImage; LPARAM lParam; #if (_WIN32_IE >= 0x0300) int iIndent; #endif#if (_WIN32_IE >= 0x560) int iGroupId; UINT cColumns; // tile view columns PUINT puColumns; #endif} LVITEM, *LPLVITEM;Field name Meaningsmask 一组标志位标明该结构体中那些成员变量中的值有效。
它的意义和上面我们提到的LV_COLUMN型结构体中向对应的成员变量基本相同更详细的信息,可以查询WIN32 API 手册iItem 该结构体代表的项目的索引号索引号是从0开始编号的该值和表单的“行”类似iSubItem 和上一个成员变量指定的项目相连的子项目的索引号您可以把它当作表单的“列”譬如您想要把一个项目插入到新创建的列表视图控件,iItem的值应为0(因为该项目是第一个项目),iSubItem的值也应当为0(我们想把该项目插到第一列)如果你想指定一个子项目和该项目相连,iItem中应该是您想要相连的项目的索引号,iSubItem的值应当是大于0的值,具体的值取决于您想把该子项目插在那一列如果你的列表视图控件一共有4列的化,第一列包含了项目,其余3列是留给子项目的如果您想把子项目插在第四列,应当指定该值为3state 该成员变量包含的标志位反应了项目的状态状态的改变可能是由用户的操作引起的或是程序改变的这些状态包括:是否有焦点/高亮度显示/被选中(由于被剪切)/被选中等另外还包括,以1为基数的索引用来代表是否处使用重叠/状态图标stateMask 由于上面的成员变量包含状态标志位、重叠的位图索引号、和状态位图的索引号,我们需要告诉WINDOWS我们到底需要设定或查询那一个值。
该成员变量就是用来做这项工作的pszText 当我们想设定项目的属性时,它包含项目名称的ASCII码的字符串的地址当查询项目的属性时,该成员变量将用来接收查询返回的项目的名称cchTextMax 仅当您用来查询项目的属性时才需要使用该值,这时它包含上一个成员变量的大小iImage 图标在列表视图中的图象链表中的索引号lParam 用户定义的值,当您给项目排序时使用当您告诉列表视图对项目排序时,列表视图将成对地比较项目 它将会把两个项目的lParam的值传给您,这样您就可以进行比较先列出那一个了如果您现在还不太明白的话,没有系,我们稍后还要讲关于排序的问题现在让我们来总结想列表控件中插入项目/子项目的步骤:1. 定义一个LV_ITEM型的结构体变量 2. 给该变量赋给合适的值 3. 如果要插入一个项目,就向列表视图控件发送LVM_INSERTITEM值 如果要插入一个子项目,发送LVM_SETITEM如果您不明白项目和子项目之间的关系的话,可能会有一些疑惑子项目仅是项目的属性而已,也就是说您可以插入一个项目但是不能插入一个子项目所以添加一个子项目十只能发送LVM_SETITEM消息而不能发送LVM_INSERTITEM消息。
列表视图控件的消息/通知既然您知道了如何创建和往其中添加内容,下一步就是如何和它通讯列表视图控件和它的父窗口之间的通讯是通过消息/通知来进行的父窗口通过发送消息来控制列表视图控件,列表视图控件通过发送WM_NOTIFY消息来通知它的父窗口这一点和其它的通用控件没有什么不同排序项目/子项目您可以在调用CreateWindowEx函数时指定LVS_SORTASCENDING 或 LVS_SORTDESCENDING风格来指定缺省的排序方式这两种风格仅仅排序项目的名称如果想要排序项目的其它属性,您可以通过发送LVM_SORTITEMS消息来完成LVM_SORTITEMSwParam = lParamSortlParam = pCompareFunctionlParamSort 用户定义的值,该值将传递给用来比较的函数pCompareFunction 用户定义的用来比较排序的函数的地址该函数的原型如下:int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);lParam1 和 lParam2 是 LV_ITEM型的结构体中的成员变量lParam的值。
lParamSort 是发送LVM_SORTITEMS消息时参数wParam中的值当列表视图控件接收到LVM_SORTITEMS消息时,当需要比较项目时它会调用在lParam中指定的比较函数比较函数将决定那一个项目排在前面方法很简单:如果函数返回一个负值,由(lParam代表的)第一个项目排在前,如果返回正值,第二个项目排在前如果相等,必须返回0 真正使得该方法能够运行的是LV_ITEM型结构体中的成员变量lParam值当您需要排序时(譬如当您点击列的标题条时),您需要考虑好排序方案在本例中,我们把项目的索引放到该成员变量中,这样我们可以通过发送LVM_GETITEM消息来得到项目的其它信息注意:当项目重排序后,它们的索引也就变了所以当重排序后,我需要在lParam参数中反应出新的索引如果您想在用户点击列的标题条时重新排序,您需要在您的窗口过程函数中处理LVN_COLUMNCLICK通知消息LVN_COLUMNCLICK消息是随同M_NOTIFY消息一起发送的例子:见光盘FirstWindow29该例子创建了一个列表视图控件,并在其中显示了当前文件夹中的文件大小和文件名缺省的视图是报告方式的,如果您点击列标题条,标题将按升/降序重新排列。
您可以通过菜单选择不同的显示方式(大图标、小图标等)当您双击一个项目时,项目的名称将显示在一个对话框中include "Windows.h"#include "tchar.h"#include "commctrl.h"#include "shlwapi.h"#pragma comment(lib,"comctl32.lib")#pragma comment(lib,"shlwapi.lib")#define IDM_MAINMENU 10000#define IDM_ICON LVS_ICON#define IDM_SMALLICON LVS_SMALLICON#define IDM_LIST LVS_LIST#define IDM_REPORT LVS_REPORTTCHAR ClassName[] = _T("ListViewWinClass");TCHAR AppName[] = _T("Testing a ListView Control");TCHAR ListViewClassName[] = _T("SysListView32");TCHAR Heading1[] = _T("Filename");TCHAR Heading2[] = _T("Size");TCHAR FileNamePattern[] = _T("*.*");TCHAR mytemplate[] = _T("%lu");DWORD FileNameSortOrder;DWORD SizeSortOrder;HINSTANCE g_hInstance;HWND hList;HMENU hMenu;void InsertColumn(){ LV_COLUMN lvc; lvc.mask = LVCF_TEXT | LVCF_WIDTH; lvc.pszText = Heading1; lvc.cx = 150; SendMessage(hList,LVM_INSERTCOLUMN,0,(LPARAM)&lvc); lvc.mask |= LVCF_FMT; lvc.fmt = LVCFMT_RIGHT; lvc.pszText = Heading2; lvc.cx = 100; SendMessage(hList,LVM_INSERTCOLUMN,1,(LPARAM)&lvc);}void ShowFileInfo(DWORD row,WIN32_FIND_DATA* lpFind){ LV_ITEM lvi; CHAR buffer[20]; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = row; lvi.iSubItem = 0; lvi.pszText = lpFind->cFileName; lvi.lParam = row; SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&lvi); lvi.mask = LVIF_TEXT; lvi.iSubItem ++; wsprintf(buffer,mytemplate,lpFind->nFileSizeLow); lvi.pszText = buffer; SendMessage(hList,LVM_SETITEM,0,(LPARAM)&lvi);}void FillFileInfo(){ WIN32_FIND_DATA finddata; HANDLE fHandle; int i; BOOL bRet = TRUE; fHandle = FindFirstFile(FileNamePattern,&finddata); if(fHandle != INVALID_HANDLE_VALUE) { i = 0; while(bRet) { if(!(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { ShowFileInfo(i,&finddata); i++; } bRet = FindNextFile(fHandle,&finddata); } FindClose(fHandle); }}int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM SortType){ TCHAR buffer[256]; TCHAR buffer1[256]; LV_ITEM lvi; INT iRet,iRet1,iRet2; lvi.mask = LVIF_TEXT; lvi.pszText = buffer; lvi.cchTextMax = 256; switch(SortType) { case 1: lvi.iSubItem = 1; SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam1,(LPARAM)&lvi); StrToIntEx(buffer,STIF_SUPPORT_HEX,&iRet1); SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam2,(LPARAM)&lvi); StrToIntEx(buffer,STIF_SUPPORT_HEX,&iRet2); iRet = iRet1 - iRet2; break; case 2: lvi.iSubItem = 1; SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam1,(LPARAM)&lvi); StrToIntEx(buffer,STIF_SUPPORT_HEX,&iRet1); SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam2,(LPARAM)&lvi); StrToIntEx(buffer,STIF_SUPPORT_HEX,&iRet2); iRet = iRet2 - iRet1; break; case 3: lvi.iSubItem = 0; SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam1,(LPARAM)&lvi); lstrcpy(buffer1,buffer); SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam2,(LPARAM)&lvi); iRet = lstrcmpi(buffer1,buffer); break; default: lvi.iSubItem = 0; SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam1,(LPARAM)&lvi); lstrcpy(buffer1,buffer); SendMessage(hList,LVM_GETITEMTEXT,(WPARAM)lParam2,(LPARAM)&lvi); iRet = lstrcmpi(buffer,buffer1); break; } return iRet;}void UpdatelParam(){ LV_ITEM lvi; int nCount = SendMessage(hList,LVM_GETITEMCOUNT,0,0); lvi.mask = LVIF_PARAM; lvi.iSubItem = 0; lvi.iItem = 0; while(nCount > 0) { lvi.lParam = lvi.iItem; SendMessage(hList,LVM_SETITEM,0,(LPARAM)&lvi); lvi.iItem++; nCount --; }}void ShowCurrentFocus(){ LV_ITEM lvi; TCHAR buffer[256]; int nIndex = SendMessage(hList,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); lvi.iItem = nIndex; lvi.iSubItem = 0; lvi.mask = LVIF_TEXT; lvi.pszText = buffer; lvi.cchTextMax = 256; SendMessage(hList,LVM_GETITEM,0,(LPARAM)&lvi); MessageBox(NULL,buffer,AppName,MB_OK);}LONG CALLBACK ProcWinMain( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ){ switch(Msg) { case WM_CREATE: { hList = CreateWindowEx(NULL,ListViewClassName,NULL,LVS_REPORT | WS_CHILD |WS_VISIBLE, 0,0,0,0,hWnd,NULL,g_hInstance,NULL); InsertColumn(); FillFileInfo(); SendMessage(hList,LVM_SETTEXTCOLOR,0,(LPARAM)RGB(255,255,255)); SendMessage(hList,LVM_SETBKCOLOR,0,(LPARAM)RGB(0,0,0)); SendMessage(hList,LVM_SETTEXTBKCOLOR,0,(LPARAM)RGB(0,0,0)); hMenu = GetMenu(hWnd); CheckMenuRadioItem(hMenu,IDM_ICON,IDM_LIST,IDM_REPORT,MF_CHECKED); } break; case WM_DESTROY: PostQuitMessage(0); break; case WM_SIZE: MoveWindow(hList,0,0,LOWORD(lParam),HIWORD(lParam),TRUE); break; case WM_COMMAND: if(lParam == 0) { LONG nStyle = GetWindowLong(hList,GWL_STYLE) ; nStyle &= ~LVS_TYPEMASK; nStyle |= LOWORD(wParam); SetWindowLong(hList,GWL_STYLE,nStyle); CheckMenuRadioItem(hMenu,IDM_ICON,IDM_LIST,LOWORD(wParam),MF_CHECKED); } break; case WM_NOTIFY: { NMHDR *pNm = (NMHDR *)lParam; if(pNm->hwndFrom == hList) { if(pNm->code == LVN_COLUMNCLICK) { NM_LISTVIEW *pLV = (NM_LISTVIEW *)lParam; if(pLV->iSubItem == 1) { if(SizeSortOrder == 0 || SizeSortOrder == 2) { SendMessage(hList,LVM_SORTITEMS,1,(LPARAM)CompareFunc); UpdatelParam(); SizeSortOrder = 1; } else { SendMessage(hList,LVM_SORTITEMS,2,(LPARAM)CompareFunc); UpdatelParam(); SizeSortOrder = 2; } } else { if(FileNameSortOrder == 0 || FileNameSortOrder == 4) { SendMessage(hList,LVM_SORTITEMS,3,(LPARAM)CompareFunc); UpdatelParam(); FileNameSortOrder = 3; } else { SendMessage(hList,LVM_SORTITEMS,4,(LPARAM)CompareFunc); UpdatelParam(); FileNameSortOrder = 4; } } } else if(pNm->code == NM_DBLCLK) ShowCurrentFocus(); } } break; default: return DefWindowProc(hWnd,Msg,wParam,lParam); } return 0;}int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ WNDCLASSEX wc; MSG msg; HWND hWnd; InitCommonControls(); g_hInstance = hInstance; wc.cbSize = sizeof(WNDCLASSEX); wc.style = NULL; wc.lpfnWndProc = ProcWinMain; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDM_MAINMENU); wc.lpszClassName = ClassName; wc.hIcon = wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL,IDC_ARROW); RegisterClassEx(&wc); hWnd = CreateWindowEx(NULL,ClassName,AppName,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL); ShowWindow(hWnd,SW_SHOWNORMAL); UpdateWindow(hWnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }由于篇幅较长,见下文分析。
windows sdk编程系列文章 ---- 列表视图控件(续)2008-04-29 16:18分析:当主窗口创建后要做的第一件事是创建一个列表视图控件应用程序 case WM_CREATE: { hList = CreateWindowEx(NULL,ListViewClassName,NULL,LVS_REPORT | WS_CHILD |WS_VISIBLE, 0,0,0,0,hWnd,NULL,g_hInstance,NULL);我们调用CreateWindowEx来创建窗口,并把窗口类的名称“SysListView32”传给它缺省的显示方式是报告方式,因为您指定了LVS_REPORT标志作为它的风格 InsertColumn();创建列表视图控件后,我们向其中插入列void InsertColumn(){ LV_COLUMN lvc; lvc.mask = LVCF_TEXT | LVCF_WIDTH; lvc.pszText = Heading1; lvc.cx = 150; SendMessage(hList,LVM_INSERTCOLUMN,0,(LPARAM)&lvc);我们指定第一列的宽度和列的标题条,为了在该列中显示文件的名称,我们需要在LV_COLUMN 型结构体变量的成员变量Mask中设定标志位LVCF_TEXT 或 LVCF_WIDTH。
我们设定pszText为列标题条文本字符串的值,cx设定为列的宽度(以像素点为单位)然后我们发送LVM_INSERTCOLUMN消息给列表视图控件,并把该结构体变量传递给它 lvc.mask |= LVCF_FMT; lvc.fmt = LVCFMT_RIGHT;插入完第一列后,我们再插入第二列,单击该列的标题条可以按文件的大小排序因为我们需要右对齐文本,我们需要在成员变量fmt中指定标志位LVCFMT_RIGHT我们还必须在成员变量Mask中除了标志位LVCF_TEXT 和 LVCF_WIDTH外还需要指定标志位LVCF_FMT lvc.pszText = Heading2; lvc.cx = 100; SendMessage(hList,LVM_INSERTCOLUMN,1,(LPARAM)&lvc);}剩余的代码比较简单在pszText中放入文本字符串的地址,在cx中放入列的宽度然后发送消息LVM_INSERTCOLUMN 给列表视图控件,在参数中同时传递列号和结构体变量的地址当插入完列后,我们向列表控件中加入项目 FillFileInfo();FillFileInfo 的代码如下:void FillFileInfo(){ WIN32_FIND_DATA finddata; HANDLE fHandle; int i; BOOL bRet = TRUE; fHandle = FindFirstFile(FileNamePattern,&finddata);我们调用FindFirstFile来得到第一个符合搜索标准的的文件的信息。
FindFirstFile函数的原型如下HANDLE FindFirstFile( LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData );lpFileName 是用来匹配搜索的文件名的地址该字符串包含了通配符在我们的例子中是*.*,这样会搜索当前文件夹中所有的文件lpFindData 是WIN32_FIND_DATA 型的结构体变量的地址,WIN32_FIND_DATA型的结构体变量将用来保存返回的文件的信息如果没有找到匹配的文件,该函数将返回INVALID_HANDLE_VALUE 否则将返回一个搜索句柄,您可以用该句柄在FindNextFile函数中来搜索下一个符合条件的文件if(fHandle != INVALID_HANDLE_VALUE){ i = 0;如果找到了一个文件,我们在一个变量fHandle中保存搜索句柄,并把变量i清零,该变量i将用作项目的索引号 while(bRet) { if(!(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {在本课中,我们将不处理文件夹,所以我们检查dwFileAttributes成员变量的值是否有FILE_ATTRIBUTE_DIRECTORY 标志,如果有,我们就忽略掉它,然后调用FindNextFile。
ShowFileInfo(i,&finddata); i++; } bRet = FindNextFile(fHandle,&finddata); }我们调用ShowFileInfo函数包文件的名称和大小信息加到列表视图控件中去然后让变量i加一来增加项目的行号最后我们调用FindNextFile函数在当前文件夹中继续搜索文件一直到该函数返回0为止(这意味着没有可供搜索的文件了) FindClose(fHandle); }}当前文件夹中的文件枚举完毕后,我们必须关闭搜索句柄先在我们看一下ShowFileInfo函数该函数由两个参数,一个是项目的索引号(也即行号),另一个是WIN32_FIND_DATA型结构体变量的地址void ShowFileInfo(DWORD row,WIN32_FIND_DATA* lpFind){ LV_ITEM lvi; CHAR buffer[20]; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iItem = row; lvi.iSubItem = 0; 我们将传递项目的名称和lParam的值,所以我们在Mask中放入标志位LVIF_TEXT 和LVIF_PARAM。
接下来我们在iItem中放入传递进来的行号,另外由于这是主项目我们必须设置iSubItem的值等于0 lvi.pszText = lpFind->cFileName; lvi.lParam = row;我们现在要把标签字符串的地址,在这里也就是WIN32_FIND_DATA 型结构体变量中的文件的名称放到pszText中由于我们要完成对项目的排序,所以必须设置lParam的值,我把它设成行号值,这样我们可以根据索引值来查询项目 SendMessage(hList,LVM_INSERTITEM,0,(LPARAM)&lvi);设置完所有LV_ITEM型变量中的值后,我们发送LVM_INSERTITEM消息给列表视图控件来把项目插入到其中 lvi.mask = LVIF_TEXT; lvi.iSubItem ++; wsprintf(buffer,mytemplate,lpFind->nFileSizeLow); lvi.pszText = buffer;我们将把子项目插入到第二列一个子项目只能有一个标签这样我们在Mask中指定LVIF_TEXT标志位接着我们指定子项目所在的列,本例中我们通过将iSubItem加一使得该值等于1。
标签值是文件的大小,为了转换成文本我们调用wsprintf函数,然后把文本的地址放到pszText中去 SendMessage(hList,LVM_SETITEM,0,(LPARAM)&lvi);}当LV_ITEM型变量中的值设定好之后,我们向列表视图控件发送LVM_SETITEM消息,并一同把LV_ITEM变量的地址传过去注意:发送的消息是LV_ITEM而不是LVM_INSERTITEM,因为我们插入的是子项目,子项目不是真正的项目而是主项目的属性所以我们这时是在设定项目的属性,而不是加入一个项目当所有的项目都插入到列表视图控件后,我们设定它的文本和背景颜色 SendMessage(hList,LVM_SETTEXTCOLOR,0,(LPARAM)RGB(255,255,255)); SendMessage(hList,LVM_SETBKCOLOR,0,(LPARAM)RGB(0,0,0)); SendMessage(hList,LVM_SETTEXTBKCOLOR,0,(LPARAM)RGB(0,0,0));我们通过发送LVM_SETTEXTCOLOR 和 LVM_SETTEXTBKCOLOR 消息来设定文本的前景和背景色。
hMenu = GetMenu(hWnd); CheckMenuRadioItem(hMenu,IDM_ICON,IDM_LIST,IDM_REPORT,MF_CHECKED);我们将让用户通过菜单来选择它想要的显示方式这样我们必须先得到菜单的句柄我了让用户跟踪当前的视图,我们在菜单中放入一组单选按钮我们可以调用CheckMenuRadioItem函数,该函数将把一个单选按钮放到一个菜单项前注意我们创建列表视图控件时把它的宽度和高度都设成为0当父窗口改变大小时,它将同时改变大小这样我们可以让列表视图总是随着主窗口改变子我们的例子中,我们让列表视图填充整个客户区case WM_SIZE: MoveWindow(hList,0,0,LOWORD(lParam),HIWORD(lParam),TRUE); break;当父窗口接收到了WM_SIZE消息后,lParam的底字部分包含了客户区新的宽和高让后我们调用MoveWindow来改变列表视图控件的大小使得它覆盖整个的客户区当用户通过菜单选择了一种选择方式,我们必须相应地改变列表视图中的显示方式。
我们调用SetWindowLong函数来设定新的风格 case WM_COMMAND: if(lParam == 0) { LONG nStyle = GetWindowLong(hList,GWL_STYLE) ; nStyle &= ~LVS_TYPEMASK;首先得到当前的风格,然后清除旧的风格LVS_TYPEMASK 是LVS_ICON+LVS_SMALLICON+LVS_LIST+LVS_REPORT四种风格的集合这样当我们用当前的风格“与”“~ LVS_TYPEMASK”就等于清除了当前的显示风格在设计菜单时,我们使用了一些小技巧我们包显示风格的常数串当作菜单的ID号define IDM_ICON LVS_ICON#define IDM_SMALLICON LVS_SMALLICON#define IDM_LIST LVS_LIST#define IDM_REPORT LVS_REPORT这样当父窗口接收到WM_COMMAND消息时,希望显示的风格值会当成菜单的ID号传递过来。
nStyle |= LOWORD(wParam);在wParam中的低字部分是欲显示的风格.我们把希望显示的风格加到列表视图的风格中去(已经去除了旧的风格) SetWindowLong(hList,GWL_STYLE,nStyle);调用SetWindowLong函数来设定新的风格 CheckMenuRadioItem(hMenu,IDM_ICON,IDM_LIST,LOWORD(wParam),MF_CHECKED); } break;我们需要在被选择的显示方式前放入单选按钮如果要排序,我们必须处理WM_NOTIFY消息 case WM_NOTIFY: { NMHDR *pNm = (NMHDR *)lParam; if(pNm->hwndFrom == hList) { 当我们接收到了WM_NOTIFY 消息后,lParam包含了指向NMHDR型结构体变量的指针我们通过把列表视图控件的值和NMHDR型结构体变量中的hwndFrom成员变量的值比较来判断,如果相等的话我们就可以确定消息是列表视图控件发送的。
if(pNm->code == LVN_COLUMNCLICK) {如果通知消息是列表视图控件发送的,我们检测该消息是否是LVN_COLUMNCLICK如果是,它意味着用户点击了列标题条在接收到LVN_。