中年人学C语言Windows程序设计,27控件ListView

最近学习的是WIN32控件ListView的使用,以下是个人的一点心得。
参考链接参考链接

ListView控件,在直观上个人认为就是表格的意思。与表格一样,ListView也有表头,表头有几个项,下面的内容行也有几个项。
在这里插入图片描述
使用列表控制的步骤如下:

调用CreateWindowEx函数来创建一个列表控件,指定它的类名为SysListView32。您还可以在此处指定控件初次显示时的方式。
创建和初始化用在列表控件中显示项目的图象列表(如果存在)。
向列表控件中插入列,如果显示的方式是报告方式这一步是必须的。
向控件中插入项目和自项目。

ListView的创建

创建ListView的函数是createWindow()函数,其中窗口类别参数使用 WC_LISTVIEW ,该参数定义在头文件 CommCtrl.h 中。

示例代码:

HWND hListview;
hListview = CreateWindowEx(0, TEXT("SysListView32"), 
           	TEXT("我是一个list表"), 
            WS_VISIBLE | WS_CHILD | WS_BORDER |LVS_REPORT |LVS_SHOWSELALWAYS,//样式
            0, 0,//坐标
            350, 100, //大小
            hwnd, 
            NULL, 
            hInst,
            NULL);

ListView控件。
为了使用ListView控件,我们需要初始化公共控件库,我们需要在程序刚刚启动时调用InitCommonControls() 函数,如果发生链接错误,说明我们没有链接拥有该函数的库文件。
它们对应的头和库 DLL分别为 #include <commctrl.h> comctl32.lib comctl32.dll
为了使用这个控件 我们就需要知道它的窗口类,利用Spy++等文件可以找到指定进程窗口的窗口类,而一个ListView控件也是一个子窗口,所以我们可以得到它的类名为syslistview32,其他的控件,我们只需要按照同样的道理来得到类名即可。
有了类名还不够,我们还需要知道每种控件的风格,比如listView控件有以下的风格LVS_REPORT | LVS_SHOWSELALWAYS, 它表示要产生报表和总是显示。为了得到控件的风格,我们可以通过MSDN中MFC中的ListView风格来作参考。有了窗口类和风格,我们利用CreateWindow就可以创建并得到
这个控件的句柄了。有了句柄,我们就可以随便控制了,具体要怎么看你自己的了。

ListView的操作

此外为了向ListView内插入项和列,我们需要两个结构体。
LVITEM和LVCOLUMN

(1)添加分栏,即为表头

LVCOLUMNW 结构(commctrl.h)


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 nameMeanings
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的值传给您,这样您就可以进行比较先列出那一个了。如果您现在还不太明白的话,没有系,我们稍后还要讲关于排序的问题。

控件通过SendMessage来发送消息来控制,常用的消息有:
SendMessage(hButton, LVM_INSERTCOLUMN, 0, (LPARAM)&colmn);

Field name
LVM_INSERTCOLUMN加入列,wParam 为整型,指定列号,lParam 为指向LV_COLUMN结构的指针
LVM_SETCOLUMN设置列,参数同上
LVM_INSERTITEM加入项目或子项目,wParam 为0,lParam 为指向LV_ITEM结构的指针
LVM_SETITEM设置项目或子项目,参数同上
LVM_GETITEM取得项目或子项目,参数同上
LVM_GETNEXTITEM取得下一个项目或子项目,可以用来取得光标选择的项目
LVM_DELETEITEM删除项目或子项目,wParam 为整型,指定项目索引号,lParam 为0
LVM_DELETEALLITEMS删除所有项目,wParam 和 lParam 均为0
LVM_SETTEXTCOLOR设置文字颜色,wParam 为0,lParam 为颜色的RGB值
LVM_SETTEXTBKCOLOR设置文字背景色,参数同上
LVM_SETBKCOLOR设置背景色,参数同上

