c++ - 如何修复ListView中的错误?封装控件时无法选择任何项目

标签 c++ listview winapi

我正在 win32api 上开发储物柜管理器应用程序(已在控制台上完成)。第一个构建是在“win32 普通样式代码”中(函数定义和声明,没有对象)。这是一个 ListView ,用于显示与之交互的项目和按钮。 现在,这个版本就像一个魅力,我通过 LVN_GETDISPINFO 通知消息填充 ListView ,我还有一个拆分按钮来处理 BCN_DROPDOWN 通知消息。

在我看到它工作后,我想让它封装窗口和控件,并为 ListView 和按钮控件制作简单的包装器。 我以相同的方式处理相同的消息(LVN_GETDISPINFOBCN_DROPDOWN 通知消息)但在这种情况下,Listview 似乎已禁用(没有颜色变化),我只能'选择任何东西! 现在,如果我从 wndproc 中删除 WM_NOTIFY 消息,并手动填充列表(没有 dispinfo),它工作正常,但是一旦我将 WM_NOTIFY 消息添加到处理拆分按钮的 BCN_DROWDOWN,事情又出错了,无法选择任何项目。

ListView .cpp:

#include "ListView.h"
#include <vector>

ListView::ListView()
    :
    hWnd(nullptr), x(0), y(0), width(0), height(0), id(0), nTotalItems(0)/*, lvi({0}), lvc({0})*/
{

    INITCOMMONCONTROLSEX icex;
    icex.dwSize = sizeof(icex);
    icex.dwICC = ICC_LISTVIEW_CLASSES;

    InitCommonControlsEx(&icex);
}

void ListView::Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in)
{
    assert(hWnd == nullptr);

    x = x_in;
    y = y_in;
    width = width_in;
    height = height_in;
    id = id_in;

    hWnd = CreateWindow(WC_LISTVIEW, "", dwStyle, x, y, width, height, hWndParent, (HMENU)id, GetModuleHandle(nullptr), nullptr);

    assert(hWnd != 0);
}

BOOL ListView::InsertRows(int nRows)
{
    LVITEM lvi = {};

    lvi.mask = LVIF_TEXT | LVIF_STATE;
    lvi.pszText = LPSTR_TEXTCALLBACK;
    lvi.iSubItem = 0;
    lvi.stateMask = 0;
    lvi.state = 0;

    for(int i = 0; i < nRows; ++i)
    {
        lvi.iItem = i;

        if(ListView_InsertItem(hWnd, &lvi) == -1)
            return FALSE;
    }

    return TRUE;
}

BOOL ListView::InsertColumns(int nCols)
{
    LVCOLUMN lvc = {};
    char textCol[] = "Columna";
    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    lvc.cx = 100;
    lvc.pszText = textCol;
    lvc.fmt = LVCFMT_LEFT;

    for(int i = 0; i < nCols; ++i)
    {
        lvc.iSubItem = i;

        if(ListView_InsertColumn(hWnd, i, &lvc) == -1)
            return FALSE;
    }

    return TRUE;
}


void ListView::SetSubItemText(int nRow, int nCol, const std::string& strText)
{
    std::vector<char> tmpChar(strText.begin(), strText.end());
    tmpChar.push_back('\0');

    ListView_SetItemText(hWnd, nRow, nCol, &tmpChar[0]);
}


void ListView::SetSubItemText(int nRow, int nCol, char * szText)
{
    ListView_SetItemText(hWnd, nRow, nCol, szText);
}

void ListView::SetExStyle(DWORD dwExStyle)
{
    ListView_SetExtendedListViewStyle(hWnd, dwExStyle);
}

HWND ListView::Hwnd() const
{
    return this->hWnd;
}

ListView.h

#ifndef _LISTVIEW_H_
#define _LISTVIEW_H_

#include "../stdafx.h"
#include <CommCtrl.h>
#include <cassert>
#include <string>

class ListView
{
public:
    ListView();
    void Create(DWORD dwStyle, int x_in, int y_in, int width_in, int height_in, HWND hWndParent, int id_in);
    BOOL InsertRows(int nRows);
    BOOL InsertColumns(int nCols);
    void SetSubItemText(int nRow, int nCol, const std::string& strText);
    void SetSubItemText(int nRow, int nCol, char* szText);
    std::string GetSubItemText(int nRow, int nCol) const;
    void SetExStyle(DWORD dwExStyle);
    HWND Hwnd() const;

public:

private:
    HWND hWnd;
    int id;
    int x;
    int y;
    int width;
    int height;
    int nTotalItems;
};

