【问题标题】:How to prevent MFC dialog closing on Enter and Escape keys?如何防止 MFC 对话框在 Enter 和 Escape 键上关闭?
【发布时间】:2013-07-23 14:08:44
【问题描述】:

我知道在按下 EnterEsc 键时防止 MFC 对话框关闭的一种方法,但我想了解更多有关该过程的详细信息和这样做的所有常见替代方法。

提前感谢您的帮助。

【问题讨论】:

  • 鉴于有两个投票率很高的答案似乎解决了这个问题,我不确定这是什么被关闭为不清楚。
  • 对于那些没有线索的人来说,以投票方式结束不清楚的问题,这方式太容易了。

标签: c++ mfc


【解决方案1】:

在处理 Dialog 风格的 MFC 应用程序时,框架会自动对一些必须重写的项目进行硬编码,以防止应用程序在 EscEnter 键按下时退出按下。但是有一种非常简单的方法,不需要任何特殊的东西,例如实现 PreTranslateMessage(),这是非常不推荐的。

需要具备三个功能:

  1. OnCancel() 函数覆盖基类版本而不是调用它。这可以防止 Esc 键关闭应用程序。
  2. OnOK() 函数覆盖基类版本而不调用基类。这可以防止 Enter 键关闭应用程序。
  3. 因为您现在已阻止关闭对话框窗口,您现在必须实现 OnClose() 事件处理程序。此函数处理程序将在单击 Windows“X”按钮或系统命令 Close Alt+F4 时进行处理。现在为了关闭应用程序,如果需要,您可以调用其他函数之一的基类版本 OnOK()、OnCancel() 以实际关闭应用程序。此时,您现在可以完全控制应用的关闭方式。

步骤 1

在标题中,添加三个函数原型。 如果您想添加 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
};

第二步

在 .cpp 文件中,将 ON_WM_CLOSE() 条目添加到消息映射和三个函数的定义中。由于 OnCancel() 和 OnOK() 通常为空,因此您可以根据需要将它们内联在标题中(请参阅我在第 1 步中所做的操作?)。

.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
}

【讨论】:

    【解决方案2】:

    @the-forest-and-the-trees 的回答非常好。除了@oneworld 解决的一种情况。您需要过滤不属于对话窗口的消息:

    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);
    }
    

    记得在头文件中添加virtual

    【讨论】:

      【解决方案3】:

      确保您没有#define CUSTOM_ID 2,因为2 已经定义为转义,我认为1 定义为输入?如果我错了,请纠正我。

      【讨论】:

        【解决方案4】:

        我只是重写 OnOk 事件,而不是将消息传递给父对话框,什么都不做。
        所以这样做基本上很简单:

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

        这应该可以防止对话框在按下回车键时关闭。

        【讨论】:

          【解决方案5】:

          有一个替代上一个答案的方法,如果您希望仍然有一个确定/关闭按钮,这很有用。如果你重写 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);
          }
          

          【讨论】:

          • 不确定您想在这里完成什么,但使用WM_GETDLGCODE 可能会更好。
          • 您的解决方案也有缺点,必须牢记。如果用户想在应用程序中使用 ESC,例如关闭 DropDownList,他将不再能够这样做。 Enter 也是一样。他不能再使用它来与 UI 组件交互,例如完成选择。
          【解决方案6】:

          当用户在对话框中按下 Enter 键时,可能会发生两种情况:

          1. 对话框有一个默认控件(请参阅CDialog::SetDefID())。然后将带有此控件 ID 的 WM_COMMAND 发送到对话框。
          2. 对话框没有默认控件。然后将 ID = IDOK 的 WM_COMMAND 发送到对话框。

          使用第一个选项,默认控件的 ID 可能等于 IDOK。那么结果将与第二个选项中的结果相同。

          默认情况下,CDialog 类有一个用于调用CDialog::OnOk()WM_COMMAND(IDOK) 的处理程序,这是一个虚函数,默认情况下它调用关闭对话框的EndDialog(IDOK)

          因此,如果您想避免关闭对话框,请执行以下操作之一。

          1. 将默认控件设置为 IDOK 以外的其他控件。
          2. WM_COMMAND(IDOK) 设置一个不调用EndDialog() 的处理程序。
          3. 覆盖 CDialog::OnOk() 并且不调用基本实现。

          关于 IDCANCEL,它是类似的,但没有等效的 SetDefID() 并且 ESC 键是硬编码的。所以为了避免对话框被关闭:

          1. WM_COMMAND(IDCANCEL) 设置一个不调用EndDialog() 的处理程序。
          2. 覆盖 CDialog::OnCancel() 并且不调用基本实现。

          【讨论】:

          • 我想阻止 ESC 关闭对话框。但是,要么在 OnCommand 中阻止它,要么覆盖 OnCancel 也会阻止 click X 关闭对话框。
          • @Zhang:自从我上次使用 MFC 已经有一段时间了,但是 IIRC 按 X 会发送一个WM_CLOSE,默认情况下会转换为WM_COMMAND(IDCANCEL)。您可以通过覆盖 OnCancel() 来执行您想要的操作,添加一个直接执行 EndDialog(IDCANCEL)OnClose()
          • 两天我一直在挖掘一个教程项目的资源,没有任何解释,当我点击简单的确定按钮时对话框是如何关闭的......为什么地狱MS包括这样MFC中的模糊行为?是否值得将其硬编码到基类中,而不是显式地为处理程序添加一行代码?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-18
          相关资源
          最近更新 更多