添加分栏需要使用到LVCOLUMN 结构体,示例:

 LV_COLUMN lvc;//LVCOLUMN结构体,定义listview的列属性
 lvc.mask = LVCF_TEXT | LVCF_WIDTH| LVCF_FMT;//指定哪些成员包含有效信息的变量。该成员可以是零
   
   lvc.fmt = LVCFMT_CENTER;  //列标题与列中子项文本的对齐方式。最左边一列的对齐方式始终是 LVCFMT_LEFT;
   lvc.pszText = TEXT("姓名");//列的名称,如果正在设置列信息,则此成员是包含列标题文本的以空字符结尾的字符串的地址
   lvc.cx = 80;//设置列的宽度   
   SendMessage(hListview, LVM_INSERTCOLUMN, 0, (long)&lvc); //LVM_INSERTCOLUMN表示添加列表示添加列,给第1列添加标体

(2)添加项,即行

添加项需要使用到LVITEM 结构体,示例:

   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

有了这两个结构体,我们就可以利用SendMessage来给ListView控件发送消息来为它添加项和列。
我们分别通过下面两个消息来添加项和列。
SendMessage(hButton, LVM_INSERTITEM, 0, (LPARAM)&item);
SendMessage(hButton, LVM_INSERTCOLUMN, 0, (LPARAM)&colmn);

LVM_INSERTITEM表示添加项
LVM_INSERTCOLUMN表示添加列。
为了更好的查找关于ListView的消息,我们只需要在网上或MSDN 里查找 LVM_XXXXXX 就可以找到

// 向列表中的添加列
VOID InsertListViewColumns(HWND hListView)
{
    // 1. 初始化一个列结构体进行设置
    // 1.1 第一个字段 mask 表示想要应用哪些设置(对齐方式,文字,宽度)
    LVCOLUMN lvColumn = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH };
    // 1.2 设置对齐方式,第一列的对其方式始终是左对齐
    lvColumn.fmt = LVCFMT_CENTER;
    // 1.3 设置每一列的宽度
    lvColumn.cx = 100;// 2. 设置列名并添加列
    lvColumn.cx = 100;
    lvColumn.pszText = (LPWSTR)L"姓名";
    ListView_InsertColumn(hListView, 0, &lvColumn);
​
    lvColumn.cx = 50;
    lvColumn.pszText = (LPWSTR)L"年龄";
    ListView_InsertColumn(hListView, 1, &lvColumn);
​
    lvColumn.cx = 260;
    lvColumn.pszText = (LPWSTR)L"学校";
    ListView_InsertColumn(hListView, 2, &lvColumn);
}

相关的消息了。最好自己整理出一份关于ListView的全部消息。
循环添加消息,用FOR语句

 LVITEM vitem;//LVITEM结构体,定义listview的项的数据属性
vitem.mask = LVIF_TEXT;
//vitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
for (i = 0; i < 6; i++)
{
    //先添加项再设置子项内容   
    //插入第一行数据,这一段代码是不可以少的。      
    vitem.iItem = i;//第几行的数据
    vitem.iSubItem = 0;//第1列
    vitem.pszText = stu[i].name;//赋值
    ListView_InsertItem(hListview, &vitem);//ListView_InsertItem表示添加项
    // 设置子项  
    vitem.iSubItem = 1;//第2列
    vitem.pszText = stu[i].age;
    ListView_SetItem(hListview, &vitem);
    vitem.iSubItem = 2;//第3列
    vitem.pszText = stu[i].dept;
    ListView_SetItem(hListview, &vitem);
    vitem.iSubItem = 3;//第4列
    vitem.pszText = stu[i].job;
    ListView_SetItem(hListview, &vitem);
}

(3)删除list控件里面的内容

int nSelectItem = ListView_GetSelectionMark(hListWnd); //获取鼠标选中项的索引


SendMessage(hListWnd, LVM_DELETEITEM, nSelectItem, 0); //LVM_DELETEITEM 消息删除nSelectItem item。

(4)获取信息

int nSelectIndex = ListView_GetSelectionMark(hListWnd);

TCHAR wstrText[4][128] = { 0 };