#endif

主窗口.cpp

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
    {
        OnCreate();
        return 0;
    }
    break;

    case WM_COMMAND:
    {
        if(HIWORD(wParam) == BN_CLICKED)
            OnCommand(LOWORD(wParam));

        return 0;
    }
    break;

    case WM_NOTIFY:
    {
        OnNotify(lParam);
    }
    break;

    case WM_DESTROY:
        PostQuitMessage(0);
    return 0;

    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    break;
    }

    return TRUE;
}

void MainWindow::OnCreate()
{
    lvMain.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_ALIGNTOP | LVS_SHOWSELALWAYS | LVS_SINGLESEL,
        11, 11, 438, 322, hWnd, ID_LISTVIEW_MAIN);
    lvMain.SetExStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

    btnAceptar.Create(IDS_ASIGNAR, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT,
        456, 11, hWnd, ID_BUTTON_ASIGNAR);

    btnFiltro.Create(IDS_TODOS, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | BS_TEXT | BS_SPLITBUTTON,
        456, 41, hWnd, ID_BUTTON_VERTODOS);

    const int nColsWidths[] = { 50, 250, 80, 20, 20 };

    lvMain.InsertColumns(3);
    lvMain.InsertRows(3);

    EnumChildWindows(hWnd, SetChildWndFontProc, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
}

void MainWindow::OnCommand(WORD wCmdId)
{
    switch (wCmdId)
    {
    case ID_BUTTON_ASIGNAR:
    {
        MessageBox(Window(), "hola mundo!", "info", MB_OK);
    }
    break;
    }
}

void MainWindow::OnNotify(LPARAM lParam)
{
    switch ( ((LPNMHDR)lParam)->code)
    {
    case BCN_DROPDOWN:
    {
        if (((NMBCDROPDOWN*)lParam)->hdr.hwndFrom == btnFiltro.Hwnd())
        {
            RECT rcButton;
            GetClientRect(btnFiltro.Hwnd(), &rcButton);
            POINT pt;
            pt.x = rcButton.left;
            pt.y = rcButton.bottom;
            ClientToScreen(btnFiltro.Hwnd(), &pt);

            // Create a menu and add items.
            HMENU hSplitMenu = CreatePopupMenu();
            char szStringBuffer[255];

            LoadStringA(GetModuleHandle(nullptr), IDS_ASIGNADOS, szStringBuffer, 255);
            AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERASIGNADOS, szStringBuffer);

            LoadStringA(GetModuleHandle(nullptr), IDS_SINASIGNAR, szStringBuffer, 255);
            AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VERSINASIGNAR, szStringBuffer);

            // Display the menu.
            TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, 0, hWnd, NULL);
        }
    }
    break;
    case LVN_GETDISPINFO:
    {
        if (((LPNMHDR)lParam)->hwndFrom == lvMain.Hwnd() )
        {
            NMLVDISPINFO* plvdi = (NMLVDISPINFO*)lParam;

            switch (plvdi->item.iSubItem)
            {
            case 0:
            {
                char buff[100];
                strncpy_s(buff, "SubItem Index 0", 100);
                plvdi->item.pszText = buff;
            }
            break;
            case 1:
            {
                char buff[100];
                strncpy_s(buff, "SubItem Index 1", 100);
                plvdi->item.pszText = buff;
            }
            break;
            case 2:
            {
                char buff[100];
                strncpy_s(buff, "SubItem Index 2", 100);
                plvdi->item.pszText = buff;
            }
            break;
            default:
            break;
            }
        }
    }
    break;
    default:
    break;
    }
}

我希望得到与使用函数和全局变量创建和控制 ListView 项和子项相同的结果。我不知道出了什么问题,已经尝试将 OnNotify 处理程序设为静态,但结果相同,项目和子项目都在那里,但我无法选择任何内容。

希望你能帮助我,谢谢你的帮助!

顺便说一句,这是工作正常的代码:

