在无模式对话框中阻止 ESC 和 Enter 键(Win32,非 MFC)
Block ESC and Enter keys in modeless dialog box (Win32, non-MFC)
有一些关于这个主题的文章,但其中 none 对我有用。我正在使用 Win32(无 MFC)编写以下内容。目标是防止 ESC
或 ENTER
键关闭无模式对话框。
这是对话框模板:
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;
}
这在大多数情况下都有效。除了这个。
顺序如下。将焦点放在多行编辑框中,然后按任意 letter/number 键,然后按 ESC
:
然后它会关闭对话框。
知道它如何通过我上面的覆盖代码吗?
PS。有趣的观察。
1) 如果我先点击 ESC
,我的代码就会捕获它。只有当我按下其他键然后 ESC
它才会失败。
2) 如果我注释掉调用 IsDialogMessage
的行(以及随后的 continue
),它将停止接受 ESC
。所以我猜这不是编辑控件做的。
IsDialogMessage
将 ESC 键转换为 WM_COMMAND IDCANCEL
并将 ENTER 转换为 WM_COMMAND IDOK
。要取消默认处理(关闭对话框),请在您的对话框过程中处理它们:
switch (message)
{
case WM_CLOSE:
// Handle WM_CLOSE here so it wouldn't generate WM_COMMAND IDCANCEL
// that would be ignored in WM_COMMAND handler.
DestroyWindow(hDlg);
return TRUE;
case WM_COMMAND:
if ( LOWORD(wParam) == IDCANCEL || LOWORD(wParam) == IDOK )
// Prevent default handling by original dialog procedure.
return TRUE;
break;
// other cases...
}
如果我们只想通过单击系统菜单中的关闭 X
按钮(或通过 ALT+F4
)关闭对话框并通过 ESC
和 ENTER
键禁用关闭 -我们需要的一切 - 在处理 (WM_SYSCOMMAND, SC_CLOSE)
时调用 DestroyWindow
并且在 (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;
....
}
}
....
}
RbMm 有很好的解决办法。所以我会把它标记为答案。
在等待回复的过程中,我能够调整我原来的消息循环并想出我自己的解决方案。原来如此。
屏蔽 Enter
键很容易。我需要做的就是定义一个默认按钮(在 VS 的对话框编辑器中,或通过发送 DM_SETDEFID
消息),它将处理所有 Enter
击键。
阻止ESC
击键的要点是不将带有ESC
击键的任何键盘消息传递给任何公共控件(或对话框window的子控件)作为@IInspectable quoted in the comments,其中一些常用控件很旧,未按照规范实现。此外,Microsoft 通常不会修复旧的 UI 错误,而只是将它们称为 功能 。
所以我通过以下修改完成了修复,该修改将重新路由(或反映)所有此类消息到我的DlgProc
,这也比RbMm 的代码还允许我对 ESC
击键进行自己的处理。
也为 goto-purists 消除了 goto
:
MSG msg;
// Main message loop:
for(int nR; nR = ::GetMessage(&msg, nullptr, 0, 0);)
{
if(nR == -1)
{
//Error
ASSERT(NULL);
break;
}
//Need special processing for modeless dialogs
if(ghActiveModelessDlg)
{
//Try to catch ESC keystrokes
if(
((msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) && msg.wParam == VK_ESCAPE) ||
(msg.message == WM_CHAR && msg.wParam == 27)
)
{
//Was this message sent to the dialog window?
if(ghActiveModelessDlg != msg.hwnd)
{
//If no, then reflect it to our dialog window
::PostMessage(ghActiveModelessDlg, msg.message, msg.wParam, msg.lParam);
continue;
}
}
else
{
//Dialog's special message-processing
if(::IsDialogMessage(ghActiveModelessDlg, &msg))
{
continue;
}
}
}
//Regular processing
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
有一些关于这个主题的文章,但其中 none 对我有用。我正在使用 Win32(无 MFC)编写以下内容。目标是防止 ESC
或 ENTER
键关闭无模式对话框。
这是对话框模板:
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;
}
这在大多数情况下都有效。除了这个。
顺序如下。将焦点放在多行编辑框中,然后按任意 letter/number 键,然后按 ESC
:
然后它会关闭对话框。
知道它如何通过我上面的覆盖代码吗?
PS。有趣的观察。
1) 如果我先点击 ESC
,我的代码就会捕获它。只有当我按下其他键然后 ESC
它才会失败。
2) 如果我注释掉调用 IsDialogMessage
的行(以及随后的 continue
),它将停止接受 ESC
。所以我猜这不是编辑控件做的。
IsDialogMessage
将 ESC 键转换为 WM_COMMAND IDCANCEL
并将 ENTER 转换为 WM_COMMAND IDOK
。要取消默认处理(关闭对话框),请在您的对话框过程中处理它们:
switch (message)
{
case WM_CLOSE:
// Handle WM_CLOSE here so it wouldn't generate WM_COMMAND IDCANCEL
// that would be ignored in WM_COMMAND handler.
DestroyWindow(hDlg);
return TRUE;
case WM_COMMAND:
if ( LOWORD(wParam) == IDCANCEL || LOWORD(wParam) == IDOK )
// Prevent default handling by original dialog procedure.
return TRUE;
break;
// other cases...
}
如果我们只想通过单击系统菜单中的关闭 X
按钮(或通过 ALT+F4
)关闭对话框并通过 ESC
和 ENTER
键禁用关闭 -我们需要的一切 - 在处理 (WM_SYSCOMMAND, SC_CLOSE)
时调用 DestroyWindow
并且在 (WM_COMMAND, IDCANCEL, IDOK)
上不执行任何操作。我们不需要特殊的消息循环或子类化任何控件。并且在对话框
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;
....
}
}
....
}
RbMm 有很好的解决办法。所以我会把它标记为答案。
在等待回复的过程中,我能够调整我原来的消息循环并想出我自己的解决方案。原来如此。
屏蔽 Enter
键很容易。我需要做的就是定义一个默认按钮(在 VS 的对话框编辑器中,或通过发送 DM_SETDEFID
消息),它将处理所有 Enter
击键。
阻止ESC
击键的要点是不将带有ESC
击键的任何键盘消息传递给任何公共控件(或对话框window的子控件)作为@IInspectable quoted in the comments,其中一些常用控件很旧,未按照规范实现。此外,Microsoft 通常不会修复旧的 UI 错误,而只是将它们称为 功能 。
所以我通过以下修改完成了修复,该修改将重新路由(或反映)所有此类消息到我的DlgProc
,这也比RbMm 的代码还允许我对 ESC
击键进行自己的处理。
也为 goto-purists 消除了 goto
:
MSG msg;
// Main message loop:
for(int nR; nR = ::GetMessage(&msg, nullptr, 0, 0);)
{
if(nR == -1)
{
//Error
ASSERT(NULL);
break;
}
//Need special processing for modeless dialogs
if(ghActiveModelessDlg)
{
//Try to catch ESC keystrokes
if(
((msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) && msg.wParam == VK_ESCAPE) ||
(msg.message == WM_CHAR && msg.wParam == 27)
)
{
//Was this message sent to the dialog window?
if(ghActiveModelessDlg != msg.hwnd)
{
//If no, then reflect it to our dialog window
::PostMessage(ghActiveModelessDlg, msg.message, msg.wParam, msg.lParam);
continue;
}
}
else
{
//Dialog's special message-processing
if(::IsDialogMessage(ghActiveModelessDlg, &msg))
{
continue;
}
}
}
//Regular processing
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}