如何在使用 CreateWindow 创建的窗口中设置默认按钮,而不是在 Windows 中设置对话框资源?

How can I set a default button in a window created with CreateWindow instead of a dialog resource in Windows?

提问人:CristhianDev 提问时间:11/7/2023 最后编辑:CristhianDev 更新时间:11/7/2023 访问量:129

问:

我正在开发一个 Windows 应用程序,我需要使用该函数创建一个自定义窗口。但是,我注意到当我使用对话框资源时,我可以设置默认按钮,但是当我使用 动态创建窗口时,我不确定如何做到这一点。CreateWindowCreateWindow

有没有办法在不依赖对话框资源的情况下在使用 CreateWindow 创建的窗口中设置默认按钮?如果是这样,实现这一目标的最佳方法是什么?我想确保默认按钮突出显示,并且可以在窗口处于焦点时通过“Enter”键激活。

我一直在网上进行广泛的研究,但我无法找到任何关于如何处理这种特定情况的明确信息。以下是我的代码摘录:main.cpp

#include <Windows.h>
#include <CommCtrl.h>

#define IDC_BUTTON1 1001
#define IDC_BUTTON2 1002

LRESULT CALLBACK MainWindowProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
    WNDCLASSEXA wcex = { };

    wcex.cbSize = sizeof(WNDCLASSEXA);
    wcex.lpfnWndProc = MainWindowProc;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIconA(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursorA(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wcex.lpszClassName = "MainWindowClass";

    RegisterClassExA(&wcex);

    HWND hWnd = CreateWindowExA(0, "MainWindowClass", "", WS_OVERLAPPEDWINDOW, 0, 0, 416, 228, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, SW_NORMAL);

    MSG msg = { };

    while (GetMessageA(&msg, NULL, 0, 0) > 0)
    {
        if (!IsDialogMessageA(hWnd, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessageA(&msg);
        }
    }

    return 0;
}

LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CREATE:
        {
            HFONT hFont = CreateFontA(13, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "MS Shell Dlg 2");

            HWND hWndEdit1 = CreateWindowExA(WS_EX_CLIENTEDGE, WC_EDIT, "", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 20, 200, 21, hWnd, NULL, NULL, NULL);
            HWND hWndEdit2 = CreateWindowExA(WS_EX_CLIENTEDGE, WC_EDIT, "", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 100, 60, 200, 21, hWnd, NULL, NULL, NULL);

            HWND hWndButton1 = CreateWindowExA(0, WC_BUTTON, "Button 1", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 122, 120, 75, 23, hWnd, (HMENU)IDC_BUTTON1, NULL, NULL);
            HWND hWndButton2 = CreateWindowExA(0, WC_BUTTON, "Button 2", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 203, 120, 75, 23, hWnd, (HMENU)IDC_BUTTON2, NULL, NULL);

            SendMessageA(hWndEdit1, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE);
            SendMessageA(hWndEdit2, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE);
            SendMessageA(hWndButton1, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE);
            SendMessageA(hWndButton2, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE);

            SendMessageA(hWndEdit1, EM_SETMARGINS, (WPARAM)EC_LEFTMARGIN, (LPARAM)0);
            SendMessageA(hWndEdit2, EM_SETMARGINS, (WPARAM)EC_LEFTMARGIN, (LPARAM)0);

            SendMessageA(hWnd, DM_SETDEFID, (WPARAM)IDC_BUTTON1, (LPARAM)0);

            SetFocus(hWndEdit1); // Need focus edit control, no default button
        } break;

        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    MessageBoxA(hWnd, "OK", "Title", MB_OK);
                } break;

                case IDCANCEL:
                {
                    MessageBoxA(hWnd, "Cancel", "Title", MB_OK);
                } break;

                case IDC_BUTTON1:
                {
                    MessageBoxA(hWnd, "BUTTON 1", "Title", MB_OK);
                } break;

                case IDC_BUTTON2:
                {
                    MessageBoxA(hWnd, "BUTTON 2", "Title", MB_OK);
                } break;
            }
        } break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

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

    return 0;
}

我尝试使用 ,但我找不到使它按预期工作的方法。似乎我可能需要手动处理 和 事件,但我不确定如何进行此操作以确保功能正常。DM_SETDEFIDDM_SETDEFIDDM_GETDEFID