Main.h

#ifndef _MAIN_H_
#define _MAIN_H_

//#define NDEBUG // for assert
//#define UNICODE
#define _WIN32_IE 0x0601
#define _WIN32_WINNT 0x0601
#include <sdkddkver.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <commctrl.h>
#include <cassert>
#include "LM/CLockManager.h"
#include "resource.h"

#define ID_LISTVIEW 1200
#define ID_BUTTON_ASIGNAR 1201
#define ID_BUTTON_VER_TODOS 1202
#define ID_BUTTON_BUSCAR 1203
#define ID_BUTTON_ELIMINAR 1204
#define ID_BUTTON_AGREGAR 1205
#define ID_MENU_VER_ASIGNADOS 1300
#define ID_MENU_VER_SIN_ASIGNAR 1301

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND CreateListView(HWND hWndParent);
HWND CreateButton(HWND hWndParent, const char* szBtnText, int x, int y, int id, DWORD dwStyle = 0);
BOOL InsertListViewColumns(HWND hWndLv_, int nColumns);
BOOL InsertListViewItem(HWND hWndLv_, unsigned int cItems);
BOOL InsertListViewRow(HWND hWndLv_, unsigned int nRows, unsigned int nCols);
void HandleWM_NOTIFY(LPARAM lParam);

#endif

WndProc.cpp

#include "main.h"
#include <string>
#include <vector>
#include <stdexcept>

char szDialogContent[256] = {};
HIMAGELIST hImage = nullptr;

HWND hWndTest = nullptr;
char columnas[3][255] =
{
    {"Col 1"},
    {"Col 2"},
    {"Col 3"}
};

CLockManager manager("basedatos.txt");
std::vector<CLockers> lockers;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CREATE:
        {
            manager.GetLockers(lockers, CLockers::Filter::All);

            char szStringBuffer[256];
            LoadString(GetModuleHandle(nullptr), IDS_ASIGNAR, szStringBuffer, 255);
            HWND hWndBt = CreateButton(hWnd, szStringBuffer, 456, 11, ID_BUTTON_ASIGNAR);

            LoadString(GetModuleHandle(nullptr), IDS_TODOS, szStringBuffer, 255);
            HWND hWndBt2 = CreateButton(hWnd, szStringBuffer, 456, 41, ID_BUTTON_VER_TODOS, BS_SPLITBUTTON);

            LoadString(GetModuleHandle(nullptr), IDS_BUSCAR, szStringBuffer, 255);
            HWND hWndBt3 = CreateButton(hWnd, szStringBuffer, 456, 71, ID_BUTTON_BUSCAR);

            LoadString(GetModuleHandle(nullptr), IDS_ELIMINAR, szStringBuffer, 255);
            HWND hWndBt4 = CreateButton(hWnd, szStringBuffer, 456, 101, ID_BUTTON_ELIMINAR);

            LoadString(GetModuleHandle(nullptr), IDS_AGREGAR, szStringBuffer, 255);
            HWND hWndBt5 = CreateButton(hWnd, szStringBuffer, 456, 131, ID_BUTTON_AGREGAR);

            HWND hWndLv = CreateListView(hWnd);

            hImage = ImageList_Create(16, 16, ILC_COLOR32, 1, 1);

            HICON hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICONLOCKER));
            ImageList_AddIcon(hImage, hIcon);
            DestroyIcon(hIcon);

            hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICONDISABLED));
            ImageList_AddIcon(hImage, hIcon);
            DestroyIcon(hIcon);

            ListView_SetImageList(hWndLv, hImage, LVSIL_SMALL);

            BOOL ilvcResult = InsertListViewColumns(hWndLv, 5);
            assert(ilvcResult == TRUE);
            BOOL ilviResult = InsertListViewItem(hWndLv, lockers.size());

            assert(ilviResult == TRUE);

            EnumChildWindows(hWnd, SetFontProc, (LPARAM)GetStockObject(DEFAULT_GUI_FONT));
        }
            break;
        case WM_COMMAND:
        {
            if(HIWORD(wParam) == BN_CLICKED)
            {
                switch(LOWORD(wParam))
                {
                    case ID_BUTTON_VER_TODOS:
                    {

                    }
                    break;
                    case ID_MENU_VER_ASIGNADOS:
                    {

                    }
                    break;
                    case ID_MENU_VER_SIN_ASIGNAR:
                    {

                    }
                    break;
                    case ID_BUTTON_AGREGAR:
                    {

                    }
                    break;
                    case ID_BUTTON_BUSCAR:
                    {

                    }
                    break;
                    case ID_BUTTON_ASIGNAR:
                    {

                    }
                    break;
                    case ID_BUTTON_ELIMINAR:
                    {

                    }
                    break;
                }
            }
        }
            break;
        case WM_NOTIFY:
            switch( ((LPNMHDR)lParam)->code )
            {
                case LVN_GETDISPINFO:
                {
                    if( ((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hWnd, ID_LISTVIEW) )
                    {
                        HandleWM_NOTIFY(lParam);
                    }
                }
                break;
                case BCN_DROPDOWN:
                {
                    HWND hWndFiltro = GetDlgItem(hWnd, ID_BUTTON_VER_TODOS);

                    if( ((NMBCDROPDOWN*)lParam)->hdr.hwndFrom == hWndFiltro )
                    {
                        RECT rcButton;
                        GetClientRect(hWndFiltro, &rcButton);
                        POINT pt;
                        pt.x = rcButton.left;
                        pt.y = rcButton.bottom;
                        ClientToScreen(GetDlgItem(hWnd, ID_BUTTON_VER_TODOS), &pt);

                        // Create a menu and add items.
                        HMENU hSplitMenu = CreatePopupMenu();
                        char szStringBuffer[255];

                        LoadString(GetModuleHandle(nullptr), IDS_ASIGNADOS, szStringBuffer, 255);
                        AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VER_ASIGNADOS, szStringBuffer);

                        LoadString(GetModuleHandle(nullptr), IDS_SINASIGNAR, szStringBuffer, 255);
                        AppendMenu(hSplitMenu, MF_BYPOSITION, ID_MENU_VER_SIN_ASIGNAR, szStringBuffer);

                        // Display the menu.
                        TrackPopupMenu(hSplitMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION, pt.x, pt.y, 0, hWnd, NULL);
                    }
                }
                break;
            }
            break;
        case WM_DESTROY:
            ImageList_Destroy(hImage);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
            break;
    }

    return 0;
}

