How to execute formal validation of TextBox on user form with EXCEL VBA?

In this article, I’d like to describe how to execute formal validation of TextBox on user form. Data type of TextBox is datetime, numeric and such string as zip code, phone number or mail address, etc. Until Excel 2003, you could divert calendar control of Access®, but after 2007, you couldn’t. You might have to type keyboard, design custom calendar or use Add-In calendar.

validation

As shown in figure above, set TextBoxes on user form, from TextBox1 to TextBox5. And set CommandButton.

Formal validation

Validation means formal validation and semantic validation. In this article, I’d like to describe about formal validation. Formal validation has verification of data type and not entered controls. You could verify not entered controls at last, it might be executed in a batch, when CommandButton for registration was clicked. However, because data type of each controls are different from each other, it might be appropriate to verify data type each time when input is entered.

Semantic validation

Although I would not describe about semantic validation in this article, it’s needed not only verifying input of one control, but also comparing input of multiple controls each other and compareing input with record in database. In the situation, it’s needed to execute validation when registration button was clicked, not to hook individual events of each controls.

Event type

Then, which event should you use? When user have entered incorrect input, you would not confirm input and would not move focus to the next control. Therefore it’s appropriate to use event with cancel. Typical event of TextBox is bellow.

  • BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
  • Change()
  • Exit(ByVal Cancel As MSForms.ReturnBoolean)
  • KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
  • KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
  • KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)

Events with cancel process are BeforeUpdate and Exit. With such other events as Change, you would have to design custom code to stop process. BeforeUpdate event is usually used.

Option Explicit

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox3_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox4_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox5_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Specified criteria for validation

It’s important that BeforeUpdate event executes cancel process when criteria you described in event procedure has been satisfied. It’s needed to deny criteria expression with Not that allows to input. When criteria has been satisfied, cancel process would be executed.

Validation of data type

Data type of each TextBoxes are datetime, numeric and string. String has mobile phone number, mail address and URL, respectively. After cancel process, following code set focus and select whole input string. In TextBox1, following code verifies whether data type of input is datetime, in textBox2, the code verifies whether data type of input is numeric, respectively. In textBox2, non-negative number would be accepted.

Validation with regular expression

After TextBox3, following code doesn’t verify data type. It’s not needed to verify data type when you deal input as string. Therefore, you would have to verify string itself. In TextBox3, it is allowed to input eleven digit number. In TextBox4, it’s allowed to input string by matching with regular expression as shown in 44 line. It’s so difficult to match e-mail address that following code is incomplete. In TextBox5, it matches with URL. As well as e-mail, it’s incomplete.

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    With TextBox1
        If Not IsDate(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    With TextBox2
        If Not (IsNumeric(.Text) And .Text >= 0) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox3_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Set myReg = CreateObject("VBScript.RegExp")
    With myReg
        .Pattern = "^[0-9]{11}$"
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox3
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox4_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Dim Pattern     As String
    Pattern = "^[-a-z0-9]+(\.[-a-z0-9]+)*\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z0-9]{2,6}$"
    Set myReg = CreateObject("VBScript.RegExp")
    With myReg
        .Pattern = Pattern
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox4
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox5_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Dim Pattern     As String
    Set myReg = CreateObject("VBScript.RegExp")
    Pattern = "^https?://[a-z0-9][-a-z0-9]{0,62}(\.[a-z0-9][-a-z0-9]{0,62})*\.[a-z0-9][a-z0-9]{0,62}$"
    With myReg
        .Pattern = Pattern
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox5
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Refferences:
DOD INTERNET HOST TABLE SPECIFICATION
Requirements for Internet Hosts — Application and Support
Hostname (Wikipedia)
Userform of Excel VBA as user interface

Excel VBAでユーザーフォームのテキストボックスの値の形式的検証を行う

 ユーザーフォーム上のテキストボックスの値を検証します.テキストボックスに入力する値のデータ型としては日付型,数値型,郵便番号や電話番号・メールアドレスなどの文字列型が主体です.日付型に関しては Excel 2003 までは Access® のカレンダーコントロールを流用できたのですが,最近のバージョンではそれもできなくなりました.キーボードからタイプするか,カレンダーを自作するか,関数アドインを利用するしかありません.

validation

 上図のようにフォーム上にテキストボックスを配置します.それぞれ TextBox1, TextBox2, TextBox3, TextBox4, TextBox5 としましょう.またコマンドボタンを配置し,Caption を OK に変更します.

形式的検証

 検証には形式的検証と意味的検証とがあります.まず形式的検証についてです.未入力のチェックとデータ型のチェックが主体となります.両者のチェックのタイミングは別の方がよいでしょう.タイミングが異なるため,異なるイベントにすべきです.未入力のチェックは値が空白であるか否かだけを確認すれば良いため,一括して検証可能でしょう.となると個々のコントロールのイベントで都度チェックするのではなく,最後に登録するコマンドボタンが押された時点でよいということになります.逆にデータ型はコントロールごとに異なります.ですので個々のテキストボックスに入力が発生した時点で都度検証するのがよいでしょう.

意味的検証

 意味的検証についてここでは詳述しませんが,単独のコントロールの入力値を検証するだけでなく,複数のコントロールの入力値同士を比較して検証する必要があったり,データベースに既に登録されたレコードの値と比較したりといった検証が必要になる場合もあります.そのような場合には個々のコントロールのイベントをフックするのではなく,最後に登録するボタンが押された時点で検証するのが妥当だと思います.そういったチェックは Access® の場合ですとテーブルに対するイベントとして登録する必要があります.

イベントの種類

 話をイベントに戻します.では次にどのイベントを用いるべきでしょうか.意図しない入力が発生した場合は入力を確定させず,次のコントロールにフォーカスを移動させたくないのですから,Cancel 処理が入っているイベントが適切です.テキストボックスのイベントで代表的なものは下記のとおりです.

  • BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
  • Change()
  • Exit(ByVal Cancel As MSForms.ReturnBoolean)
  • KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
  • KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
  • KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)

 Cancel 処理を持つのは BeforeUpdate と Exit です.Change イベントを始め他のイベントでは処理を止めるために自前でコーディングする必要があるでしょう.BeforeUpdate と Exit とでいずれが適切かは個々の要件によりますが,通常は BeforeUpdate でよいでしょう.それぞれのコントロールで BeforeUpdate イベントを選択した状態は下記のようになります.

