在 Win32 中创建 ComboBox 无法正常工作
Creating ComboBox in Win32 not working properly
我是第一次创建 Win32 ComboBox。我这里有个问题。
当为 ComboBox 调用 CreateWindow
时,它使用 WM_CREATE
消息再次调用 WndProc 回调函数,所以发生的是 ComboBox 生成一个子 ComboBox,像递归一样一次又一次。
代码如下:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
结果如下:
我试图设置一些布尔标志来只执行一次 WM_CREATE
,它起作用了,我的意思是只创建一个没有任何子项的 ComboBox。
但是,它看起来只是一个带有边框标记的白色 window,没有箭头按钮或 ComboBox 应该具有的任何下拉页面。
当我创建按钮、复选框、列表框等不同的控件时,这种递归情况从未发生过
并且创建的 ComboBox 看起来也没有正确的形状。
希望我只是遗漏了一些简单的东西。
您实际上根本没有创建 Win32 ComboBox。您正在注册自己的名为 "ComboBox"
的 class,然后创建 class 的 window,这会创建 class 的 window,它创建了 class 的 window,递归地依此类推。
您需要更改此行:
LPCTSTR lpszClass = L"ComboBox";
到不同的唯一名称,例如 "MyWindowClass"
。
附带说明一下,您的消息循环结构错误。它应该看起来像这样:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
并且在您的 WndProc()
中,return 0;
语句 在 下方 WM_COMMAND
处理程序位于错误的位置。它需要移动 inside 到 WM_COMMAND
处理程序而不是:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here
When calling CreateWindow for the ComboBox, it calls the WndProc
callback function again with the WM_CREATE message, so what happens
is the ComboBox makes a child ComboBox, again and again like
recursion.
WM_CREATE
消息在window创建后被发送到新window的window过程。您的第一条 WM_CREATE
消息是由这一行 hwnd = CreateWindow()
生成的。然后在第一条 WM_CREATE
消息中创建另一个 window,因此它将生成第二条 WM_CREATE
消息。因为你用同一个注册的class("ComboBox"
/ "combobox"
,不区分大小写)创建所有这些windows,它们都使用同一个window 程序。所以你一次又一次地收到 WM_CREATE
消息,直到 CreateWindow
无法创建 window 和 return NULL
.
But, it just looked like only a white window with a border mark,
there's no arrow button or anything to dropdown page that the ComboBox
is supposed to have.
根本原因是您注册了一个与现有系统 class 同名的 class:"ComboBox"
/ "combobox"
。这个新注册的 class 覆盖了现有的。它只是一个常见的 window 而不是 @RemyLebeau 指出的预定义组合框控件。
An application can register an application local class having the same
name as a system class. This replaces the system class in the context
of the application but does not prevent other applications from using
the system class.
参考“How the System Locates a Window Class”。
要使 Combobox 以预期的形状显示,您需要做的是将 lpszClass
更改为非预定义的,例如 "SimpleComboBoxExample"
.
建议使用Combobox Class Name: WC_COMBOBOX
的预定义宏,而不是L"combobox"
.
更多参考:“How to Create a Simple Combo Box”。
我是第一次创建 Win32 ComboBox。我这里有个问题。
当为 ComboBox 调用 CreateWindow
时,它使用 WM_CREATE
消息再次调用 WndProc 回调函数,所以发生的是 ComboBox 生成一个子 ComboBox,像递归一样一次又一次。
代码如下:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
结果如下:
我试图设置一些布尔标志来只执行一次 WM_CREATE
,它起作用了,我的意思是只创建一个没有任何子项的 ComboBox。
但是,它看起来只是一个带有边框标记的白色 window,没有箭头按钮或 ComboBox 应该具有的任何下拉页面。
当我创建按钮、复选框、列表框等不同的控件时,这种递归情况从未发生过
并且创建的 ComboBox 看起来也没有正确的形状。
希望我只是遗漏了一些简单的东西。
您实际上根本没有创建 Win32 ComboBox。您正在注册自己的名为 "ComboBox"
的 class,然后创建 class 的 window,这会创建 class 的 window,它创建了 class 的 window,递归地依此类推。
您需要更改此行:
LPCTSTR lpszClass = L"ComboBox";
到不同的唯一名称,例如 "MyWindowClass"
。
附带说明一下,您的消息循环结构错误。它应该看起来像这样:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
并且在您的 WndProc()
中,return 0;
语句 在 下方 WM_COMMAND
处理程序位于错误的位置。它需要移动 inside 到 WM_COMMAND
处理程序而不是:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here
When calling CreateWindow for the ComboBox, it calls the WndProc callback function again with the WM_CREATE message, so what happens is the ComboBox makes a child ComboBox, again and again like recursion.
WM_CREATE
消息在window创建后被发送到新window的window过程。您的第一条 WM_CREATE
消息是由这一行 hwnd = CreateWindow()
生成的。然后在第一条 WM_CREATE
消息中创建另一个 window,因此它将生成第二条 WM_CREATE
消息。因为你用同一个注册的class("ComboBox"
/ "combobox"
,不区分大小写)创建所有这些windows,它们都使用同一个window 程序。所以你一次又一次地收到 WM_CREATE
消息,直到 CreateWindow
无法创建 window 和 return NULL
.
But, it just looked like only a white window with a border mark, there's no arrow button or anything to dropdown page that the ComboBox is supposed to have.
根本原因是您注册了一个与现有系统 class 同名的 class:"ComboBox"
/ "combobox"
。这个新注册的 class 覆盖了现有的。它只是一个常见的 window 而不是 @RemyLebeau 指出的预定义组合框控件。
An application can register an application local class having the same name as a system class. This replaces the system class in the context of the application but does not prevent other applications from using the system class.
参考“How the System Locates a Window Class”。
要使 Combobox 以预期的形状显示,您需要做的是将 lpszClass
更改为非预定义的,例如 "SimpleComboBoxExample"
.
建议使用Combobox Class Name: WC_COMBOBOX
的预定义宏,而不是L"combobox"
.
更多参考:“How to Create a Simple Combo Box”。