C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

矢印キーが押されている時だけシフトキーを無効にする /レタス (21/03/26(Fri) 20:19) #97097
Re[1]: 矢印キーが押されている時だけシフトキーを無効にする /魔界の仮面弁士 (21/03/26(Fri) 20:58) #97099
  └ Re[2]: 矢印キーが押されている時だけシフトキーを無効にする /レタス (21/03/27(Sat) 14:55) #97103
    └ Re[3]: 矢印キーが押されている時だけシフトキーを無効にする /魔界の仮面弁士 (21/03/28(Sun) 23:00) #97107


親記事 / ▼[ 97099 ]
■97097 / 親階層)  矢印キーが押されている時だけシフトキーを無効にする
□投稿者/ レタス (1回)-(2021/03/26(Fri) 20:19:03)

分類:[.NET 全般] 

VB.NETを使用しております。

画像ビューアのようなものを作成していまして、
矢印キーを押すと画像が入れ替わるような仕様になっています。

更に、シフトキーを押しながら、矢印キーを押すと早送り(1フレーム飛ばし)で画像が入れ替わるようにしています。

一応、そこまでは以下のコードでうまくいっているのですが、
一つだけ問題があります。

先にシフトキーを押してから、矢印キーを押すとうまく動作するのですが、
矢印キーを押した状態でシフトキーを押すと
フレーム送りの動作が停止してしまい、再度、矢印キーを押し直す必要があります。

そのため、矢印キーを押している間だけシフトキーを押しても
矢印キーの動作が止まらないようにしたいのですが
どのようにすれば良いでしょうか?

以下はコードの抜粋です。

フォーム生成時に、
CursorMovementKeyDownは全てのコントロールのKeydownに対して、
再帰的にAddHandlerされています。



    Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean

        If (keyData And Keys.KeyCode) = Keys.Shift Then

		’シフトキーが押されていると抜けたいがそれでも矢印キーはキャンセルされてしまう
            Return True

        End If

        If FrameChange(keyData And Keys.KeyCode) = True Then

            Return True

        Else

            Return MyBase.ProcessDialogKey(keyData)

        End If

    End Function


    Private Sub CursorMovementKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)


        If e.KeyCode = Keys.ShiftKey Then

		’シフトキーが押されていると抜けたいがそれでも矢印キーはキャンセルされてしまう
            e.Handled = True
            Exit Sub

        End If


        e.Handled = FrameChange(e.KeyCode)


    End Sub




    Function FrameChange(keyCode As Keys) As Boolean

            Dim incre As Integer = 1

		’シフトキーが押されていると増分を2に変更する
            If Control.ModifierKeys = Keys.Shift Then
                incre = 2
            End If

	画像を変更するコード		


    End Sub

[ □ Tree ] 返信 編集キー/

▲[ 97097 ] / ▼[ 97103 ]
■97099 / 1階層)  Re[1]: 矢印キーが押されている時だけシフトキーを無効にする
□投稿者/ 魔界の仮面弁士 (3028回)-(2021/03/26(Fri) 20:58:13)
No97097 (レタス さん) に返信
> 画像を変更するコード
えぇと…Function FrameChange なのに、End Sub で終わるのですか?
何か過剰に省略してしまったのか、それとも掲示板投稿時の転記ミスでしょうか。


> 矢印キーを押すと画像が入れ替わるような仕様になっています。

テキストボックス内の文章で、矢印キーを押し続けた場合、
いわゆる「キーリピート」が発生しますよね。
(リピートの発生タイミングはコントロール パネルで調整可能)

そのリピートも拾うような仕組みの方が良いのでしょうか。
それとも、押し続けてもリピートさせずに、
キーを押し下げた瞬間に反応するのが良いのでしょうか。
あるいは、キーを離したときに反応するのが良いのでしょうか。


ただ、Shift キーは連打されると Windows の「固定キー機能」と競合するので、
もしかしたら、Ctrl や Alt の方が扱いやすいかも…。


> 先にシフトキーを押してから、矢印キーを押すとうまく動作するのですが、
> 矢印キーを押した状態でシフトキーを押すと
> フレーム送りの動作が停止してしまい、再度、矢印キーを押し直す必要があります。

入力キーが変化したことで、キーリピート判定が再開しているだけではないでしょうか。
そのままシフトキーを矢印キーと押し続ければ、キーリピートが再開されるはずです。

即座にキーリピートを開始してほしいということであれば、コントロール パネルから
キーボードのプロパティダイアログで速度を調整できます。

あるいは、そうしたシステム設定に依存したくないのであれば、キー入力イベントには頼らず、
Timer を使って「現在、キーが押し下げられているかどうか」を判断して、
自分でタイミングを管理するという方針もあろうかと思います。

