コマンドボタン上でカーソルキーの動きを制御したい




複数(3個以上)のコマンドボタンを並べてあるフォーム上で(これはこれで気味が悪い(笑))、「カーソルキーの右を押したらフォーカスも下へではなく、横に移動させたい」と思った時のテクです。

最初にハッキリ言うと『コマンドボタンにフォーカスが居る場合のカーソルキー入力』は、VBだろうがVC++だろうが、キー入力イベントは発生しません。ウィンドウ・プロシーシャにすら流れて来ないんです。

じゃぁ、どうするか!?
フォーカスの出入りを捕らえるしかありません。
ですが、単純に LostFocus イベントでは困った事に、あまり正常に動いてはくれません(爆)
私が取った方法は、Win32APIの、SetWindowLong() によって、コマンドボタンのウィンドウ・ハンドルに流れてくるイベント・メッセージを先に監視し、フォーカス移動の際に発生するGDI系のイベントを押さえ、その押さえたタイミングで GetKeyboardState() 関数によってキーボードのキー入力状態を取得し、フォーカス移動がカーソルキーによって行われた場合に『自前のフォーカス移動処理をさせる』と言う方法です。

挙動を観ての推測ですが、コマンドボタン上ではフォーカス移動に直接関わるキー、すなわちカーソルキー・TABキーはキー入力イベントが発生しない。ウィンドウ・プロシージャにキー入力イベントとして渡さずにOSが先に処理し、LostFocus イベントのみを渡すと言う事だと思います。
また挙動を観て他に判った事は、コレにはリターンキーも含まれている(スペースバー(スペースキー)は、キー入力イベントとして押さえられます)と言う事です。
で・・・、サンプルソースを全部載せる気にはなれないので、一部だけ(笑)


-----<<ここから>>--------------------------------------------------

Rem グローバル変数

Rem ウィンドウプロシージャハンドル保持変数
Public lpPrevWndProc As Long
Public glHWin1       As Long

Rem 2回同じプロシージャフックを呼ぶと暴走するのでそれを防止する
Public bNever        As Boolean

Rem その他
Private lKeys        As Long
Private cBuf(256)    As Byte


Rem Hook - in
Public Sub Hook()
    lpPrevWndProc = SetWindowLong(glHWin1, GWL_WNDPROC, AddressOf WindowProc)
End Sub


Rem Hook - out
Public Sub Unhook()
    Dim temp As Long
    
    temp = SetWindowLong(glHWin1, GWL_WNDPROC, lpPrevWndProc)
End Sub


Rem Window-Pro.
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    
    Rem Debug.Print "Message: "; hw, uMsg, wParam, lParam
    
    Rem フォーカスが移動した時にのみ発生しているメッセージを確認した時、
    If uMsg = &H100E Then
        Rem キーボードの状態を取得し、カーソルキーの入力をチェックする。
        lKeys = GetKeyboardState(cBuf(0))
        If cBuf(37) And &H80 Then
            TEST.Label1.Caption = "Left!"
            
        ElseIf cBuf(38) And &H80 Then
            TEST.Label1.Caption = " Up !"
            
        ElseIf cBuf(39) And &H80 Then
            TEST.Label1.Caption = "Right!"
            
        ElseIf cBuf(40) And &H80 Then
            TEST.Label1.Caption = "Down!"
            
        End If
    End If
    WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
    
End Function

-----<<ここまで>>--------------------------------------------------


これらをプログラムの然るべき箇所に設置し、望むべき改造を施してやればOKです。
何故って、ラベルに表示するだけですからね(笑)<フォーム名も固定だし(爆)
また、API使用の宣言は行っていませんので、まるまるCOPY&PASTEする人は気を付ける事。
AddressOf は『関数のポインタ』を渡させるのに必要なモノです。詳しい説明はWin32API利用の本を読んで下さい(笑)
つまり、フック時には Function WindowProc() のアドレスを渡している訳です。なおアン・フック時にはフック時の返り値である、『変更前のプロシージャへのポインタ』をセットしてあげます。
使ったモノはちゃんと返さないとね(笑)
また自前の作業が終わったら CallWindowProc で、やっぱり『変更前のプロシージャへのポインタ』を渡してあげれば、それ以外の通常のイベント処理をやってくれます。じゃないと、自分で処理するしか無いッスよ(笑)
Debug.Print はデバッグ作業中に、イミディエイトでイベントメッセージを眺めるのに使いましょう(笑)

これを行うメリットは、カーソルキーと同じ配置のコマンドボタンを用意して、コマンドボタンの入力とカーソルキーの入力を一致させたい場合に特に有功であると思います。見た目とカーソルキーが一致する訳ですから。



これを用いたアルゴリズムによる損害などの責任は、私には負う義務・理由はありません。利用される方にのみあります。
利用する際には、事後でも結構ですから e-mail でも下さい。事前承諾は一切しませんから、お好きな様に。
無いとは思いますが、記事に利用される場合には事前承諾を取ってからにして下さい。
自分で試したテストルーチンなので、ちゃんと動きます。





戻 る