【问题标题】:Modeless dialog keyboard handling (winapi)无模式对话键盘处理 (winapi)
【发布时间】:2013-03-01 06:05:12
【问题描述】:

我有一个带有一个主窗口的应用程序,它有一堆控件,包括空格键,它由一个名为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;
    }

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

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

【问题讨论】:

  • You're cout 消息可能会更有洞察力,如果您尝试打印 something 除了您刚刚放入的消息处理程序部分的名称之外。也许code NMHDR 的成员可能会让您对键盘通知的怀疑有所了解,尤其是如果您确实显示它以便知道它是什么。

标签: winapi dialog keyboard


【解决方案1】:

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

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

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

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

相反,您需要找出一些方法来捕获按键通知它们被发送到焦点控件之前。为此,您需要修改应用程序的消息循环以在调用DispatchMessageIsDialogMessage 之前捕获WM_KEYDOWNWM_KEYUP 消息

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

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

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

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

【讨论】:

  • 实际上,该应用程序使用主窗口中的 DirectInput 来读取所有键,包括空格键。 TreeView 包含不可编辑的项目,所以我宁愿坚持使用该控件。话虽如此 - 我担心您建议的解决方案(在消息循环中捕获按键通知)是否会破坏我基于 DirectInput 的主窗口。
  • 我对 DirectInput 一无所知。我从标签中假设这个问题完全是关于 Win32 编程的。如果 DirectInput 为您提供了一种处理键盘事件的方法主消息循环调度它们以进行默认处理,那么它可能会起作用。这就是为什么我花时间解释正在发生的事情,而不仅仅是提供可复制粘贴的代码。 @sy。
  • 我确信有一种简单的方法可以连接到对话框的默认值——我收集的内容通常在 Windows 中。但我毕竟是新手。这就是为什么我没有进入主窗口细节的原因。我没想到 DirectInput 是相关的。我已经按照您建议的方式实现了空格键处理。这一切都像一个魅力。非常感谢!不过,我有一个后续问题:WM_KEYDOWN、WM_CHAR 和 WM_KEYUP 消息仅在对话框具有焦点时才会收到,而当主窗口具有焦点时它们不会收到。在我的情况下这实际上是可取的,但为什么会这样呢?
  • @sy。我不知道“挂钩到对话框默认值”是什么意思。我们在这里不处理对话框,我们处理的是子控件on,该对话框正在处理键盘消息,然后才将其传递给对话框父级。至于您的后续问题,同样的原因也适用。这些消息仅发送到当前具有焦点的窗口。这是 Windows 中的一条硬性规则——只有焦点窗口才会收到输入通知。如果这是一个不处理输入的子窗口,它可以将其传递给它的父窗口,直到找到一个处理程序。
猜你喜欢
  • 2011-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
相关资源
最近更新 更多