//通过一个for循环 能够获取第 nSelectIndex item的所有内容

for (int i = 0; i < 4; i++)

{

ListView_GetItemText(hListWnd, nSelectIndex, i, wstrText[i], sizeof(wstrText[i]));

}

(5)修改行数据

如何添加一个行:插入一行数据+设置行的信息

ListView_InsertItem + ListView_SetItemText

// 添加数据到某一行
VOID InsertListViewItem(HWND hListView, int index, LPCWSTR Name, LPCWSTR Age, LPCWSTR School)
{
    // 1. 先添加一行数据,并且设置第一列的信息
    LVITEM lvItem = { LVIF_TEXT };
    lvItem.iItem = index;
    lvItem.pszText = (LPWSTR)Name;
    ListView_InsertItem(hListView, &lvItem);// 2. 设置每一行中的元素信息
    ListView_SetItemText(hListView, index, 1, (LPWSTR)Age);
    ListView_SetItemText(hListView, index, 2, (LPWSTR)School);
}

(6)如何获取列表的选中项

需要注意通知码的筛选, NM_XXXX

当响应的是列表控件产生的通知消息时, LParam 保存的是一个指针,指向 NMLISTVIEW

// 2. 筛选消息是由谁产生的
if (lpNmhdr->idFrom == IDC_LIST1)
{
    // 3. 如果产生的是列表的通知消息,lParam 指向的是另外一个结构
    LPNMLISTVIEW lpNmListVew = (LPNMLISTVIEW)lParam;// 4. 如果产生的是鼠标的点击消息
    if (lpNmhdr->code == NM_CLICK)
    {
        // 判断点击的行是否有效
        // int n = ListView_GetItemCount(lpNmhdr->hwndFrom);
        if (-1 != lpNmListVew->iItem)
        {
            // 4.1 可以通过 LPNMLISTVIEW 获取点击的位置
            LVITEM lvItem = { LVIF_TEXT };
            // 4.2 必须要将 pszText 指向一个有效的位置
            lvItem.pszText = new WCHAR[0x10];
            // 4.3 设置缓冲区的大小
            lvItem.cchTextMax = 0x10;// 4.4 设置要获取的行列信息学
            lvItem.iItem = lpNmListVew->iItem;
            lvItem.iSubItem = lpNmListVew->iSubItem;// 4.5 发送消息获取数据
            ListView_GetItem(lpNmhdr->hwndFrom, &lvItem);// 4.6 显示获取的数据
            MessageBox(hWnd, lvItem.pszText, L"左键点击", MB_OK);
        }
    }
}

(7)在列表中弹出一个菜单项

case WM_NOTIFY:
{
    // 1. 响应 WM_NOTIFY 消息的时候, lParam 指向的但通常十一个结构体
    LPNMHDR lpNmhdr = (LPNMHDR)lParam;// 2. 筛选消息是由谁产生的
    if (lpNmhdr->idFrom == IDC_LIST1)
    {
        // 右键弹出菜单
        if (NM_RCLICK == lpNmhdr->code)
        {
            // 1. 获取点击的位置,获取的是相对于桌面的
            POINT Point = { 0 };
            GetCursorPos(&Point);// 2. 获取一个子菜单
            HMENU hMenu = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU1));
            HMENU hSubMenu = GetSubMenu(hMenu, 0);// 3. 弹出菜单
            TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, Point.x, Point.y, NULL, hWnd, nullptr);
        }
    }break;
}

(8)第一列不能居中和乱码的问题

曾经卡了我一天的bug 就是 第一列不能居中和乱码的问题,最后发现把第一列宽度设置成0,从第二列开始显示就能解决问题。
代码如下:

LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;//指定哪些成员包含有效信息的变量。该成员可以是零        
lvc.fmt = LVCFMT_CENTER;  //列标题与列中子项文本的对齐方式。最左边一列的对齐方式始终是 LVCFMT_LEFT;
lvc.pszText = TEXT("序号");//列的名称,如果正在设置列信息,则此成员是包含列标题文本的以空字符结尾的字符串的地址
lvc.cx = 0;//设置列的宽度   
SendMessage(hListview, LVM_INSERTCOLUMN, 0, (LPARAM)&lvc); //LVM_INSERTCOLUMN表示添加列表示添加列,给第1列添加标体