たとえば、ProcessDialogKey / KeyDown / KeyPress / KeyUp などに頼らず、
任意のタイミング(Timer など)において、それぞれのキーが現在押されているかどうかを判定するため、
参照設定に PresentationCore と WindowsBase を加えたうえで、
 a = System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift)
 b = System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.RightShift)
などと書くことができます。
[ 親 97097 / □ Tree ] 返信 編集キー/

▲[ 97099 ] / ▼[ 97107 ]
■97103 / 2階層)  Re[2]: 矢印キーが押されている時だけシフトキーを無効にする
□投稿者/ レタス (2回)-(2021/03/27(Sat) 14:55:16)

> えぇと…Function FrameChange なのに、End Sub で終わるのですか?
> 何か過剰に省略してしまったのか、それとも掲示板投稿時の転記ミスでしょうか。

すいません、掲示板投稿時の転記ミスです。




> そのリピートも拾うような仕組みの方が良いのでしょうか。
> それとも、押し続けてもリピートさせずに、
> キーを押し下げた瞬間に反応するのが良いのでしょうか。
> あるいは、キーを離したときに反応するのが良いのでしょうか。

リピートも拾うような仕組みを必要としています。



> 入力キーが変化したことで、キーリピート判定が再開しているだけではないでしょうか。
> そのままシフトキーを矢印キーと押し続ければ、キーリピートが再開されるはずです。

どういうわけか、私の環境では自動でキーリピートは再開しません。
他のPCでもそうなっているので、コードに問題があるのかも知れませんが、
どうすれば再開できるようにできますか?




> あるいは、そうしたシステム設定に依存したくないのであれば、キー入力イベントには頼らず、
> Timer を使って「現在、キーが押し下げられているかどうか」を判断して、
> 自分でタイミングを管理するという方針もあろうかと思います。

そうですね、どうしてもできない場合にはそういうやり方になるとは思いますが、
できれば、Timerなど使わずに実行できたらと思います。


[ 親 97097 / □ Tree ] 返信 編集キー/

▲[ 97103 ] / 返信無し
■97107 / 3階層)  Re[3]: 矢印キーが押されている時だけシフトキーを無効にする
□投稿者/ 魔界の仮面弁士 (3030回)-(2021/03/28(Sun) 23:00:52)
No97103 (レタス さん) に返信
> そうですね、どうしてもできない場合にはそういうやり方になるとは思いますが、
> できれば、Timerなど使わずに実行できたらと思います。

キーリピートの押下は必ずしも拾いきれそうにないですね。
ProcessDialogKey だと限界がありそうなので、PreProcessMessage で捉えるようにしてみましたが、

1: Right キーを押し下げる ⇒ KeyDown がリピート発生
2: さらに LShift も押し下げる ⇒ KeyDown がリピート発生
3: LShift から指を離すが Right は押したまま ⇒ KeyUp が発生し、KeyDown のリピートが止まる。

という状態になっていました。やはり、Timer を使うことになりそうです。


ちなみに NumericUpDown コントロールの場合、ボタン押下開始時刻を Now.Ticks で記録したり、
内部で Timer コントロールを使うといった処理が行われているようです。また、押し続けたときに
数値の増加速度を加速させるため、秒数と増分値のコレクションを Accelerations プロパティで管理しています。

今回の画像ビューワーの場合とは早送りのさせ方が違いますが、考え方の参考にはなるかもしれません。

なお、既定のキーリピートのタイミングについては、SystemInformation クラスの
KeyboardDelay プロパティ と KeyboardSpeed プロパティで得られます。


キーボードの押下状態については、
 Private Declare Function GetKeyboardState Lib "user32" (pbKeyState() As Byte) As Boolean
で拾えます。

Const StateMask As Byte = &H80
Dim keyState(255) As Byte
If GetKeyboardState(keyState) Then
 LShiftKey = CBool(keyState(Keys.LShiftKey) And StateMask)
 RShiftKey = CBool(keyState(Keys.RShiftKey) And StateMask)
 UpKey = CBool(keyState(Keys.Up) And StateMask)
 RightKey = CBool(keyState(Keys.Right) And StateMask)
 DownKey = CBool(keyState(Keys.Down) And StateMask)
 LeftKey = CBool(keyState(Keys.Left) And StateMask)
 IsShiftKeyDown = LShiftKey OrElse RShiftKey
 IsArrowKeyDown = UpKey OrElse RightKey OrElse DownKey OrElse LeftKey
  :
  :
End If
[ 親 97097 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -