Handling ESC and Enter Key Events in MFC Dialogs
In MFC applications, pressing ESC or Enter closes a dialog by default. To prevent this, override CDialog::PreTranslateMessage() and intercept these key events before they reach the default handler.
Basic Implementation
BOOL CResultCollectorDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN) {
switch(pMsg->wParam) {
case VK_RETURN: // Enter key
return TRUE;
case VK_ESCAPE: // ESC key
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Returning TRUE tells the message loop the message has been handled and shouldn’t be processed further. Returning FALSE passes it up the chain to the base class.
Allow Enter in Multiline Edit Controls
Blocking both keys entirely is rarely what you need. More commonly, allow Enter in multiline text boxes while blocking ESC:
BOOL CResultCollectorDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN) {
switch(pMsg->wParam) {
case VK_RETURN: {
// Allow Enter only in multiline edit controls
HWND hFocus = GetFocus();
if(!hFocus) return FALSE;
WCHAR className[256] = {0};
GetClassName(hFocus, className, 256);
if(wcscmp(className, L"EDIT") == 0) {
// Check if this is a multiline edit
DWORD style = GetWindowLong(hFocus, GWL_STYLE);
if(style & ES_MULTILINE) {
return FALSE; // Let multiline edits handle Enter
}
}
return TRUE; // Block Enter in other controls
}
case VK_ESCAPE:
return TRUE; // Always block ESC
}
}
return CDialog::PreTranslateMessage(pMsg);
}
This checks the focused control’s class name and window style. For edit controls, it verifies the ES_MULTILINE flag to determine whether Enter should be allowed. For all other controls, Enter is blocked.
Route Enter to a Specific Button
Route Enter to trigger a custom button instead of closing the dialog:
BOOL CResultCollectorDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN) {
switch(pMsg->wParam) {
case VK_RETURN: {
// Trigger your custom button
CButton* pBtn = (CButton*)GetDlgItem(IDC_CUSTOM_BUTTON);
if(pBtn) {
pBtn->PostMessage(BM_CLICK);
return TRUE;
}
break;
}
case VK_ESCAPE:
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Use PostMessage(BM_CLICK) rather than SendMessage() for button clicks. Posting queues the message asynchronously, preventing potential reentrancy issues if your button handler modifies dialog state.
Control-Specific Key Handling
For complex scenarios, detect which control has focus and apply different rules per control:
BOOL CResultCollectorDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN) {
HWND hFocus = GetFocus();
if(!hFocus) return FALSE;
int controlID = GetDlgCtrlID(hFocus);
switch(pMsg->wParam) {
case VK_RETURN: {
// Allow Enter in IDC_SEARCH_EDIT and IDC_NOTES_EDIT
if(controlID == IDC_SEARCH_EDIT || controlID == IDC_NOTES_EDIT) {
return FALSE;
}
// For IDC_SINGLE_LINE_EDIT, trigger the Search button
if(controlID == IDC_SINGLE_LINE_EDIT) {
PostMessage(WM_COMMAND, IDC_SEARCH_BUTTON);
return TRUE;
}
// Block Enter everywhere else
return TRUE;
}
case VK_ESCAPE:
return TRUE; // Always block ESC
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Key Points
- Return TRUE: Message is consumed; not passed to the default handler
- Return FALSE: Message continues through the normal message chain
- Always call the base class method as a fallback for unhandled messages
WM_KEYDOWNfires when a key is pressed;WM_KEYUPfires when released- Use
GetFocus()to identify which control has keyboard focus for context-specific handling - Use
GetDlgCtrlID()to get a control’s resource ID without needing to cast or know the class type - Always null-check
GetFocus()before using its return value
UX Considerations
Blocking ESC entirely in modal dialogs may confuse users who expect it as a standard way to close the dialog. If you block ESC, either:
- Apply the block only to specific controls or scenarios
- Provide an explicit Cancel button as an alternative close mechanism
- Document this non-standard behavior in your UI guidelines
This PreTranslateMessage() approach is cleaner and more maintainable than modifying button styles or dialog properties, especially when you need fine-grained control over which keys work in different contexts.
