首页 > 解决方案 > 在无模式对话框中阻止 ESC 和 Enter 键(Win32,非 MFC)

问题描述

有一些关于这个主题的文章,但没有一篇对我有用。我正在使用 Win32(无 MFC)编写以下内容。目标是防止ESCENTER键关闭无模式对话框。

这是对话框模板:

IDD_DIALOG_1 DIALOGEX 0, 0, 345, 179
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION ""
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    CONTROL         "New Pt",IDC_CHECK_NEW_PT,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,7,3,39,12
    CONTROL         "Lines",IDC_CHECK_LINES,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,54,3,39,12
    CONTROL         "Curves",IDC_CHECK_CURVES,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,94,3,39,12
    CONTROL         "Ellipses",IDC_CHECK_ELLIPSE,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,134,3,39,12
    CONTROL         "Circles",IDC_CHECK_CIRCLE,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,174,3,39,12
    LTEXT           "Pen Size:",IDC_STATIC,242,7,30,8
    EDITTEXT        IDC_EDIT_PEN_SIZE,275,3,40,14,ES_CENTER | ES_AUTOHSCROLL | ES_NUMBER
    CONTROL         "",IDC_SPIN_PEN_SIZE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,316,3,11,14
    EDITTEXT        IDC_EDIT_SRC,7,19,331,106,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL
END

为了捕获这两个键,我将消息循环更改为:

MSG msg;

// Main message loop:
for(int nR;;)
{
    nR = ::GetMessage(&msg, nullptr, 0, 0);
    if(!nR)
    {
        break;
    }
    else if(nR == -1)
    {
        //Error
        ASSERT(NULL);
        break;
    }

    if(ghActiveModelessDlg)
    {
        BOOL bProcessAsDlgMsg = TRUE;

        if(msg.message == WM_KEYDOWN ||
            msg.message == WM_KEYUP)
        {
            //Try to trap ESC & Enter keys
            if(msg.wParam == VK_ESCAPE)
            {
                //Do not process
                bProcessAsDlgMsg = FALSE;
            }
            else if(msg.wParam == VK_RETURN)
                goto lbl_check_enter;
        }
        else if(msg.message == WM_CHAR)
        {
            //Try to trap ESC & Enter key
            if(msg.wParam == 27)
            {
                //ESC - Do not process
                bProcessAsDlgMsg = FALSE;
            }
            else if(msg.wParam == '\r')
            {
lbl_check_enter:
                //See what window is it
                WCHAR buffClass[256];
                if(::GetClassName(msg.hwnd, buffClass, _countof(buffClass)) &&
                    lstrcmpi(buffClass, L"edit") == 0 &&
                    (::GetWindowLongPtr(msg.hwnd, GWL_STYLE) & ES_WANTRETURN))
                {
                    //This is edit ctrl that can handle its own Enter keystroke
                }
                else
                {
                    //Do not process
                    bProcessAsDlgMsg = FALSE;
                }
            }
        }

        if(bProcessAsDlgMsg)
        {
            if(::IsDialogMessage(ghActiveModelessDlg, &msg))
            {
                continue;
            }
        }
    }

    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

并且ghActiveModelessDlg是从内部DlgProc为无模式对话框设置的:

INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(hDlg)
    {
        //...

        case WM_ACTIVATE:
        {
            //Needed to ensure that keyboard shortcuts are properly processed in the message loop
            ghActiveModelessDlg = wParam != WA_INACTIVE ? hDlg : NULL;
        }
        break;
    }

    return 0;
}

这工作......在大多数情况下。除了这个。

这是顺序。将焦点放在多行编辑框中,然后按任意字母/数字键,然后ESC

在此处输入图像描述

然后它将关闭对话框。

知道如何通过上面的覆盖代码吗?

PS。有趣的观察。

1)如果我先打ESC,我的代码会捕获它。只有当我按下其他键然后ESC它失败时。

2)如果我注释掉调用IsDialogMessage(和后续continue)它停止接受的行ESC。所以我的猜测是不是编辑控件执行此操作。

标签: c++winapimodeless-dialog

解决方案


如果我们只想通过单击X系统菜单(或 by ALT+F4)中的关闭按钮来关闭对话框并禁用关闭ESCENTER键 - 所有我们需要的 -DestroyWindow在处理时调用(WM_SYSCOMMAND, SC_CLOSE)并且什么也不做(WM_COMMAND, IDCANCEL, IDOK)。我们不需要特殊的消息循环或包含任何控件。并且在对话框中没有带有 IDOK/IDCANCEL id 的按钮

INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

    switch (uMsg)
    {
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_CLOSE) DestroyWindow(hwndDlg);
        break;
    case WM_COMMAND:
        switch (wParam)
        {
        case MAKEWPARAM(IDOK, BN_CLICKED):
        case MAKEWPARAM(IDCANCEL, BN_CLICKED):
            // ignore this
            break;
        ....
        }
    }
    ....
}

推荐阅读