0

我有一个带有一个主窗口的应用程序,它有一堆控件,包括空格键,它由一个名为onSpacebar(). 在那个主窗口的顶部,我有一个持久的无模式对话框。我需要空格键的行为完全相同,无论对话框是否具有焦点,或者主窗口是否具有焦点。

这个对话框由一个看起来像这样的 DialogProc 支持:

 BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      switch(uMsg)
      {
      case WM_NOTIFY:
        std::cout<< "WM_NOTIFY" <<std::endl;
        switch(LOWORD(wParam))
        {
              // which component caused the message?
        case COMP_TREE:
              if(((LPNMHDR)lParam)->code == NM_DBLCLK){
                          onDoubleclk()
              }
              //...
        break;
        // other components...

        }
      break;
      case WM_CLOSE: 
          // the dialog can only be closed when the whole app is closed
          //EndDialog(hDlg, IDCANCEL); 
          return TRUE;
      case WM_DESTROY:
          PostQuitMessage(0); 
          return TRUE;
      }
      return FALSE;
    }

根据我收集的信息,我应该onSpacebar()从 DialogProc 中调用我的方法,类似于我处理双击的方式。我可以看到WM_NOTIFY按下空格键时对话框收到了该消息(短语 WM_NOTIFY 打印到 cout),但我似乎无法将空格键通知与对话框收到的其他众多通知区分开来。

请告诉我如何识别特定WM_NOTIFY是对空格键按键的响应。

4

1 回答 1

5

WM_NOTIFY消息不是窗口处理按键事件的标准方式。当一个键被按下时,你的窗口应该会收到WM_KEYDOWN, WM_KEYUP, 和可能的WM_CHAR消息。WM_NOTIFY完全不同的目的:将消息从公共控件传递到其父窗口。

因此,您收到WM_NOTIFY响应按键的消息是一件相当不寻常的事情,当您了解焦点的工作原理(这是解决最终问题的关键)时可以解释。

在 Windows 中,一次只能聚焦一个窗口,当前聚焦的窗口是接收所有键盘输入的窗口。因此,如果对话框具有焦点,它将收到按键通知。如果该对话框上的子控件具有焦点,则它(不是其父对话框)将收到按键通知。并且对话框上有一个可聚焦的子控件,它将始终优先于其父对话框接收焦点,因此它也将始终接收按键通知。

WM_NOTIFY因此,对您的好奇消息的可能解释是,对话框上的常见控件之一具有焦点,它正在接收空格键按下事件,并在处理它之后,将通知传递给它的父窗口(您的对话框)消息的形式WM_NOTIFY。正如您可能想象的那样,这不是检测空格键已被按下的可靠方法。

相反,您需要找出某种方法来捕获按键通知,然后再将它们发送到焦点控件。为此,您需要在调用or之前WM_KEYDOWN修改应用程序的消息循环以捕获或WM_KEYUP消息。DispatchMessageIsDialogMessage

  • 如果键事件对应于空格键,您将调用您的onSpacebar函数并指示该消息已被处理,从而防止它被传递并被另一个窗口处理。
  • 如果键事件与空格键对应,那么您将需要像往常一样处理消息,确保它确实被传递并由另一个窗口处理。

由于这种方法在全局级别过滤掉空格按键,因此它解决了对话框上的子控件窃取按键和其他无模式对话框的问题。但是,您确实需要小心,因为很容易搞砸,以至于用户根本无法使用键盘导航您的对话框。

更根本的是,我认为你处理空格键的想法从根本上是有缺陷的。某些常用控件的逻辑基本上要求它们处理空格键的按下。例如,考虑一个文本框:如果您在全局级别过滤掉所有按空格键,用户将永远无法在文本框中键入空格。如果您坚持处理空格键,则需要检查全局处理程序中的焦点控件,如果它是文本框(或您希望接收空格的其他常见控件),请将其传递;否则,请自行处理。

老实说,我要做的是选择一个更独特的组合键(例如,我不知道,Ctrl+ Space)并将其设置为Accelerator。大概,您的全局消息循环已经通过调用该TranslateAccelerator函数来处理加速键,这样就可以为您处理所有脏活。甚至不需要任何代码——您只需在项目中编辑加速器资源文件即可完成所有工作。有关键盘加速器的 MSDN 文档在此处,但您可能会更轻松地查阅您最喜欢的有关 Visual C++ 的书籍。

于 2013-03-13T11:12:07.990 回答