none
テキストボックス等でのキー入力時に、入力キーが文字キーなのか制御系キーなのかを簡単に判定する方法について RRS feed

  • 質問

  • 【環境】Windos7,VB.NET Framework4.5 Windows.Forms

    いつもお世話になっております。質問させて下さい。

    PreviewKeyDownイベント内にて、入力キーの判定をしているのですが、
    入力されたキーが文字として表示されるキーかどうか、という判定は
    どうしたら一番スマートな記述が出来るのでしょうか?

    "文字として表示されるキーか"というのは物理キーボードの全てのキーのうち、
    押したらTextBox等に改行などでなく何かしらの文字が表示されるキーの事です。

    現在以下のようにSelect CaseでKeys.xxx....を大量記述して判定するようにしていますが、
    例えば、IsXXXとかで一発で判定する等、もっと簡単な方法はないのでしょうか?

    <pre>
            Select Case e.KeyCode
                Case Keys.A To Keys.Z, Keys.D0 To Keys.D9, Keys.NumPad0 To Keys.NumPad9 _
                   , Keys.OemMinus, Keys.Space _
                   , Keys.Oem1 To Keys.Oem7, Keys.Oem102 _
                   , Keys.Oemplus, Keys.Oemcomma, Keys.OemPeriod _
                   , Keys.Divide, Keys.Multiply, Keys.Subtract, Keys.Add, Keys.Decimal
                    '以下の文字キーが押された場合
                    '   A-Z,0-9,テンキー0-9
                    '   -、Spaceキー
                    '   :/@[\]^(\※右下\)
                    '   ;,.
                    '   テンキーの/*-+.
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

                Case Else
                    'それ以外の制御系キーが押された場合
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            End Select
    </Pre>








    2016年4月18日 5:46

回答

  • 東京都_会社員 さん、

    発想を変えて、入力用コントロール(TextBox など)の TextChanged イベントで文字数が増えたかどうかで判断してはいかがでしょう?
    文字数が増えれば制御系以外のキーが押されたことになり、文字数が減れば [Del] や [BackSpace] のようなキー、文字数が変わらなければ制御系のキーが押されたと判断できるのではないかと思います。

    以上です。


    2016年4月19日 0:24

