【问题标题】:Creating a table using Win32 API使用 Win32 API 创建表
【发布时间】:2012-01-23 01:25:40
【问题描述】:

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

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

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

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

现在,我找到了可以放置在窗口、按钮和右键菜单中的列表查看器对象(?)...但我不知道如何使用 Win32 API 制作表格。除了窗口本身,我什至还没有真正阅读过背景颜色。

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

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

【问题讨论】:

  • 你使用的是什么开发环境? (它有助于确定您可以使用什么。)
  • 我在 MSVC++ 工作,对不起,你是对的,我应该这么说......

标签: winapi visual-c++ frameworks


【解决方案1】:

使用 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)

等等……

【讨论】:

  • 老兄,这是这个问题的最佳解决方案。真的很奇怪,选择的答案是试图避开库存解决方案。
  • 还有Adding LV Items & SubItems上的MSDN中的示例代码。
【解决方案2】:

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

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

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

如果您坚持使用 VC++,The Grid Control 和 The Ultimate Grid 是基于 MFC 的。

如果您不使用 MFC,则有 BABYGRID 或 The Win32 SDK Data Grid。

如果它们都不适合,那么搜索“网格”要比搜索“表格”更幸运。

【讨论】:

    【解决方案3】:

    对于 Listview 示例,没有什么比 Classic Sample 更清晰的了!

    与此同时,Google 翻译以及 Unicode + 对 @Alejadro 的 Listview 的德语链接的救援进行了微小的修改 - 由于该页面不包含适当的 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 结构描述(见下文)。每个元素都可以表示为ICON、SMALLICON、LIST 元素或REPORT 行的左列。

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

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

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

    LVIF_IMAGE iImage
    LVIF_INDENT 缩进
    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_GETITEMW 和 LVM_SETITEMW 消息更改元素的属性。作为参数,你会得到一个指向 ListView 的 HWND 旁边的 LVITEMW 结构的指针,必须提前填写。

    结构元素详解:

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

    LVIF_IMAGE iImage

    LVIF_INDENT 缩进

    LVIF_PARAM lParam

    LVIF_STATE 状态

    LVIF_TEXT pszText

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

    iSubItem 与结构相关的子项的索引(从 1 开始)。如果结构引用一个项目而不是子项目,则为 0。

    pszText 指向一个以空字符结尾的字符串。如果值为LPWSTR_TEXTCALLBACK,则为回调项。如果这种情况发生变化,pszText 必须设置为LPSTR_TEXTCALLBACK,并且 ListView 由LVM_SETITEMW 或LVM_SETITEMTEXT 通知。 如果 ListView 的样式为 LVS_SORTASCENDING 或 LVS_SORTDESCENDING,则 pszText 不得设置为 LPWSTR_TEXTCALLBACK。

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

    iImage 图片列表中此元素图标的索引。

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

    元素动作

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

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

    编辑选定元素

    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..报告所有项目都已删除
    LVN_COLUMNCLICK......表示用户点击了报表显示的标题
    LVN_GETDISPINFO.......控件从父窗口请求有关演示的信息
    LVN_SETDISPINFO.......该项目的父窗口信息必须更新
    LVN_INSERTITEM........表示插入一个item
    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 属性一起发送,并且所有项目都已被编辑。

    【讨论】:

    • 您的回答在解释win32 ListView api时非常详细。我自己编写了一个类,可以更轻松地在 ListView 上创建、添加项目等。但是 win32 ListView 非常有限,可能不支持此任务(创建数据表)。 win32 ListView 设计为一维列表。即使在报告模式下,也只有一列是可选择和可编辑的。每隔一列只是附加信息。这很愚蠢。您必须将 ListView 子类化以使其更好地工作,这需要大量的研究。
    猜你喜欢
    • 2012-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    相关资源
    最近更新 更多