使用Win32 API创建表

时间:2012-01-23 01:25:40

标签: winapi visual-c++ frameworks

我一直在网上搜索关于win32 API的不同内容,但似乎所有关于它的信息都相当稀疏。

我希望创建一个显示项目列表的简单窗口,但是我希望以表格式格式显示每个项目的不同数据列,在这种格式中,可能允许用户调整其他列的大小宽度。

如果可能的话,我还希望能够在代码中更改不同行的背景颜色,只需要一般的白色,红色,黄色或绿色。

用户也可以右键单击不同的行,并能够调用它们上的函数,或者将数据复制到剪贴板(但这部分是另一个故事)。

现在,我找到了可以放在窗口,按钮和右键菜单中的list-viewer对象(?)......但是我无法弄清楚如何使用Win32 API来创建表。除了窗口本身之外,我甚至都没有真正阅读过背景颜色。

我应该使用不同的,更好的框架,还是有一些我一直缺少的功能或项目?关于这个想法的所有帮助或指导将不胜感激......

我正在使用MSVC ++做我正在处理的所有事情。

3 个答案:

答案 0 :(得分:10)

Windows提供了一个相当基本的内置控件集合,列出了here

如果你想要更复杂的东西,你的选择是:

  • 自己编码。你必须自己绘制它,处理所有用户交互,滚动等。这是一项很大的工作。
  • 查找现有实施。
  • 放弃VC ++并使用WinForms或WPF。

如果你坚持使用VC ++,The Grid ControlThe Ultimate Grid是基于MFC的。

如果您没有使用MFC,则BABYGRIDThe Win32 SDK Data Grid

如果它们都不合适,那么搜索“grid”而不是“table”会更幸运。

答案 1 :(得分:8)

使用Windows API和标准控件ListView,您可以使用LVS_REPORT样式执行表格

文档链接 - 不幸的是没有代码:( -

About List-View Controls

我找到了这篇好文章Windows Programmierung: List View 解释是用德语,但谷歌翻译与代码应该足以理解它。从文章中,创建窗口:

#include "commctrl.h"

InitCommonControls();
hwndList = CreateWindow(WC_LISTVIEW, "", 
         WS_VISIBLE|WS_BORDER|WS_CHILD | LVS_REPORT | LVS_EDITLABELS, 
         10, 10, 300, 100, 
         hWnd, (HMENU)ID_LIST, hInst, 0);

然后解释了如何在方法

中创建列
int CreateColumn(HWND hwndLV, int iCol, char *Text, int iWidth)

如何插入项目(一列)

int CreateItem(HWND hwndList, char *Text)

或插入带有两列的项目

int Create2ColItem(HWND hwndList, char *Text1, char *Text2)

等...

答案 2 :(得分:0)

对于Listview示例,没有什么比Classic Sample

更清晰

与此同时,谷歌翻译以及Unicode +微小的修改以拯救Listview的@ Alejadro德语链接 - 搜索结果中没有提供直接翻译作为页面不包含相应的meta tag。为了简洁起见,略微剪了一下:

后续的样式更改

创建后可以更改ListView的样式。为此,使用函数GetWindowLong和SetWindowLong。关于面具可以定义不同的样式。

  

面具.................................蒙面样式:
  LVS_TYPEMASK .............. LVS_ICON,LVS_LIST,LVS_REPORT和LVS_SMALLICON   LVS_ALIGNMASK ............. LVS_ALIGNLEFT和LVS_ALIGNTOP   LVS_TYPESTYLEMASK ... LVS_ALIGNLEFT和LVS_ALIGNTOP以及VS_NOCOLUMNHEADER和LVS_NOSORTHEADER

对于以下序列,dwView包含要使用的样式,例如LVS_REPORT or LVS_ICON

DWORD dwStyle = GetWindowLong(hwndLV, GWL_STYLE); // get current style
if ((dwStyle & LVS_TYPEMASK)! = dwView) // only on change
SetWindowLong(hwndLV, GWL_STYLE, (dwStyle & ~ LVS_TYPEMASK) | dwView); }

控制ListView控件

生成列表

使用CreateWindow函数创建列表视图。窗口类使用常量WC_LISTVIEW。为此,必须包含公共控件头文件。

#include "commctrl.h"

InitCommonControls();
hwndList = CreateWindow(WC_LISTVIEW, "",
  WS_VISIBLE | WS_BORDER | WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
  10, 10, 300, 100,
  hWnd, (HMENU) ID_LIST, hInst, 0);


在对话框中,它只是在资源中定义。

