Создание ComboBox в Win32 не работает должным образом

Я впервые создаю 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 тоже не выглядит так, как будто он имеет правильную форму.

Надеюсь, я просто пропустил что-то простое.

Поскольку ваше главное окно и дочернее окно имеют одно и то же имя класса, то же самое и wndproc.

user2120666 13.12.2020 07:21

Код непонятен. Если вы хотите, чтобы люди помогали вам, вам нужно сделать так, чтобы им было легко увидеть вашу проблему. Однако это очень похоже на участника IOCCC и даже не соответствует категории в более известных формах программы . Пожалуйста, отредактируйте свой вопрос, чтобы исправить это.

IInspectable 13.12.2020 10:05

Ответ работает для вас?

Rita Han 28.12.2020 10:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
394
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

На самом деле вы вообще не создаете 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".

Дополнительная ссылка: «Как создать простое поле со списком».

Другие вопросы по теме