Option Explicit

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox3_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox4_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

Private Sub TextBox5_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)

End Sub

検証の条件指定

 次にデータ型をそれぞれ検証します.ここで強調したいのは,キャンセルする際の条件を満たした場合にキャンセル処理を行うコードを記述することです.その条件には,逆に入力を許可する条件式を指定しておき,最後に Not で条件式全体を否定するというちょっと複雑な処理が必要です.If … Then … ElseIf … と条件分岐を連ねていく方法もありますが,その都度キャンセル処理を書き連ねていく必要があります.ここは好みの問題かもしれませんが,ド・モルガンの法則からすると,And で条件を絞り込み,最後に Not で式全体を否定するのがスマートかと思います.そして Cancel = True としてキャンセル処理を実行します.では逆に入力を満たす条件をそのまま記述し,Cancel = False とすればどうなるでしょうか.これは宿題にしておきましょう.

データ型の検証

 さて,話を戻します.各テキストボックスのデータ型は順に日付型,数値型,電話番号・メールアドレスおよび URL の文字列型です.TextBox1 から TextBox5 までいずれもキャンセル処理後フォーカスを残し,かつ入力した値全体を選択して即座にタイプできるようにしています.TextBox2 では非負の数値のみを受け付けます.

正規表現による文字列の検証

 TextBox3 以降ではデータ型の検証をしていません.文字列型として扱う場合はデータ型を検証する必要がないからです.したがって入力された文字列そのものを検証する必要があります.TextBox3 では 11 桁の半角数値以外の入力を許可していません.TextBox4 においては正規表現を使って 44 行目のようにパターンにマッチさせる文字列を指定します.メールアドレスの正規表現によるマッチングはかなり複雑らしく,例文では不十分です.TextBox5 では URL にマッチさせます.メールアドレス同様,例文の検証法も不十分です.

Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    With TextBox1
        If Not IsDate(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox2_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    With TextBox2
        If Not (IsNumeric(.Text) And .Text >= 0) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox3_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Set myReg = CreateObject("VBScript.RegExp")
    With myReg
        .Pattern = "^[0-9]{11}$"
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox3
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox4_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Dim Pattern     As String
    Pattern = "^[-a-z0-9]+(\.[-a-z0-9]+)*\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z0-9]{2,6}$"
    Set myReg = CreateObject("VBScript.RegExp")
    With myReg
        .Pattern = Pattern
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox4
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

Private Sub TextBox5_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim myReg       As Object
    Dim Pattern     As String
    Set myReg = CreateObject("VBScript.RegExp")
    Pattern = "^https?://[a-z0-9][-a-z0-9]{0,62}(\.[a-z0-9][-a-z0-9]{0,62})*\.[a-z0-9][a-z0-9]{0,62}$"
    With myReg
        .Pattern = Pattern
        .IgnoreCase = True
        .Global = True
    End With
    With TextBox5
        If Not myReg.Test(.Text) Then
            Cancel = True
            .SetFocus
            .SelStart = 0
            .SelLength = Len(.Text)
        End If
    End With
End Sub

参照:
サイト URL、フォルダ名、ファイル名に使用できない文字 (Microsoft®)
Active Directory 内のコンピューター、ドメイン、サイト、および Ou の名前付け規則 (Microsoft®)
ホスト名 (Wikipedia)
インターフェースとしてのEXCEL VBAによるユーザーフォーム