チェックボックスのキーダウン時に矢印キーを検知する方法について
VB6のチェックボックスについて質問させて頂きます。
環境
Windows 2000 SP4
Visual Basic 6.0(SP6)
チェックボックスのキーダウン時に矢印キーを検知する方法について、ご存知の方がいらっしゃいましたら、ぜひ教えて頂きたいです。
自分なりに調べてみた結果は以下の通りです。
チェックボックスのKeyDownイベントでは矢印キーは検知できませんでした。
次に試したのが、サブクラス化を行い、WM_KEYDOWNを取得する方法です。
正直、サブクラス化を行えば矢印キーを確実に検知できると考えていたのですが、なぜか矢印キーを押してもWM_KEYDOWNメッセージを取得できず、このため矢印キーの検知ができませんでした。
矢印キー以外のキー、例えばAやSなどは問題なく検知できました。
ちなみに、この状態でSpy++を起動し、矢印キーを押したときのメッセージを確認してみると、チェックボックスウィンドウはきちんとWM_KEYDOWNを受け取っていました。
それなのにサブクラス化した方ではWM_KEYDOWNは取得できません。
まだ完全にサブクラス化の概念を理解している訳ではないため、恐らく何か原因があるのでしょうが、想像ができないでいます。
( Windowsから送られるメッセージがチェックボックスウィンドウに届くまでの間に誰がメッセージを処理してるのかが分かりません。自分は間に何もないと考えていました )。
この動作についても知っている方がいたら、説明して頂けるととても助かります。
すいませんが、ご教授お願い致します。
No.8ベストアンサー
- 回答日時:
APIで自力でチェックボックスを作成すると、KeyDownが取得できます。
その結果からVB製のチェックボックスは、VBのライブラリ中で、自前サブクラス関数より先にメッセージを捕まえ、カーソルキーであればフォーカス制御の処理を行って、メッセージを破棄していることが予想できます。
破棄されたメッセージは、捕まえようがありません。
なのでVBより先にメッセージを得るために、フックしてあげないとできないのではないかな?
下に、APIでCheckBoxを作成する方法を載せておきます。
値の変更処理とかを追加しないと、OnOffが切り替わりません。
質問者さんのサンプルに、コードの変更と追加です。
モジュール追加で以下
Option Explicit
Private Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal HINSTANCE As Long, lpParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32" (ByVal hWnd As Long) As Long
Private Const BS_CHECKBOX = &H2&
Private Const WS_CHILD = &H40000000
Private Const WS_VISIBLE = &H10000000
Public Sub CreChk(Owner As Form)
Dim hChk As Long
hChk = CreateWindowEx(0, "Button", "チェックボックス", _
WS_CHILD Or WS_VISIBLE Or BS_CHECKBOX, _
0, 0, 150, 30, Owner.hWnd, 0&, App.HINSTANCE, 0&)
Call SubClass(hChk)
End Sub
あとそちらのサンプルの関数「WindowProc」をちょっと改造
Select Case uMsg
Case WM_KEYDOWN
Debug.Print Now & "KeyDown : KeyCode( " & wParam & " )"
Case WM_DESTROY
Call UnSubClass(hWnd)
End Select
呼び出しはフォームLoadで
Call CreChk(Me)
Unload時は不要
サブクラス関数内で、勝手に破棄
回答ありがとうございます。
あ、やばい、かぶちゃった…
すいません、先に回答して頂いてたのですね。
分かりやすい回答に、さらにサンプルまで付けて頂き、ありがとうございます。
WindowProcでWM_DESTROY拾って破棄など、とても参考になりました。
No.7
- 回答日時:
回答番号:No.6 の回答は取消させて下さい。
どうも違うメッセージを拾ってしまったようです。
回答ありがとうございます。
あ、そうでしたか。
わざわざ調査して頂きすいません、凄い助かります。
私の方でいま調査していたところ、CreateWindowExを使用し、作成したチェックボックスをサブクラス化した場合は、問題なく矢印キーを検知できることが分かりました。
確かに、VB6のチェックボックスは矢印キーでのフォーカス移動が最初から実装されているので、なんか怪しいとは感じていたのですが。
このことから、VB6のチェックボックス自体がフックなどを使用し、矢印キーの制御を実装しているかもしれませんね。
試しにいまCreateWindowExで作成したチェックボックスにキーボードフックのインストールとサブクラス化を行い、試したみたところ、VB6のコントロールと同様の動きになりました。
いま私が考えられるサブクラス化しても矢印キーを検知できなかった原因としては、これが精一杯ですね。
No.6
- 回答日時:
>サブクラス化でできないか調査している次第でした。
If uMsg = &H100E Then
If wParam = 3 Then
Debug.Print "↑"
End If
If wParam = 7 Then
Debug.Print "↓"
End If
End If
今試して見たら、uMsg = &H100E で wParam = 3 で ↑ で
wParam = 7 で ↓ が取得できるようです。
理由とかは、現在調査中、取りあえず取得出来る事だけを。
No.5
- 回答日時:
ハッキリした目的が解らないので、使えるかどうか、解りませんが
一応下記のようにすれば、矢印キーが押された事を取得できます。
Option Explicit
Private Declare Function GetKeyState Lib "user32" _
(ByVal nVirtKey As Long) As Integer
Private Sub Check1_GotFocus()
Label1.Caption = ""
End Sub
Private Sub Check1_LostFocus()
If GetKeyState(vbKeyUp) < 0 Then
Label1.Caption = "↑"
ElseIf GetKeyState(vbKeyDown) < 0 Then
Label1.Caption = "↓"
End If
End Sub
回答ありがとうございます。
あ、私も昨日ググって調べているときに、この方法を目にしました。
確かにこの方法を使えば矢印キーを取得することができるのですが、フォーカス喪失時というのが私がやろうとしていた目的に合わず、いまはサブクラス化でできないか調査している次第でした。
きちんと目的を記載せずに、すいませんでした。
No.4
- 回答日時:
回答ではありません。
目的は、何なのでしょうか?
それによっては、代替案があるかも知れません。
1個だけ位なら、ダミーのテキストボックスを使ってそちらで
取得するとか?
ピクチャーボックスでチェックボックスを作るとか。
少なくても、サブクラス化するよりは簡単かなと思いますよ。
早速のご返信、ありがとうございます。
そうですね、私もピクチャボックスでチェックボックスを作るなどの代替案を考えてみて、幾つか対応できる方法を見つけることができました。
しかし、どうしてサブクラス化をしているのに、方向キーでのWM_KEYDOWNが検知できないのかが分からず、誰か知っている人がいれば教えて頂きたいなと思いまして質問させて頂いた次第です。
No.3
- 回答日時:
参考URL
http://okwave.jp/qa/q4164908.html
SetWindowsHookEx
UnhookWindowsHookEx
CallNextHookEx
↓
フック関数「SetWindowsHookEx」
の呼び出し引数は「WH_CBT」となっているけどそこを変更して
「WH_KEYBOARD=2」
か
「WH_KEYBOARD_LL=13」
でいけるかな?
.NET用だけど、殆ど一緒です。
http://azumaya.s101.xrea.com/wiki/index.php?%B3% …
あと、不要なキーボードイベントもくるので、チェックボックスにカーソルがあるときの判定を、しなくてはいけません。
アクティブコントールのハンドルを取得するなどしてやるようにですね。
質問者さんのスキルは高そうなので、多くは言いません。
早速のご回答、ありがとうございます。
大変参考になります。
やはりフックを使用する方法しかないですよね。
記載して頂いた通り、フックは目的のコントロールを判定してやらなければならないので、できればサブクラス化でできないかなと思い、はまってしまった状態でした。
というのは、いまチェックボックスをベースにしたユーザコントロールを作成していまして、既存のチェックボックスのキーダウンイベントでは矢印キーを検知できないので、サブクラス化を利用し、矢印キーも検知できるキーダウンイベントを実装していました。
このため、好みの問題かもしれませんが、できればフックよりサブクラス化の方がしっくりくるかなと思い、現状に至っています。
ちなみに、このコントロールはグリッド専用のチェックボックスという目的で使用を考えてまして、チェックボックスなどを使用できるグリッドコントロールも同時に作成しています。
グリッドのため、方向キーでの移動を実装しなければならず、このためチェックボックスでの方向キーを検知したかったのでした。
打開策としては、フォントでチェックボックスっぽく見せる方法や、チェックボックスのコンテナをピクチャボックスとし、ちょこっと制御することで、方向キーを検知する方法などが見つかりましたが、なんでサブクラス化じゃできないかが、どうしても分からず、凄く気になってしまい、どなたか知っている方がいないかと思いまして、質問させて頂いた次第でした。
No.2
- 回答日時:
_
この回答への補足
Private Const PROPNAME As String = "OriginWindowProc"
Public Sub SubClass(hWnd As Long)
Dim DefProc As Long
DefProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
If DefProc <> 0 Then
Call SetProp(hWnd, PROPNAME, DefProc)
End If
End Sub
Public Sub UnSubClass(hWnd As Long)
Dim DefProc As Long
DefProc = GetProp(hWnd, PROPNAME)
If DefProc <> 0 Then
SetWindowLong hWnd, GWL_WNDPROC, DefProc
End If
End Sub
Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim DefProc As Long
Select Case uMsg
Case WM_KEYDOWN
Debug.Print "KeyDown : KeyCode( " & wParam & " )"
End Select
DefProc = GetProp(hWnd, PROPNAME)
If DefProc <> 0 Then
WindowProc = CallWindowProc(DefProc, hWnd, uMsg, wParam, lParam)
End If
End Function
4. イミディエイトウィンドウに押したキーが表示されるが、矢印キーは表示されないことが確認できる
5. Spy++で確認すると、WM_KEYDOWNが発生していることが確認できる
No.1
- 回答日時:
_
この回答への補足
文字数が足りないので、分割させて頂きます。
以下に自分が調査したときに使用したサブクラス化のサンプルコードを添付します。
1. プロジェクトにフォームとモジュールを追加し、フォームにチェックボックスを1つ追加する。
2. 以下コードをフォームに貼り付ける。
Option Explicit
Private Sub Form_Load()
SubClass Me.Check1.hWnd
End Sub
Private Sub Form_Unload(Cancel As Integer)
UnSubClass Me.Check1.hWnd
End Sub
3. 以下コードをモジュールに貼り付ける。
Option Explicit
Private Const GWL_WNDPROC = (-4)
Private Const WM_KEYDOWN = &H100
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long _
) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _
ByVal lpPrevWndFunc As Long, _
ByVal hWnd As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long _
) As Long
Private Declare Function SetProp Lib "user32" Alias "SetPropA" ( _
ByVal hWnd As Long, _
ByVal lpString As String, _
ByVal hData As Long _
) As Long
Private Declare Function GetProp Lib "user32" Alias "GetPropA" ( _
ByVal hWnd As Long, _
ByVal lpString As String _
) As Long
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
このQ&Aを見た人はこんなQ&Aも見ています
-
準・究極の選択
「年収1000万円で一生カレーライス」か 「年収180万円で毎日何でも食べ放題」 あなたはどちらを選びますか?
-
フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
あなたが普段思っている「これまだ誰も言ってなかったけど共感されるだろうな」というあるあるを教えてください
-
映画のエンドロール観る派?観ない派?
映画が終わった後、すぐに席を立って帰る方もちらほら見かけます。皆さんはエンドロールの最後まで観ていきますか?
-
海外旅行から帰ってきたら、まず何を食べる?
帰国して1番食べたくなるもの、食べたくなるだろうなと思うもの、皆さんはありますか?
-
天使と悪魔選手権
悪魔がこんなささやきをしていたら、天使のあなたはなんと言って止めますか?
-
矢印(左右)キーでイベントを発生させたい。
Visual Basic(VBA)
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
vbaから他のアプリを終了
-
Excel VBA で外部アプリケーシ...
-
msgboxの表示位置
-
マウスホイールをフックしたい
-
WM_NCLBUTTONUPについて
-
VC++でポップアップアラート
-
有効でないウインドウハンドル...
-
VBでRegisterWindowMessage関数...
-
VBAでコントロールのハンドルを...
-
CloseHandle()
-
他のウィンドウのボタンを自動...
-
メッセージハンドラ
-
デスクトップ上のアイコンの位...
-
システム例外のメッセージを変...
-
SetWindowText関数について
-
DEVICECHANGE() の受け取り
-
キーボード入力をHSPでさせたい...
-
VB6でシャットダウン、ログオフ...
-
MQプログラミング MQGetの手法...
-
ウインドウの位置を知る。
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
vbaから他のアプリを終了
-
他のウィンドウのボタンを自動...
-
メッセージハンドラ
-
VBAでコントロールのハンドルを...
-
msgboxの表示位置
-
メッセージボックスの選択ボタ...
-
SetWindowText関数について
-
VBA 複数セルが空白なら印刷さ...
-
WM_NCLBUTTONUPについて
-
デスクトップ上のアイコンの位...
-
点滅で知らせる方法
-
チェックボックスのキーダウン...
-
CloseHandle()
-
Excel VBA で外部アプリケーシ...
-
WM_CLEARなど使えないメッセー...
-
マウスの状態の取得
-
DEVICECHANGE() の受け取り
-
SendMessage で ESC など
-
VBでのハンドルの使い方
-
VB.netでSendMessageを使用して...
おすすめ情報