简体   繁体   English

如何防止 MFC 对话框在 Enter 和 Escape 键上关闭?

[英]How to prevent MFC dialog closing on Enter and Escape keys?

I know one method of preventing an MFC dialog from closing when the Enter or Esc keys are pressed, but I'd like to know more details of the process and all the common alternative methods for doing so.我知道一种防止 MFC 对话框在按下EnterEsc键时关闭的方法,但我想了解该过程的更多详细信息以及执行此操作的所有常用替代方法。

Thanks in advance for any help.在此先感谢您的帮助。

When the user presses Enter key in a dialog two things can happen:当用户在对话框中按下 Enter 键时,会发生两种情况:

  1. The dialog has a default control (see CDialog::SetDefID() ).该对话框有一个默认控件(请参阅CDialog::SetDefID() )。 Then a WM_COMMAND with the ID of this control is sent to the dialog.然后将带有此控件 ID 的 WM_COMMAND 发送到对话框。
  2. The dialog does not have a default control.该对话框没有默认控件。 Then WM_COMMAND with ID = IDOK is sent to the dialog.然后将 ID = IDOK 的 WM_COMMAND 发送到对话框。

With the first option, it may happen that the default control has a ID equal to IDOK.对于第一个选项,可能会发生默认控件的 ID 等于 IDOK。 Then the results will be the same that in the second option.那么结果将与第二个选项中的结果相同。

By default, class CDialog has a handler for the WM_COMMAND(IDOK) that is to call to CDialog::OnOk() , that is a virtual function, and by default it calls EndDialog(IDOK) that closes the dialog.默认情况下,类CDialog有一个用于调用CDialog::OnOk()WM_COMMAND(IDOK)处理程序,这是一个虚函数,默认情况下它调用EndDialog(IDOK)来关闭对话框。

So, if you want to avoid the dialog being closed, do one of the following.因此,如果您想避免关闭对话框,请执行以下操作之一。

  1. Set the default control to other than IDOK .将默认控件设置为IDOK以外的其他控件。
  2. Set a handler to the WM_COMMAND(IDOK) that does not call EndDialog() .将处理程序设置为不调用EndDialog()WM_COMMAND(IDOK) EndDialog()
  3. Override CDialog::OnOk() and do not call the base implementation.覆盖CDialog::OnOk()并且不调用基本实现。

About IDCANCEL, it is similar but there is not equivalent SetDefID() and the ESC key is hardcoded.关于 IDCANCEL,它是类似的,但没有等效的SetDefID()并且 ESC 键是硬编码的。 So to avoid the dialog being closed:所以为了避免对话框被关闭:

  1. Set a handler to the WM_COMMAND(IDCANCEL) that does not call EndDialog() .将处理程序设置为不调用EndDialog()WM_COMMAND(IDCANCEL) EndDialog()
  2. Override CDialog::OnCancel() and do not call the base implementation.覆盖CDialog::OnCancel()并且不调用基本实现。

There is an alternative to the previous answer, which is useful if you wish to still have an OK / Close button.上一个答案有一个替代方案,如果您希望仍然有一个确定/关闭按钮,这将很有用。 If you override the PreTranslateMessage function, you can catch the use of VK_ESCAPE / VK_RETURN like so:如果您覆盖 PreTranslateMessage 函数,您可以像这样捕获 VK_ESCAPE / VK_RETURN 的使用:

BOOL MyCtrl::PreTranslateMessage(MSG* pMsg)
{
    if( pMsg->message == WM_KEYDOWN )
    {
        if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
        {
            return TRUE;                // Do not process further
        }
    }

    return CWnd::PreTranslateMessage(pMsg);
}

The answer of @the-forest-and-the-trees is quite good. @the-forest-and-the-trees 的回答很好。 Except one situation which was addressed by @oneworld.除了@oneworld 解决的一种情况。 You need to filter messages which are not for dialog window:您需要过滤不是对话窗口的消息:

BOOL CDialogDemoDlg::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->hwnd == this->m_hWnd && pMsg->message == WM_KEYDOWN)
    {
        if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
        {
            return TRUE;                // Do not process further
        }
    }
    return CWnd::PreTranslateMessage(pMsg);
}

Remember to add virtual in the header file.记得在头文件中添加virtual