HWND CreateListView(HWND hWndParent)
{
    RECT rcClient = {};
    GetClientRect(hWndParent, &rcClient);

    HWND hWndLv_ = CreateWindowEx(0, WC_LISTVIEW,
            "", WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT  |LVS_ALIGNTOP | LVS_SHOWSELALWAYS,
            11,11, 438, 322,// rcClient.bottom - rcClient.top - 50,
            hWndParent, (HMENU)ID_LISTVIEW, GetModuleHandle(nullptr), nullptr);

    ListView_SetExtendedListViewStyle(hWndLv_, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    assert(hWndLv_ != nullptr);

    return hWndLv_;
}

HWND CreateButton(HWND hWndParent, const char* szBtnText, int x, int y, int id, DWORD dwStyle)
{
    HWND hWndBtn_ = CreateWindow("BUTTON", szBtnText,
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | dwStyle, x, y, 75, 23,
            hWndParent, (HMENU)(long long)id, GetModuleHandle(nullptr), nullptr);

    assert(hWndBtn_ != nullptr);

    return hWndBtn_;
}

BOOL InsertListViewColumns(HWND hWndLv_, int nColumns)
{
    LVCOLUMN lvc = {};
    int iCol;
    int wCols[] = {50, 250, 80, 20, 20};

    LoadString(GetModuleHandle(nullptr), IDS_LOCKER, columnas[0], 255);
    LoadString(GetModuleHandle(nullptr), IDS_USUARIO, columnas[1], 255);
    LoadString(GetModuleHandle(nullptr), IDS_FECHA, columnas[2], 255);

    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

    for(iCol = 0; iCol < nColumns; ++iCol)
    {
        if(iCol < 1)
        {
            lvc.fmt = LVCFMT_RIGHT;
        }
        else
        {
            lvc.fmt = LVCFMT_LEFT;
        }

        lvc.iSubItem = iCol;
        lvc.pszText = columnas[iCol];
        lvc.cx = wCols[iCol];

        if (ListView_InsertColumn(hWndLv_, iCol, &lvc) == -1)
            return FALSE;
    }

    return TRUE;
}

BOOL InsertListViewItem(HWND hWndLv_, unsigned int cItems)
{
    //HWND hWnd = GetParent(hWndLv_);
    LVITEM lvi = {0};

    lvi.pszText = LPSTR_TEXTCALLBACK;
    lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
    lvi.iSubItem = 0;
    lvi.stateMask = 0;
    lvi.state = 0;

    //SendMessage((HWND)hWnd, WM_SETREDRAW, (WPARAM)FALSE, 0);

    for(int i = 0; i < cItems; ++i)
    {
        lvi.iItem = i;

        if(!lockers.at(i).Enabled())
            lvi.iImage = 1;
        else
            lvi.iImage = 0;

        if(ListView_InsertItem(hWndLv_, &lvi) == -1)
        {
            return FALSE;
        }
    }

    return TRUE;
}

void HandleWM_NOTIFY(LPARAM lParam)
{
    NMLVDISPINFO* plvdi;

    switch ( ((LPNMHDR) lParam)->code )
    {
        case LVN_GETDISPINFO:
        {
            static constexpr size_t size = 256;
            plvdi = (NMLVDISPINFO*)lParam;

            switch(plvdi->item.iSubItem)
            {
            case 0:
            {
                std::string tmp = std::to_string( lockers.at(plvdi->item.iItem).GetLockerNumber());

                char buff[size];
                strncpy(buff, tmp.c_str(), size );
                plvdi->item.pszText = buff;
            }
                break;

            case 1:
            {
                std::string tmp = lockers.at(plvdi->item.iItem).GetAssignedUser();

                char buff[size];
                strncpy(buff, tmp.c_str(), size );
                plvdi->item.pszText = buff;
            }
                break;

            case 2:
            {
                std::string tmp = lockers.at(plvdi->item.iItem).GetDate();

                char buff[size];
                strncpy(buff, tmp.c_str(), size );
                plvdi->item.pszText = buff;
            }
                break;
            case 3:
            {
                char key[2][2] = {"N", "S"};
                plvdi->item.pszText = key[lockers.at(plvdi->item.iItem).HasKey()];
            }
                break;
            case 4:
            {
                char status[3][2] = {"B", "R", "M"};
                plvdi->item.pszText = status[lockers.at(plvdi->item.iItem).GetStatusInt()];
            }
                break;
            default:
                break;
            }
            break;
        }
    }
}

最佳答案

好的,我解决了。

尽管 MS 文档声明 WM_NOTIFY 不返回任何值“除了指定其他方式的通知消息”,LVN_GETDISPINFO 和 BCN_DROPDOWN 都不会返回任何值。

https://learn.microsoft.com/en-us/windows/win32/controls/wm-notify

https://learn.microsoft.com/en-us/windows/win32/controls/bcn-dropdown

https://learn.microsoft.com/en-us/windows/win32/controls/lvn-getdispinfo

所以,WM_NOTIFY 应该不会返回任何值,对吗?....好吧,只要我添加“return 0;”,一切都会正常进行。在 WM_NOTIFY 案例中。

现在一切都解决了:D 我希望这对其他人有用

这是唯一更改的代码: 在 MainWindow.cpp 上

case WM_NOTIFY:
{
    OnNotify(lParam);
    return 0;
}
break;

关于c++ - 如何修复ListView中的错误?封装控件时无法选择任何项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58277293/

相关文章:

vb.net - ListView右键单击VB.Net

c++ - 是否可以在键盘输入缓冲区中向前看并检测MFC/Win32中的条码输入?

c - 在跨 dll 边界访问导出的全局变量时检测缺少的 __declspec(dllimport)

delphi - 如何查询 "Size on disk"文件信息?

android - 当 listview 滚动时 edittext 设置默认值

c++ - 使用菜单按钮将 View 一分为二? (MFC)

c++ - 如何将 C++ 类函数作为回调传递

c++ - 向构造函数调用添加括号会导致 xlc C++ 编译器出现重复参数错误

c# - ListView 项 UWP 的替代颜色

c++ - C++ 中的 OpenCV 整数除法不符合预期