如果存在未解析的外部,则应检查是否包含公共控件(comctl32.lib)的库。

ListView的列

在可以在REPORT中插入某些内容之前,必须定义列。结构LVCOLUMN描述了一列。以下例程创建一个列。

int CreateColumn(HWND hwndLV, int iCol, char * text, intwidth)
{
LVCOLUMN lvc;

lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = iWidth;
lvc.pszText = text;
lvc.iSubItem = iCol;
return ListView_InsertColumn(hwndLV, iCol, & lvc);
}

可以通过发送到ListView的消息或通过调用最终将执行SendMessage的宏来修改列。

Message            Macro call                                    Function
LVM_INSERTCOLUMN   ListView_InsertColumn(HWND, int, LVCOLUMN * ) Insert column
LVM_DELETECOLUMN   ListView_DeleteColumn(HWND, int)              Delete column
LVM_GETCOLUMN      ListView_GetColumn(HWND, int, LVCOLUMN * )    Get properties of the column
LVM_SETCOLUMN      ListView_SetColumn(HWND, int, LVCOLUMN * )    Change properties of the column
LVM_GETCOLUMNWIDTH ListView_GetColumnWidth(HWND, int)            Determine column width
LVM_SETCOLUMNWIDTH ListView_SetColumnWidth(HWND, int, int)       Set column width

插入一行

ListView的一个元素由结构LVITEMW描述(见下文)。每个元素都可以表示为ICONSMALLICONLIST元素或REPORT行的左列。

int CreateItem(HWND hwndList, wchar_t * text)
 {
 LVITEMW lvi = {0};
 lvi.mask = LVIF_TEXT;
 lvi.pszText = text;
 return ListView_InsertItem(hwndList, & lvi);
 }

mask字段确定LVITEMW结构的哪些元素真正被使用。由于保持指向保存对象后面数据的内存对象的指针通常是有意义的,因此lParam字段很有用。为了使用它,必须将LVIF_TEXT | LVIF_PARAM设置为掩码。

掩码的常量和启用它们的字段:

  

LVIF_IMAGE iImage
  LVIF_INDENT iIndent
  LVIF_PARAM lParam
  LVIF_STATE状态
  LVIF_TEXT pszText

报告的其他列

元素本身始终保留在报表视图中,并且可以选择。要填充更多列,会在项目中添加一个文本。

int Create2ColItem(HWND hwndList, wchar_t * Text1, wchar_t * Text2)
{
LVITEMW lvi = {0};
int Ret;
// Initialize LVITEMW members that are common to all items.
lvi.mask = LVIF_TEXT;
lvi.pszText = Text1;
Ret = ListView_InsertItem(hwndList, & lvi);
if (Ret >= 0)
{
ListView_SetItemText(hwndList, Ret, 1, Text2);
}
return Ret;
}

上述Create2ColItem最能通过以下陈述中的某些内容得到证明:

    LVHwnd = Your_Create_LV_Routine();
    if (LVHwnd)
    {
    CreateColumn(LVHwnd, 0, ptrColHeaderString1, iColSize1);
    CreateColumn(LVHwnd, 1, ptrColHeaderString2, iColSize2);
    Create2ColItem(LVHwnd, ptrItemText1, ptrItemText2);
    }

结构LVITEMW

结构LVITEMW(在 CommCtrl.h 中)描述了ListView的一个元素。这里简要介绍了最重要的元素。首先是定义:

typedef struct tagLVITEMW
{
  UINT mask;
  int iItem;
  int iSubItem;
  UINT state;
  UINT stateMask;
  LPWSTR pszText;
  int cchTextMax;
  int iImage;
  LPARAM lParam;
  #if (_WIN32_IE >= 0x0300) //historical note for IE3 users!
  int iIndent;
  #endif
  #if (NTDDI_VERSION >= NTDDI_WINXP)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
  #endif
  #if (NTDDI_VERSION >= NTDDI_VISTA)
    int* piColFmt;
    int iGroup; // readonly. only valid for owner data.
  #endif
} LVITEMW, *LPLVITEMW;

LVM_GETITEMWLVM_SETITEMW消息会更改元素的属性。作为参数,您将获得指向ListView的LVITEMW旁边的HWND结构的指针,该结构必须提前填充。

详细结构要素:

面膜: 指定使用哪些元素。可以使用以下标志的组合:

  

LVIF_IMAGE iImage

     

LVIF_INDENT iIndent

     

LVIF_PARAM lParam

     

LVIF_STATE状态

     

LVIF_TEXT pszText

的iItem 与结构相关的项目的索引(从0开始)。

