用于在 ListView 控件中插入列、项目和子项目的 C 函数






3.92/5 (11投票s)
Win32 API 和 C。
引言
如果您需要在 ListView 控件中插入列和项目,那么您可能已经花了一些时间寻找 SDK 代码。这是一篇简短的文章,其中包含两个随时可用的 C 函数
fInsertListViewColumn
用于将带有字符串的列插入 ListView 控件。fInsertListViewItem
用于将带有字符串的项目/子项目插入 ListView 控件。
背景
- 您应该熟悉使用 Win32 API 进行 C 程序编译。
- C 语言背景:指针、字符串、函数(例如
sprintf
和memset
)。 - API 背景:函数(例如
GetClientRect
、InvalidateRect
、MessageBox
、SendMessage
)和struct
(例如RECT
、LVITEM
、LVCOLUMN
)。
使用代码
声明、创建和调整 ListView 控件的大小,即类型为 WC_LISTVIEW
的子窗口,样式例如 WS_BORDER | WS_CHILD | WS_VISIBLE | LVS_REPORT
。您可以 CreateWindowEx
ListView,和/或将其包含在您的 Visual Studio 项目中。此 ListView 将包含文本。
请记住,在 ListView 中,您处理的是列、项目和子项目。一列仅包含项目(通常是最左边的列),所有其他列仅包含子项目。
在下面的函数中,我们使用关键 API 函数 SendMessage
以及 LVM_INSERTCOLUMN
、LVM_INSERTITEM
和 LVM_SETITEM
消息来分别插入列、项目和子项目。
这些函数简单易懂,并带有嵌入式注释,逐步解释它们的作用。但是,这里有一些元素
函数的声明
int fInsertListViewColumn(HWND hwndListView,
long lCol, int iPercent, unsigned char* text);
int fInsertListViewItem(HWND hwndListView,long lLin,
long lCol, int iSubItemYesNo, unsigned char* text);
函数的参数
hwndListView
是您的 ListView 控件的句柄。lLin
和lCol
分别是行和列的从零开始的索引。iPercent
是您要插入到 ListView 控件中的列的宽度百分比。- 如果
iSubItemYesNo
等于 0,则插入项目;如果iSubItemYesNo
等于 1,则插入子项目。 text
是LVITEM
和LVCOLUMN
struct
的pszText
成员将指向的“字符串”。
您可能还需要这些 include
。
#include <windows.h>
#include <stdio.h>
#include <commctrl.h>
(etc...)
调用函数,例如像这样 - 以下代码将插入 5 列和 8 行(即 5 列,8 个项目和 32 个子项目)
int iColumnsToInsert = 5, iLinesToInsert = 8, i, j;
unsigned char sListViewText[200];
// Remember 65 is ASCII decimal for 'A', 66 is 'B', etc.:
for (i=0; i<iColumnsToInsert; i++)
{
sprintf(sListViewText, "%c (column)", 65 + i);
// Columns are labeled 'A', 'B', ... , 'E':
fInsertListViewColumn(hwndLV, i, 100 / iColumnsToInsert, sListViewText);
}
for (i=0; i<iColumnsToInsert; i++)
{
for (j=0; j<iLinesToInsert; j++)
{
// Items / subitems are labeled 'A1', 'A2', ... , 'E8':
if (i == 0) sprintf(sListViewText, "%c%d (item)", 65 + i, j + 1);
else sprintf(sListViewText, "%c%d (subitem)", 65 + i, j + 1);
// Here iSubItemYesNo=0 if i is 0; iSubItemYesNo=1 otherwise:
fInsertListViewItem(hwndLV, j, i, i == 0 ? 0 : 1, sListViewText);
}
}
函数的返回值
- 0 表示失败。
- 1 表示正常。
这是第一个函数的代码
int fInsertListViewColumn(HWND hwndListView, long lCol,
int iPercent, unsigned char* text)
{
/*********************
* Local declarations *
*********************/
// LVCOLUMN struct (see MSDN for content):
LVCOLUMN lvcolumn;
// RECT struct, used in column width and
// WM_PAINT jobs:
RECT rect;
// Enter the client area of the ListView
//into the RECT structure:
GetClientRect(hwndListView, &rect);
// To appear in the caption of the MessageBox:
static unsigned char *sYourApplicationsName = "you name it :)";
// Error 'string' you can use to 'MessageBox' and/or to
// 'fprintf' into a logfile:
unsigned char sErrorString[5000];
// Value to be returned after the function tried
// to insert a column:
int iResult;
/*************************
* Minimum error checking *
*************************/
// iPercent adjustement before display, e.g.:
iPercent = iPercent > 10 ? min(iPercent, 90) : 10;
// Column width, with 'rect.right' the x-coordinate of
// the lower-right corner of the RECT:
int iWidth = (int) (rect.right * (iPercent / 100.0));
// Check ListView handle:
if (hwndListView == NULL)
{
sprintf(sErrorString,
"! Handle of ListView NULL (fInsertListViewColumn)");
MessageBox(NULL, (LPSTR) sErrorString,
(LPSTR) sYourApplicationsName, MB_OK | MB_ICONEXCLAMATION);
return(0);
}
/***********************************************
* Use a LVCOLUMN struct to describe the column *
* that you want to insert into the ListView *
***********************************************/
// Initialize the LVCOLUMN struct (see LVCF_xxx and
// LVCFMT_xxx values in MSDN):
memset(&lvcolumn, 0, sizeof(lvcolumn));
// Necessary mask values for this job:
lvcolumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
// Left-align the text:
lvcolumn.fmt = LVCFMT_LEFT;
// Point to the right address:
lvcolumn.pszText = text;
// Nota bene: you might want to check lCol value first
lvcolumn.iSubItem = lCol;
// Set column width:
lvcolumn.cx = iWidth;
// SendMessage returns the number (zero based) of the
// column inserted,
// or -1 if it fails:
if (SendMessage((HWND) hwndListView, (UINT) LVM_INSERTCOLUMN,
(WPARAM) (int) lCol, (LPARAM) &lvcolumn) == -1)
iResult = 0; else iResult = 1;
// Paints (updates) the client area of the ListView control:
InvalidateRect(hwndListView, &rect, TRUE);
// Returns either 1 (OK) or 0 (failed to insert column - e.g.,
// lCol too big):
return(iResult);
}
这是第二个函数的代码
int fInsertListViewItem(HWND hwndListView, long lLin,
long lCol, int iSubItemYesNo, unsigned char* text)
{
/*********************
* Local declarations *
*********************/
// LVITEM struct (see MSDN for content):
LVITEM lvi;
// RECT struct, used in column width and WM_PAINT jobs:
RECT rect;
// Enter the client area of the ListView into
// the RECT structure:
GetClientRect(hwndListView, &rect);
// To appear in the caption of the MessageBox:
static unsigned char *sYourApplicationsName = "you name it :)";
// Error string you can use to 'MessageBox' and/or to 'fprintf'
// into a logfile:
unsigned char sErrorString[5000];
// Value to be returned after the function tried to insert
// an item / subitem:
int iResult;
/*************************
* Minimum error checking *
*************************/
// Check ListView handle:
if (hwndListView == NULL)
{
sprintf(sErrorString,
"! Handle of ListView NULL (fInsertListViewItem)");
MessageBox(NULL, (LPSTR) sErrorString,
(LPSTR) sYourApplicationsName, MB_OK | MB_ICONEXCLAMATION);
return(0);
}
/*****************************************************
* Use a LVITEM struct to describe the item / subitem *
* that you want to insert into the ListView *
*****************************************************/
// Initialize the LVITEM struct (see LVIF_xxx reference in MSDN):
memset(&lvi, 0, sizeof(lvi));
// Minimum mask value for this job:
lvi.mask = LVIF_TEXT;
lvi.state = 0;
lvi.stateMask = 0;
// Point to the right address:
lvi.pszText = text;
// Nota bene: you might want to check lLin and lCol values first
lvi.iItem = lLin;
lvi.iSubItem = lCol;
switch(iSubItemYesNo)
{
case 0:
// Send LVM_INSERTITEM message if you want to
// insert an item (returns -1 if it fails):
if (SendMessage((HWND) hwndListView,
(UINT) LVM_INSERTITEM, (WPARAM) 0, (LPARAM) &lvi) == -1)
iResult = 0; else iResult = 1;
break;
case 1:
// Send LVM_SETITEM message if you want to insert
// a subitem (returns FALSE if it fails):
if (SendMessage((HWND) hwndListView,
(UINT) LVM_SETITEM, (WPARAM) 0, (LPARAM) &lvi) == FALSE)
iResult = 0; else iResult = 1;
break;
default:
sprintf(sErrorString,
"! Unexpected iSubItemYesNo value: %d (fInsertListViewItem)",
iSubItemYesNo);
MessageBox(NULL, (LPSTR) sErrorString,
(LPSTR) sYourApplicationsName, MB_OK | MB_ICONEXCLAMATION);
return(0);
break;
}
// Paints (updates) the client area of the ListView control:
InvalidateRect(hwndListView, &rect, TRUE) ;
// Returns either 1 (OK) or 0 (failed to insert an item / subitem):
return(iResult);
}
关注点
- 在 MSDN 中,关于插入列(标题为“LVCOLUMN Structure (Windows Explorer and Controls)”)
如果将列添加到索引为 0(最左边的列)且指定了
LVCFMT_RIGHT
或LVCFMT_CENTER
的列表视图控件,则文本不会右对齐或居中对齐。索引 0 列中的文本是左对齐的。 - 如果您编写 SDK 程序,请记住 MSDN 关于
GetMessage
函数的警告因为返回值可以是非零值、零或 -1,所以要避免像这样的代码
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
返回值为 -1 的可能性意味着此类代码可能导致致命的应用程序错误。相反,使用像这样的代码
BOOL bRet; while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); DispatchMessage(&msg); } }
参考文献
- Giannini M., Keogh J. Windows programming - Programmer's notebook. Prentice Hall PTR, 2001, pp. 381-407.
- Bengi. 使用 Win32 API 下的 ListView 控件(CodeProject 文章)。
- Petzold C. Programming Windows (fifth edition). Microsoft Press, 1999.
- MSDN:关键词:CreateWindowEx --> ListView --> listview 样式、消息、通知。
历史
- 2004 年 12 月 24 日th:版本 1。