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 では、一度に 1 つのウィンドウしかフォーカスできず、現在フォーカスされているウィンドウがすべてのキーボード入力を受け取るウィンドウです。したがって、ダイアログ ボックスにフォーカスがある場合、キー プレス通知を受け取ります。そのダイアログ ボックスのコントロールにフォーカスがある場合、 (親ダイアログではなく) キー プレス通知を受け取ります。また、ダイアログ ボックスにはフォーカス可能な子コントロールがあり、親ダイアログに優先して常にフォーカスを受け取るため、キー プレス通知も常に受け取ります。

したがって、興味深いWM_NOTIFYメッセージの説明としては、ダイアログの共通コントロールの 1 つにフォーカスがあり、スペース キーの押下イベントを受け取り、それを処理した後、親ウィンドウ (ダイアログ) に通知を渡すことです。WM_NOTIFYメッセージの形。ご想像のとおり、これはスペース バーが押されたことを検出する信頼できる方法ではありません。

代わりに、キー プレス通知がフォーカスされたコントロールに送信されるにトラップする何らかの方法を見つける必要があります。これを行うには、またはを呼び出す前にWM_KEYDOWN、アプリケーションのメッセージ ループを trapまたはWM_KEYUPmessagesに変更する必要があります。DispatchMessageIsDialogMessage

  • キー イベントがスペース バーに対応する場合は、onSpacebar関数を呼び出してメッセージが処理されたことを示し、メッセージが別のウィンドウに渡されて処理されるのを防ぎます。
  • キー イベントがスペース バーに対応していない場合はメッセージが他のウィンドウに渡されて処理されるように、通常どおりにメッセージを処理する必要があります。

このアプローチは、グローバル レベルでスペース キーの押下を除外するため、キーの押下を盗むダイアログ上の子コントロールの問題と、他のモードレス ダイアログの問題の両方を解決します。ただし、ユーザーがキーボードを使用してダイアログをまったくナビゲートできないようにするのは非常に簡単であるため、注意が必要です。

より根本的には、スペースバーのプレスを処理するというあなたの考えには根本的な欠陥があると思います. 特定の共通コントロールのロジックでは、基本的に、スペース バーの押下を処理する必要があります。たとえば、テキスト ボックスを考えてみましょう。グローバル レベルでスペース バーのすべての押下を除外すると、ユーザーはテキスト ボックスにスペースを入力できなくなります。どうしてもスペース バーを処理したい場合は、グローバル ハンドラーでフォーカスされたコントロールを確認する必要があります。それがテキスト ボックス (またはスペースを受け取りたい他の共通コントロール) である場合は、それを渡します。それ以外の場合は、自分で処理してください。

正直なところ、代わりにもっとユニークなキーの組み合わせ (わからない、Ctrl+など) を選択し、それをアクセラレータSpaceとして設定します。おそらく、グローバル メッセージ ループは、関数を呼び出して既にアクセラレータ キーを処理しているため、面倒な作業はすべて処理されます。コードは必要ありません。プロジェクト内のアクセラレータ リソース ファイルを編集するだけで、すべてを実行できます。キーボード アクセラレータに関する MSDN のドキュメントはこちらにありますが、お気に入りの Visual C++ の本を参照する方が簡単かもしれません。TranslateAccelerator

于 2013-03-13T11:12:07.990 に答える