我正在寻找有关如何有效管理 和 事件或任何其他推荐技术的指导,这些技术将使我能够在 Windows 应用程序中为使用 CreateWindow 创建的自定义窗口设置默认按钮。DM_SETDEFIDDM_GETDEFID

c winapi

评论

3赞 i486 11/7/2023
您需要 call 而不是 .DefDlgProcDefWindowProc
0赞 IInspectable 11/9/2023
@i486 是的,确实如此。如果目标是损坏内存。有问题的代码创建一个常规窗口,该窗口没有为对话框过程分配额外的窗口内存来存储其返回值。从非对话窗口过程调用具有未定义的行为。这也记录在案:“在所有情况下,自定义对话框类的 WNDCLASScbWndExtra 成员必须至少设置为 DLGWINDOWEXTRADWLP_MSGRESULTDefDlgProc

答:

1赞 Remy Lebeau 11/7/2023 #1

BS_DEFPUSHBUTTON窗口样式指定给默认按钮:

创建一个按钮,其行为类似于BS_PUSHBUTTON样式的按钮,但具有独特的外观。如果按钮位于对话框中,则用户可以通过按 Enter 键选择该按钮,即使该按钮没有输入焦点也是如此。此样式可用于使用户能够快速选择最可能(默认)选项。

此外,请确保在显示窗口时,默认按钮具有初始输入焦点。而且,您可能需要向窗口发送一条消息,以便知道按下时要发送哪个控件 ID,否则它将根据 MSDN 的对话框键盘界面文档发送:DM_SETDEFIDIsDialogMessageA()EnterIDOK

系统为对话框提供了一个特殊的键盘界面,该界面对多个键进行特殊处理。该接口生成与对话框中的某些按钮相对应的消息,或将输入焦点从一个控件更改为另一个控件。以下是此接口中使用的键及其各自的操作。

钥匙 行动
... ...
进入 向对话框过程发送消息。该参数设置为默认按钮的控制标识符。WM_COMMANDwParamIDOK
... ...

系统会自动为所有模式对话框提供键盘界面。它不提供无模式对话框的接口,除非应用程序调用 IsDialogMessage 函数来筛选其主消息循环中的消息。这意味着应用程序必须在从消息队列中检索消息后立即将消息传递到。如果该函数用于对话框,则该函数处理消息,并返回一个非零值,以指示消息已处理,不得传递给 or 函数。IsDialogMessageTranslateMessageDispatchMessage

评论

0赞 CristhianDev 11/7/2023
我试了一下,虽然它提供了视觉效果,但它不符合功能要求。当我按下文本字段中的键时,它会触发带有 的警报,但我需要它来触发 。此外,一旦我使用键改变焦点,样式似乎就丢失了EnterIDOKBUTTON 1Tab
0赞 Remy Lebeau 11/7/2023
这是在消息循环中使用引入的完全正常的行为。请参阅对话框键盘界面返回键为带焦点的按钮生成 IDOK。不过,你没有展示你是如何尝试使用的。但我认为你不需要那个。我之前已经制作了 Win32 GUI,它具有默认按钮,无需使用 .IsDialogMessage()DM_SETDEFIDDM_SETDEFID
0赞 CristhianDev 11/7/2023
我在使用 BS_DEFPUSHBUTTON 时遇到的问题如下:当我按下该键并且按钮第一次获得焦点时,它会失去特征性的蓝色边框,而是显示黑色虚线边框。我不确定这是否是一个错误。此外,当我继续使用按键在控件之间转移焦点时,它似乎失去了其独特的风格。TABdefault buttonTAB
0赞 CristhianDev 11/7/2023
这些问题不会出现在资源对话框中。您提到您之前已经成功实现了默认按钮。您能否分享有关您如何实现这一目标的代码?我认为使用 和 消息可能是解决我遇到的错误的关键。我找到了Raymond Chen评论的答案,建议这种方法,但不幸的是,没有附加代码来说明解决方案。DM_SETDEFIDDM_GETDEFID
0赞 Remy Lebeau 11/7/2023
确保默认按钮具有初始焦点,如果未设置窗口的默认 ID,则只需让处理程序调用默认按钮的操作即可。但同样,我建议你编辑你的问题,以显示你如何尝试使用对你不起作用的东西IDOKDM_SETDEFID