すべての返信

  • もっと単純なコードがあるのかもしれませんが、API の MapVirtualKey を使ったコードを考えてみました。

    ※修飾キーを使ったキーの同時押しには対応していません。

    Imports System.Runtime.InteropServices
    
    Public Class Form1
        <DllImport("user32.dll", EntryPoint:="MapVirtualKeyA")>
        Private Shared Function MapVirtualKey(ByVal wCode As Integer, ByVal wMapType As Integer) As Integer
        End Function
    
        Public Function IsCharacterKey(ByRef keyData As Keys) As Boolean
            Return Not Char.IsControl(Convert.ToChar(MapVirtualKey(CUInt(keyData), 2)))
        End Function
    
        Private Sub Form1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles MyBase.PreviewKeyDown
            If IsCharacterKey(e.KeyData) Then
                MsgBox("文字: " & e.KeyData.ToString())
            Else
                MsgBox("制御文字: " & e.KeyData.ToString())
            End If
        End Sub
    End Class





    2016年4月18日 10:21
  • 全然簡単ではないけど、拡張メソッドを定義してみるとか

    Public Class Form1
        Private WithEvents textBox1 As TextBox
        Sub New()
            InitializeComponent()
    
            textBox1 = New TextBox()
            Me.Controls.Add(textBox1)
        End Sub
    
        Private Sub TextBoxEx1_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles textBox1.PreviewKeyDown
            If (e.IsDisplayCharKey()) Then ' e.KeyData.IsDisplayCharKey 
                System.Diagnostics.Debug.WriteLine(e.GetDisplayChar())
            End If
        End Sub
    End Class
    
    <System.Runtime.CompilerServices.Extension> _
    Module VirtualKeyTool
        <System.Runtime.InteropServices.DllImport("User32.dll")> _
        Private Function GetKeyboardLayout(ByVal idThread As Integer) As IntPtr
        End Function
        <System.Runtime.InteropServices.DllImport("user32.dll", charset:=Runtime.InteropServices.CharSet.Unicode)> _
        Private Function VkKeyScanEx(ch As Char, dwhkl As IntPtr) As Short
        End Function
        <System.Runtime.InteropServices.DllImport("user32.dll", charset:=Runtime.InteropServices.CharSet.Unicode)>
        Private Function MapVirtualKeyEx(ByVal wCode As UInteger, ByVal wMapType As UInteger, ByVal hkl As IntPtr) As UInteger
        End Function
        Private Const MAPVK_VK_TO_CHAR As UInteger = &H2
    
        Public Function GetMapKeyToChar() As Dictionary(Of Keys, Char)
            Dim dic As New Dictionary(Of Keys, Char)
    
            Dim hkl As IntPtr = GetKeyboardLayout(0) '現在のスレッドに対応するキーボードレイアウトを取得
    
            For i As Integer = 1 To &HFF
                Dim c As Char = ChrW(i)
                If (Not Char.IsControl(c)) Then '制御文字以外
    
                    'キーボードに対応するVKEYとShift,Contorl,Altキーを取得
                    Dim ret As Short = CType(VkKeyScanEx(c, hkl), Keys)
    
                    Dim k As Keys = CType(ret And &HFF, Keys) 'VKEY部分
    
                    'Shift,Contorl,Altキー部分
                    If (ret And &H100) Then
                        k = k Or System.Windows.Forms.Keys.Shift
                    End If
                    If (ret And &H200) Then
                        k = k Or System.Windows.Forms.Keys.Control
                    End If
                    If (ret And &H400) Then
                        k = k Or System.Windows.Forms.Keys.Alt
                    End If
    
                    If (Not dic.ContainsKey(k)) Then
                        dic.Add(k, c)
                    End If
                End If
            Next
    
            'テンキー部分
            For Each k As Keys In From x In [Enum].GetValues(GetType(Keys)) Select x And &HFFFF Distinct
                If (Not dic.ContainsKey(k)) Then
                    Dim r As UInteger = MapVirtualKeyEx(CType(k, UInteger), MAPVK_VK_TO_CHAR, hkl)
                    If (r <> 0) Then
                        Dim c As Char = ChrW(r)
                        If (Not Char.IsControl(c)) Then
                            dic.Add(k, ChrW(r))
                        End If
                    End If
                End If
            Next
            Return dic
        End Function
    
        Private ReadOnly Property DisplayKeys As System.Collections.Generic.IDictionary(Of Keys, Char)
            Get
                If (_DisplayKeys Is Nothing) Then
                    _DisplayKeys = GetMapKeyToChar()
                End If
                Return _DisplayKeys
            End Get
        End Property
        Private _DisplayKeys As System.Collections.Generic.Dictionary(Of Keys, Char)
    
        '拡張メソッドとして定義
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function IsDisplayCharKey(ByVal keyData As Keys) As Boolean
            Return DisplayKeys.ContainsKey(keyData)
        End Function
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function IsDisplayCharKey(ByVal e As PreviewKeyDownEventArgs) As Boolean
            Return DisplayKeys.ContainsKey(e.KeyData)
        End Function
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function IsDisplayCharKey(ByVal e As KeyEventArgs) As Boolean
            Return DisplayKeys.ContainsKey(e.KeyData)
        End Function
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function GetDisplayChar(ByVal keyData As Keys) As Char
            Return DisplayKeys(keyData)
        End Function
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function GetDisplayChar(ByVal e As PreviewKeyDownEventArgs) As Char
            Return DisplayKeys(e.KeyData)
        End Function
        <System.Runtime.CompilerServices.ExtensionAttribute> _
        Public Function GetDisplayChar(ByVal e As KeyEventArgs) As Char
            Return DisplayKeys(e.KeyData)
        End Function
    End Module
    

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2016年4月18日 12:40
  • 東京都_会社員 さん、

    発想を変えて、入力用コントロール(TextBox など)の TextChanged イベントで文字数が増えたかどうかで判断してはいかがでしょう?
    文字数が増えれば制御系以外のキーが押されたことになり、文字数が減れば [Del] や [BackSpace] のようなキー、文字数が変わらなければ制御系のキーが押されたと判断できるのではないかと思います。

    以上です。


    2016年4月19日 0:24
  • kenjinoteさん
    gekkaさん

    やはりAPIを使うって方法になりますよね....

    でもAshidacchiさん方式も捨てがたいですね、
    ちょっとそれでやってみようかと思います。


    ご教示して頂いてみなさん本当にありがとうございます!m(_ _)m


    2016年4月19日 6:02