When dealing with Dialog style MFC applications, the framework automatically hard codes a few items that must be overridden to prevent the application from quitting when the ESC or Enter keys are pressed.在处理 Dialog 样式的 MFC 应用程序时,框架会自动硬编码一些必须覆盖的项目,以防止应用程序在按下 ESC 或 Enter 键时退出。 But there is a very simple way that doesn't require anything special such as implementing PreTranslateMessage() which is very much not recommend.但是有一种非常简单的方法不需要任何特殊的东西,例如实现非常不推荐的 PreTranslateMessage()。

There are three functions that need to be in place:有三个功能需要到位:

  1. The OnCancel() function to override the base class version and not to call it. OnCancel() 函数覆盖基类版本而不是调用它。 This prevents the ESC key from closing the app.这可以防止 ESC 键关闭应用程序。
  2. The OnOK() function to override the base class version and not to call the base class. OnOK() 函数覆盖基类版本而不是调用基类。 This prevents the Enter key from closing the app.这可以防止 Enter 键关闭应用程序。
  3. Because you've now prevented the dialog window from being closed you must now implement the OnClose() event handler.由于您现在已阻止关闭对话框窗口,因此您现在必须实现 OnClose() 事件处理程序。 This function handler will handle when the windows "X" button or the system command Close Alt+F4 are clicked.当 Windows 的“X”按钮或系统命令 Close Alt+F4 被单击时,此函数处理程序将进行处理。 Now in order to close the application, you then call the base class version of one of the other functions OnOK(), OnCancel() if desired, to actually close the app.现在为了关闭应用程序,如果需要,您可以调用其他函数 OnOK()、OnCancel() 之一的基类版本,以实际关闭应用程序。 At this point you now have full control of how the app is closed.此时,您现在可以完全控制应用程序的关闭方式。

Step 1第1步

In the header, add the three function prototypes.在标题中,添加三个函数原型。 You can use the Class Wizard if you like to add the WM_CLOSE event handler but it's super simple to just type it in.如果您想添加 WM_CLOSE 事件处理程序,您可以使用类向导,但只需输入它就非常简单。

// DefaultDialogAppDlg.h
//

class CDefaultDialogAppDlg : public CDialogEx
{
    // ... other code
  
protected:
    virtual void OnCancel(){}    // inline empty function
    virtual void OnOK(){}        // inline empty function
public:
    afx_msg void OnClose();      // message handler for WM_CLOSE

    // ...other code
};

Step 2第2步

In the .cpp file, add the ON_WM_CLOSE() entry to the message map and the definitions for the three functions.在 .cpp 文件中,将 ON_WM_CLOSE() 条目添加到消息映射和三个函数的定义中。 Since OnCancel() and OnOK() are generally going to be empty, you could just inline them in the header if you want (see what I did in Step 1?).由于 OnCancel() 和 OnOK() 通常为空,如果需要,您可以将它们内联到标头中(看看我在第 1 步中做了什么?)。

The .cpp file will have something like this: .cpp 文件将包含以下内容:

// DefaultDialogAppDlg.cpp

// ... other code

BEGIN_MESSAGE_MAP(CDefaultDialogAppDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_CLOSE()        // WM_CLOSE messages are handled here.
END_MESSAGE_MAP()

// ... other code

void CDefaultDialogAppDlg::OnClose()
{
    // TODO: Add exit handling code here
    // NOTE: to actually allow the program to end, call the base class
    // version of either the OnOK() or OnCancel() function.
    
    //CDialogEx::OnOK();      // returns 1 to theApp object
    CDialogEx::OnCancel();    // returns 2 to theApp object
}

I simply override the OnOk event and instead of passing the message to the parent dialog, do nothing.我只是覆盖 OnOk 事件,而不是将消息传递给父对话框,什么都不做。
So it's basically simple as doing so:所以这样做基本上很简单:

void OnOk() override { /*CDialog::OnOK();*/ }

This should prevent the dialog from closing when pressing the return/enter key.这应该可以防止对话框在按下回车/回车键时关闭。

Make sure you don't #define CUSTOM_ID 2 because 2 is already defined for escape and I think 1 is defined for enter?确保你没有#define CUSTOM_ID 2因为2已经定义为转义而我认为1是为输入定义的? Correct me if i'm wrong.如果我错了,请纠正我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM