27win32编程基础——通用控件ListView的使用和通用控件的消息传递
这些常用的控件,叫标准控件。
其他不常用的控件,叫做通用控件,就放在一个dll中,windows通用控件,放在Comctl32.dll中使用的时候,要在文件头加
#include<commctrl.h> #pragma comment(lib,"comctl32.lib")
在vc6低版本中,在使用通用控件之前还要加代码
INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_WIN95_CLASSES; InitCOmmonControlsEx(&icex);
对控件使用初始化操作,vs2010版本中都不用加初始化代码,只用加头文件的2句代码即可。
一、以ListView举例用法:
#include <windows.h> #include "resource.h" #include<commctrl.h> #pragma comment(lib,"comctl32.lib") HINSTANCE hIns; INT_PTR CALLBACK DialogProc(HWND hwndDlg,UINT uMsg, WPARAM wParam,LPARAM lParam); int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { hIns = hInstance; DialogBox(hIns,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DialogProc); return 0; } void InitListView(HWND hwndDlg){ LVCOLUMN lvc; HWND HlistV=GetDlgItem(hwndDlg,IDC_LIST2); // //设置ListView的风格,点击就会整行选中 SendMessage(HlistV, LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); //相当于宏,SendMessage相当于宏ListView_SetExtendedListViewStyle(HlistV,LVS_EX_FULLROWSELECT) //第一列 lvc.mask = LVCF_TEXT | LVCF_WIDTH|LVCF_SUBITEM; lvc.pszText = TEXT("进程"); //列标题 lvc.cx = 80; //列宽 lvc.iSubItem =0; //第0列 SendMessage(HlistV, LVM_INSERTCOLUMN, 0, (DWORD)&lvc); //第2列 lvc.pszText = TEXT("PID"); lvc.cx = 60; lvc.iSubItem =1; SendMessage(HlistV, LVM_INSERTCOLUMN, 1, (long)&lvc); //第3列 lvc.mask = LVCF_TEXT | LVCF_WIDTH|LVCF_SUBITEM;; lvc.pszText = TEXT("镜像基址"); lvc.cx = 60; lvc.iSubItem =2; //SendMessage(HlistV, LVM_INSERTCOLUMN, 2, (long)&lvc); ListView_InsertColumn(HlistV,2, (long)&lvc); //第4列 lvc.pszText = TEXT("镜像大小"); lvc.cx = 60; lvc.iSubItem =3; //SendMessage(HlistV, LVM_INSERTCOLUMN, 3, (long)&lvc); ListView_InsertColumn(HlistV,3, (long)&lvc); //设置背景颜色 SendMessage(hwndDlg, LVM_SETTEXTCOLOR, 0, RGB(255, 255, 255)); SendMessage(hwndDlg, LVM_SETBKCOLOR, 0, RGB(135, 160, 135)); SendMessage(hwndDlg, LVM_SETTEXTBKCOLOR, 0, RGB(60, 100, 130)); struct STUDENTINFO { TCHAR name[15]; TCHAR age[4]; TCHAR dept[20]; TCHAR job[20]; }; STUDENTINFO stu[6] = { { TEXT("无忌"), TEXT("20"),TEXT("技术部"), TEXT("工程师") }, { TEXT("三丰"), TEXT("80"), TEXT("总经理"), TEXT("总经理") }, { TEXT("远桥"), TEXT("40"), TEXT("技术部"), TEXT("经理") }, { TEXT("敏敏"), TEXT("18"), TEXT("客服部"), TEXT("经理") }, { TEXT("芷若"), TEXT("18"), TEXT("行政部"), TEXT("经理") }, { TEXT("小昭"), TEXT("16"), TEXT("经理"), TEXT("前台") } }; //往里面填充内容,需要结构体LVITEM LVITEM vitem; vitem.mask = LVIF_TEXT; //填入文本 for (int i = 0; i < 6; i++) { vitem.iItem = i; //第几行填充内容 //先添加项再设置子项内容 vitem.iSubItem = 0; //第几列 vitem.pszText = stu[i].name; ListView_InsertItem(HlistV, &vitem); //只有第1列使用的是ListView_InsertItem,后边都是ListView_SetItem //SendMessage(HlistV, LVM_INSERTITEM, 2, (long)&vitem); // 设置子项 vitem.iSubItem = 1; vitem.pszText = stu[i].age; ListView_SetItem( HlistV, &vitem); //宏,可以F12看一下,和SendMessag是一样的 vitem.iSubItem = 2; vitem.pszText = stu[i].dept; //ListView_SetItem(HlistV, &vitem); SendMessage(HlistV, LVM_SETITEM, 2, (long)&vitem);//必须使用LVM_SETITEM,只有第一列是使用LVM_INSERTITEM //SendMessage(HlistV, LVM_INSERTITEM, 2, (long)&vitem);// vitem.iSubItem = 3; vitem.pszText = stu[i].job; ListView_SetItem(HlistV, &vitem); } }; void EnumModules(HWND hwnd,WPARAM wParam,LPARAM lParam){ DWORD dwRowId; TCHAR szPid[0x20]; LVITEM lv; //初始化结构 memset(&lv,0,sizeof(LVITEM)); memset(szPid,0,0x20); //获取选择的行,也可以使用ListView_GetNextItem(hwnd,LVNI_SELECTED ) dwRowId = SendMessage(hwnd,LVM_GETNEXTITEM,-1,LVNI_SELECTED); dwRowId = ListView_GetNextItem(hwnd,-1,LVNI_SELECTED); //dwRowId =ListView_GetNextItem(hwnd,-1,LVM_GETNEXTITEM); if(dwRowId==-1) { MessageBox(NULL,TEXT("请选择进程"),TEXT("出错啦"),MB_OK); return; } //获取选择的行中,pid这一列的内容 lv.iSubItem = 1; lv.pszText = szPid; lv.cchTextMax = 0x20; SendMessage(hwnd,LVM_GETITEMTEXT,dwRowId,(DWORD)&lv); MessageBox(NULL,szPid,TEXT("PID"),MB_OK); }; INT_PTR CALLBACK DialogProc(HWND hwndDlg,UINT uMsg, WPARAM wParam,LPARAM lParam){ HICON hicBig; switch(uMsg){ case WM_CLOSE: { EndDialog(hwndDlg,0); break; } case WM_INITDIALOG: { hicBig = LoadIcon(hIns, MAKEINTRESOURCE(IDI_ICON1)); SendMessage(hwndDlg,WM_SETICON,ICON_BIG,DWORD(hicBig)); SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,DWORD(hicBig)); InitListView(hwndDlg); break; } case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BUTTON_PE: return true; case IDC_BUTTON_ABOUT: return true; case IDC_BUTTON_EXIT: EndDialog(hwndDlg,0); return true; } break; case WM_NOTIFY: //当时WM_NOTIFY的时候lParam指向NMHDR结构体 NMHDR* pNMHDR = (NMHDR*)lParam; //判断是否是控件ListView和点击了ListView if(wParam == IDC_LIST2 && pNMHDR->code==NM_CLICK) { EnumModules(GetDlgItem(hwndDlg,IDC_LIST2),wParam,lParam); } break; } return FALSE; };
列表视图控件是一种非常常用的控件,在需要以报表形式显示数据时,列表控件通常是最好的选择,许多专用的数据报表控件,也是在它的基础上派生而来。与树视图类似,列表控件可以由多个子项目组成,可以设置为Icon(大图标)、SmallIcon(小图标)、List(列表)或Report(报表)。
一、列表视图控件有两个重要的数据结构LVCOLUMN和LVITEM。LVCOLUMN用于定义报表方式下的“列”的结构;LVITEM用于定义“项”的结构。这两个结构的定义及说明如下:
typedef struct _LVCOLUMN { UINT mask; 说明此结构中哪些成员是有效的 int fmt; 列的对齐方式 int cx; 列的初始宽度 LPTSTR pszText; 列的标题 int cchTextMax; pszText所指向的缓冲区的大小 int iSubItem; 与列关联的子项的索引值,从0开始 int iImage; 与列关联的图像列表中指定图像的索引值 int iOrder; 第几列,0代表最左一列 } LVCOLUMN, FAR LPLVCOLUMN; ----------------------------------- typedef struct _LVITEM { UINT mask; 说明LVITEM结构中哪些成员有效 int iItem; 项目的索引值(可以视为行号)从0开始 int iSubItem; 子项的索引值(可以视为列号)从0开始 UINT state; 子项的状态 UINT stateMask; 状态有效的屏蔽位 LPTSTR pszText; 主项或子项的名称 int cchTextMax; pszText所指向的缓冲区大小 int iImage; 关联图像列表中指定图像的索引值 LPARAM lParam; 程序定义的32位参数 int iIndent; 表示图像位置缩进的单位 } LVITEM, FAR LPLVITEM;
二、相关操作。因为是SDK编程,所以对控件的操作是向控件发送SendMessage()来实现的 LISTVIEW中的相关消息为: 1、LVM_SETTEXTCOLOR 和 LVM_SETTEXTBKCOLOR和 LVM_SETBKCOLOR //消息设定文本的前景和背景色,wParam 为0,lParam 为颜色的RGB值 2、 LVM_GETNEXTITEM //找到选中的行,对应ListView_GetNextItem宏将SendMessage打包为函数方式操作。 3、LVM_SETCOLUMNWIDTH //设置列宽,对应ListView_SetColumnWidth 4、LVM_SETEXTENDEDLISTVIEWSTYLE //设置扩展风格如:LVS_EX_FULLROWSELECT(选中一整行), LVS_EX_GRIDLINES(网络线),LVS_EX_CHECKBOXES(选择按钮),对应ListView_SetExtendedListViewStyle 5、LVM_INSERTCOLUMN //插入新列,对应ListView_InsertColumn,wParam 为整型,指定列号,lParam 为指向LV_COLUMN结构的指针 6、LVM_SETCOLUMN //设置列,参数同上 7、LVM_INSERTITEM //插入项目(行),对应ListView_InsertItem,加入项目或子项目,wParam 为0,lParam 为指向LV_ITEM结构的指针 8、LVM_SETITEM //设置子项(行中的每列),对应ListView_SetItem,设置项目或子项目,参数同上 9、LVM_GETITEM //取得项目或子项目,参数同上 10、LVM_GETITEMCOUNT //获取项数,对应ListView_GetItemCount 11、LVM_GETNEXTITEM 取得下一个项目或子项目,可以用来取得光标选择的项目 12、LVM_DELETEITEM //删除项,对应ListView_DeleteItem,删除项目或子项目,wParam 为整型,指定项目索引号,lParam 为0 13、LVM_DELETEALLITEMS //删除所有项目,wParam 和 lParam 均为0 三、ListCtrl控件的扩展样式 LVS_EX_GRIDLINES //绘制表格线 LVS_EX_SUBITEMIMAGES//子项目图标列表 LVS_EX_CHECKBOXES //带复选框 LVS_EX_TRACKSELECT //自动换行 LVS_EX_HEADERDRAGDROP//报表头可以拖拽 LVS_EX_FULLROWSELECT //选择整行 LVS_EX_ONECLICKACTIVATE//单击激活 LVS_EX_TWOCLICKACTIVATE//双击激活 LVS_EX_FLATSB//扁平滚动条 LVS_EX_REGIONAL LVS_EX_INFOTIP LVS_EX_UNDERLINEHOT LVS_EX_UNDERLINECOLD LVS_EX_MULTIWORKAREAS//多工作区
二、LiveView等通用控件的消息,不再是使用WM_COMMAND而是WM_NOTIFY
点击ListView中不同的位置,应该如何处理这种消息呢?
之前的子窗口是通过WM_COMMAND转向父窗口发送的。
但是像ListView这种复杂控件就不能这样的,因此不同的位置,包含的消息信息比较多,使用W_COMMAND,不能包含复杂的消息信息,就产生了WM_NOTIFY消息。
WM_NOTIFY消息与WM_COMMAND类似,都是由子窗口向父窗口发送的消息。
但是WM_NOTIFY包含的消息信息比WM_COMMAND更多、更丰富
Windows中通用组件中很多消息,都是通过WM_NOFITY来描述的,而不是使用WM_COMMAND
当为WM_NOTIFY消息的时候,WPARAM wParam是控件ID,LPARAM lParam指向一个消息扩充的结构体:NMHDR、NMLVCACHEHINT 、NMLVDISPINFO、NMLVFINDITEM
typedef struct tagNMHDR { HWND hwndFrom; //发送消息通知的控制窗口句柄 UINT_PTR idFrom; //发送通知消息的控制ID值 UINT code; //通知码,做的什么事情,左键还是右键,选中,例如WM_SELCHANGED } NMHDR; 如果这些消息描述的信息内容还不够,windows还有扩充了另外一些消息结构 typedef struct tagNMLVCACHEHINT { NMHDR hdr; 像不像继承,这就是c语言写的继承的方法 int iFrom; int iTo; } NMLVCACHEHINT, *PNMLVCACHEHINT; typedef struct tagNMLVDISPINFO { NMHDR hdr; LVITEM item; } NMLVDISPINFO; typedef struct _NMLVFINDITEM { NMHDR hdr; int iStart; LVFINDINFO lvfi; } NMLVFINDITEM, *PNMLVFINDITEM;
例如:
NMLVDISPINFO 是 Windows API 中的一个消息类型,用于在列表视图控件(如 ListView)中显示信息。它通常用于自定义列表视图项的显示方式。
在编程中使用 NMLVDISPINFO 消息,你需要按照以下步骤进行:
包含头文件 windows.h 以访问所需的 API。
创建一个消息处理函数,该函数将处理 NMLVDISPINFO 消息。函数签名应如下所示:
c++
void OnDisplayInfo(NMLVDISPINFO* pDispInfo);
在你的代码中,使用 SetWindowLong 或 SetWindowLongPtr 函数将该消息处理函数与 NMLVDISPINFO 消息关联起来。例如:
c++
HWND hwnd = CreateWindowEx(0, L"MyWindowClass", L"NMLVCACHEHINT Example", WS_OVERLAPPEDWINDOW, 0, 0, 300, 300, nullptr, nullptr, nullptr, nullptr);//先不定义回调函数,然后使用SetWindowLong定义回调函数。
SetWindowLong(m_hWnd, GWL_WNDPROC, OnDisplayInfo);
在消息处理函数中,解析 NMLVDISPINFO 结构以获取有关消息的信息。你需要关注以下字段:
hdr:消息的头部信息,包括消息类型和其他信息。
item:LVITEM 结构,表示要显示的项目信息。它包含了文本、图像、状态等信息。你可以使用 item.pszText 获取文本信息,并根据需要修改其他字段。
iItem:要显示的项目索引。
iSubItem:要显示的子项索引。如果你的列表视图只包含一个列,则该字段通常为 0。
根据需要自定义项目的显示方式。你可以修改 item.pszText 字段来改变文本的显示内容,或者根据其他字段的值来自定义其他显示属性。
返回 TRUE 以指示消息已被处理。如果你返回 FALSE,则消息将被传递给下一个窗口处理函数。
以下是一个简单的示例代码片段,展示了如何使用 NMLVDISPINFO 消息自定义列表视图项的显示方式:
c++
#include <windows.h>
LRESULT CALLBACK OnDisplayInfo(NMLVDISPINFO* pDispInfo)
{
// 获取消息头部信息
const NMHDR& hdr = pDispInfo->hdr;
// 获取要显示的项目信息
const LVITEM& item = pDispInfo->item;
TCHAR szText[256]; // 假设文本不超过 256 个字符
// 根据需要修改项目的显示方式
switch (item.iSubItem)
{
case 0: // 修改第一列的显示内容
if (item.iItem % 2 == 0)
{
_tcscpy_s(szText, _T("Modified Text "));
_tcscat_s(szText, _T("Item "));
_tcscat_s(szText, item.pszText);
}
else
{
_tcscpy_s(szText, item.pszText);
}
break;
default: // 其他子项的显示方式保持不变
_tcscpy_s(szText, item.pszText);
break;
}
// 设置显示文本并返回 TRUE 表示消息已处理
ListView_SetItemText(pDispInfo->hdr.hwndFrom, item.iItem, item.iSubItem, szText);
return TRUE;
}