我正在 win32api 上开发储物柜管理器应用程序(已在控制台上完成)。第一个构建是在“win32 普通样式代码”中(函数定义和声明,没有对象)。这是一个 ListView ,用于显示与之交互的项目和按钮。
现在,这个版本就像一个魅力,我通过 LVN_GETDISPINFO
通知消息填充 ListView ,我还有一个拆分按钮来处理 BCN_DROPDOWN
通知消息。
在我看到它工作后,我想让它封装窗口和控件,并为 ListView 和按钮控件制作简单的包装器。
我以相同的方式处理相同的消息(LVN_GETDISPINFO
和 BCN_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/