效果实例

创建一个listview,然后对其进行赋值
在这里插入图片描述

实例代码

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rt;
    //int ret;
    int i;

    struct 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("前台") }
    };

    switch (message)
    {
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {

        case IDM_ABOUT: //点击关于选项

            MessageBox(hwnd, TEXT("listviewDemo v1.0\nCopyright (C) 2021\n by 卖身买镜头"),
                TEXT("listviewDemo"),  MB_ICONINFORMATION);
            break;
        case IDM_EXIT://点击退出选项
            DestroyWindow(hwnd);
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;
    case WM_CREATE://WS_EX_CLIENTEDGE
        //创建listview子窗口
        hListview = CreateWindowEx(0, TEXT("SysListView32"), TEXT("我是一个list表"), 
            WS_VISIBLE | WS_CHILD | WS_BORDER |LVS_REPORT | LVS_SHOWSELALWAYS,
            0, 0,350, 100, 
            hwnd, NULL, hInst, NULL);

        LV_COLUMN lvc;//LVCOLUMN结构体,定义listview的列属性
        lvc.mask = LVCF_TEXT | LVCF_WIDTH| LVCF_FMT;//指定哪些成员包含有效信息的变量。该成员可以是零
        
        lvc.fmt = LVCFMT_CENTER;  //列标题与列中子项文本的对齐方式。最左边一列的对齐方式始终是 LVCFMT_LEFT;
        lvc.pszText = TEXT("姓名");//列的名称,如果正在设置列信息,则此成员是包含列标题文本的以空字符结尾的字符串的地址
        lvc.cx = 80;//设置列的宽度   
        SendMessage(hListview, LVM_INSERTCOLUMN, 0, (long)&lvc); //LVM_INSERTCOLUMN表示添加列表示添加列,给第1列添加标体

        lvc.pszText = TEXT("年龄");
       
        lvc.cx = 70;
        SendMessage(hListview, LVM_INSERTCOLUMN, 1, (long)&lvc);
       
        lvc.pszText = TEXT("部门");
        lvc.cx = 80;
        SendMessage(hListview, LVM_INSERTCOLUMN, 2, (long)&lvc);
        lvc.pszText = TEXT("职务");
        lvc.cx = 80;
        SendMessage(hListview, LVM_INSERTCOLUMN, 3, (long)&lvc);

        SendMessage(hListview, LVM_SETTEXTCOLOR, 0, RGB(255, 255, 255));//设置文字颜色
        SendMessage(hListview, LVM_SETTEXTBKCOLOR, 0, RGB(100, 0, 200));//设置文字背景颜色
        SendMessage(hListview, LVM_SETBKCOLOR, 0, RGB(150, 255, 50));//设置控件背景颜色
        

        LVITEM vitem;//LVITEM结构体,定义listview的项的数据属性
        vitem.mask = LVIF_TEXT;
        //vitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
        for (i = 0; i < 6; i++)
        {
            //先添加项再设置子项内容   
            //插入第一行数据,这一段代码是不可以少的。      
            vitem.iItem = i;//第几行的数据
            vitem.iSubItem = 0;//第1列
            vitem.pszText = stu[i].name;//赋值
            ListView_InsertItem(hListview, &vitem);//ListView_InsertItem表示添加项
            // 设置子项  
            vitem.iSubItem = 1;//第2列
            vitem.pszText = stu[i].age;
            ListView_SetItem(hListview, &vitem);
            vitem.iSubItem = 2;//第3列
            vitem.pszText = stu[i].dept;
            ListView_SetItem(hListview, &vitem);
            vitem.iSubItem = 3;//第4列
            vitem.pszText = stu[i].job;
            ListView_SetItem(hListview, &vitem);
        }

        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &rt);

        EndPaint(hwnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值