Я впервые создаю Win32 ComboBox. А у меня тут проблема.
При вызове CreateWindow
для ComboBox он снова вызывает функцию обратного вызова WndProc с сообщением WM_CREATE
, так что получается, что 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 без каких-либо дочерних элементов в нем.
Но это выглядело только как белое окно с рамкой, без кнопки со стрелкой или чего-либо еще на раскрывающейся странице, которую должен иметь ComboBox.
Этот рекурсивный случай никогда не случался, когда я создавал различные элементы управления, такие как кнопки, флажки, списки и т. д.
И созданный ComboBox тоже не выглядит так, как будто он имеет правильную форму.
Надеюсь, я просто пропустил что-то простое.
Код непонятен. Если вы хотите, чтобы люди помогали вам, вам нужно сделать так, чтобы им было легко увидеть вашу проблему. Однако это очень похоже на участника IOCCC и даже не соответствует категории в более известных формах программы . Пожалуйста, отредактируйте свой вопрос, чтобы исправить это.
Ответ работает для вас?
На самом деле вы вообще не создаете Win32 ComboBox. Вы регистрируете свой собственный класс с именем "ComboBox"
, а затем создаете окно этого класса, которое создает окно этого класса, которое создает окно этого класса и так далее рекурсивно.
Вам нужно изменить эту строку:
LPCTSTR lpszClass = L"ComboBox";
На другое уникальное имя, например "MyWindowClass"
.
С другой стороны, ваш цикл сообщений структурирован неправильно. Вместо этого это должно выглядеть так:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
И в вашем WndProc()
оператор return 0;
под обработчиком WM_COMMAND
находится не в том месте. Вместо этого его нужно переместить внутрь обработчика WM_COMMAND
:
case WM_COMMAND:
{
switch (...)
{
...
}
return 0; // <-- moved here
}
//return 0; // <-- from here
При вызове CreateWindow для ComboBox он вызывает WndProc. функция обратного вызова снова с сообщением WM_CREATE, так что происходит ComboBox снова и снова создает дочерний ComboBox. рекурсия.
Сообщение WM_CREATE отправляется оконной процедуре нового окна после создания окна. Эта строка WM_CREATE
генерирует ваше первое hwnd = CreateWindow()
сообщение. Затем вы создаете еще одно окно в первом сообщении WM_CREATE
, чтобы оно сгенерировало второе сообщение WM_CREATE
. Поскольку вы используете один и тот же зарегистрированный класс ("ComboBox"
/ "combobox"
, регистр не учитывается) для создания всех этих окон, все они используют одну и ту же оконную процедуру. Таким образом, вы получаете сообщение WM_CREATE
снова и снова, пока CreateWindow
не удастся создать окно и вернуть NULL
.
Но это было похоже только на белое окно с окантовкой, нет кнопки со стрелкой или чего-либо на раскрывающейся странице, которую ComboBox предполагается иметь.
Основная причина в том, что вы регистрируете класс с тем же именем, что и у существующего системного класса: "ComboBox"
/"combobox"
. Этот новый зарегистрированный класс переопределяет существующий. Это просто обычное окно вместо предопределенного элемента управления Combobox, как указал @RemyLebeau.
Приложение может зарегистрировать локальный класс приложения, имеющий тот же name как системный класс. Это заменяет системный класс в контексте приложения, но не мешает другим приложениям использовать системный класс.
См. «Как система находит класс окна».
Чтобы поле со списком отображалось в ожидаемой форме, вам нужно изменить lpszClass
на не предопределенный, например, как "SimpleComboBoxExample"
.
Предлагается использовать предопределенный макрос имени класса Combobox: WC_COMBOBOX вместо L"combobox"
.
Дополнительная ссылка: «Как создать простое поле со списком».
Поскольку ваше главное окно и дочернее окно имеют одно и то же имя класса, то же самое и wndproc.