iSubItem 结构所涉及的子项的索引(从1开始)。如果结构引用的是项而不是子项,则为0。

pszText 指向以null结尾的字符串。如果值为LPWSTR_TEXTCALLBACK,则为回调项。如果此更改,则必须将pszText设置为LPSTR_TEXTCALLBACK,并通过LVM_SETITEMWLVM_SETITEMTEXT通知ListView。 如果ListView的样式为pszTextLPWSTR_TEXTCALLBACK,则LVS_SORTASCENDING不得设置为LVS_SORTDESCENDING

cchTextMax定义 读取文本时缓冲区的大小。

IIMAGE 图像列表中此元素图标的索引。

lParam的 特定于此元素的32位值。

元素操作

  

LVM_INSERTITEM插入元素   LVM_DELETEITEM删除元素   LVM_DELETEALLITEMS删除所有元素   LVM_GETITEMW读取元素的属性   LVM_GETITEMTEXT读取元素的文本   LVM_SETITEMW改变了   LVM_SETITEMTEXT更改为文本

在插入多个项目之前,将向ListView发送LVM_SETITEMCOUNT消息,指示最终将包含多少项目。这允许ListView优化其内存分配和释放。可以使用LVM_GETITEMCOUNT确定ListView包含的元素数量。

编辑所选元素

int Pos = -1;
LVITEMW Item;
Pos = ListView_GetNextItem(hwndList, Pos, LVNI_SELECTED);
while (Pos> = 0)
{
Item.iItem = Pos;
Item.iSubItem = 0;
ListView_GetItem(hwndList, & Item);
TuWasMitElement((Element Type * ) Item.lParam);
Pos = ListView_GetNextItem(hwndList, Pos, LVNI_SELECTED);
}

<强>事件 ListView将WM_NOTIFY消息发送到父窗口。代码可以采用以下值:

  

消息............说明
  LVN_BEGINDRAG .............开始拖放动作
  LVN_BEGINRDRAG ..........用鼠标右键开始拖放操作   LVN_BEGINLABELEDIT ....开始编辑标签
  LVN_ENDLABELEDIT .......结束编辑标签
  LVN_DELETEITEM ..........报告该项目已删除
  LVN_DELETEALLITEMS..Reports删除所有项目   LVN_COLUMNCLICK ......表示用户点击了报告显示的标题中   LVN_GETDISPINFO .......控件从父窗口请求有关演示的信息   LVN_SETDISPINFO .......必须更新项目父窗口的信息   LVN_INSERTITEM ..........表示项目的插入
  LVN_ITEMCHANGED .....表示项目已更改
  LVN_ITEMCHANGING ....表示项目的预期更改
  LVN_KEYDOWN .............按键

编辑标签 必须使用LVS_EDITLABELS样式创建列表视图。然后可以单击标签并接受输入。但是,之后立即丢弃输入。要允许更改标签,您只需要抓住WM_NOTIFY并返回TRUE。为了访问中间输入的文本,访问项目的文本。该示例在消息框中显示输入。

case WM_NOTIFY:
 switch (((LPNMHDR) lParam) -> code)
 {
  case LVN_ENDLABELEDIT:
  pItem = (NMLVDISPINFO) lParam;
  MessageBox (hWnd, pItem-> item.pszText, "entry", MB_OK);
  return TRUE;

如果编辑中止,pszText元素将为0。

如果您想阻止编辑,则会捕获并返回LVN_BEGINLABELEDIT消息TRUE。在这里,也可以通过lParam以相同的方式访问该项目,因此,例如,可以排除某个项目组。

点击ListView中的列标题

case WM_NOTIFY:
 switch (((LPNMHDR) lParam) -> code)
 {
 case LVN_COLUMNCLICK:
 ColumnNr = ((LPNMLISTVIEW) lParam) -> iSubItem;
 .....

选择事件

当用户激活项目时,会发送LVN_ITEMACTIVATE事件。与其他ListView事件一样,它将窗口函数作为WM_NOTIFY消息的一部分实现。

case WM_NOTIFY:
switch (((LPNMHDR) lParam) -> code)
{
case LVN_ITEMACTIVATE:
HWND hwndFrom = (HWND) ((LPNMHDR) lParam) -> hwndFrom;MarkedItemIndex = 
ListView_GetNextItem(hwndFrom, -1, LVNI_SELECTED);
.....

LVM_GETSELECTEDCOUNT消息可用于确定已激活的项目数。 LVM_GETNEXTITEM消息与LVNI_SELECTED属性一起发送,所有项目都已编辑。