マクロ実行中にユーザーから文字列や数値を受け取るとき、最も簡単な方法のひとつはInputBoxを使うことです。次のコードは、ユーザーに名前を入力してもらい、入力された文字列をセルA1に書き込みます。
Sub Sample1() Dim buf As String buf = InputBox("名前を入力してください") Range("A1") = buf End Sub
特に難しいテクニックでもなく、ややもするとVBAの解説本でさえ紹介されないこともある基本ワザですが、実はこのInputBoxは意外と奥が深いんです。
まず衝撃的な事実からお話ししましょう。Excel VBAで使えるInputBoxには次の2種類があります。
ひとつはInputBox関数です。文字列の左端を抜き出すLeft関数や、文字の種類を変更して返すStrConv関数などと同じ関数です。ふたつめはApplicationオブジェクトのInputBoxメソッドです。ApplicationオブジェクトはExcel本体を表しますから、InputBoxメソッドはExcel固有の機能だといえます。
両者は何が違うのでしょう?実は動作や仕様が微妙に違うのです。InputBoxメソッドには関数にない「Type」という引数を使えます。引数の意味については次項で解説します。なお本コンテンツで「InputBox」と種類を明記しないときは、関数とメソッドの両方を指すものとします。
InputBox関数
InputBox(Prompt,[Title],[Default],[XPos],[YPos],[HelpFile],[Context])
InputBoxメソッド
Application.InputBox(Prompt,[Title],[Default],[XPos],[YPos],[HelpFile],[HelpContextId],[Type])
InputBox("名前を入力してください") のように使われることが多いInputBoxですが、前項のようにInputBoxはたくさんの引数が用意されています。順番に見ていきましょう。
・Prompt
必ず指定します。InputBoxの中で唯一必須の引数はこのPromptだけです。他はすべて省略できます。
PromptにはInputBoxに表示する文字列を指定します。指定できるのは文字列・数値・日付・ブール型(True/False)ですが、実行される前にVBAがすべて文字列に変換してくれます。Promptに指定した文字列は通常1行で表示されます。1行に何文字表示されるかはフォントの種類や画面の解像度などによって異なります。明示的に改行したいときは、vbCrLfという定数を使いましょう。vbCrLfは、Chr(13)+Chr(10)の意味です。Chr(13)だけ、またはChr(10)だけでも改行することができますけどvbCrLfを使う方がスマートです。
Sub Sample2() Dim buf As String, msg As String msg = "ここに住所を入力してください。" & vbCrLf & _ "ただし、東京都・横浜市にお住まいの方は区からお願いします。" buf = InputBox(msg) Range("A1") = buf End Sub
引数Promptに指定できる文字数は次の通りです。
種類 | 文字数 |
---|---|
関数 | 全角で512文字、半角では1024文字まで表示される |
メソッド | 全角で160文字、半角では225文字まで表示される |
画面上に表示される文字数はフォントの種類などによって異なります。ちなみに使用するフォントの種類や文字サイズを指定することはできません。そうしたことを望む方はUserFormで自作しましょう。また、上記のようにInputBoxメソッドは巨大な文字数を引数Promptに渡すとエラーが発生します。それがバグかどうかは重要ではありません。大事なことは、その事実を忘れずに運用面で注意することです。
・Title
ダイアログボックスのタイトルを指定します。省略可能です。省略したときに表示される文字列は、関数とメソッドによって異なります。
種類 | 省略したときタイトルに表示される文字列 |
---|---|
関数 | Microsoft Excel |
メソッド | 入力 |
・Default
ダイアログボックスを表示したとき、最初から入力ボックスに表示しておく文字列を指定します。
Sub Sample3() Dim buf As String buf = InputBox(Prompt:="住所を入力してください。", Default:="横浜市保土ヶ谷区") ActiveCell = buf End Sub
Defaultに表示する文字列に改行が含まれていた場合、ダイアログボックス上では改行されません。入力用のボックスが1行しか表示できないのですから当然ですね。しかし、実際には改行コードが含まれているのですから、返り値をセルに入力すると改行されます。
Sub Sample3() Dim buf As String buf = InputBox(Prompt:="住所を入力してください。", Default:="横浜市" & vbCrLf & "保土ヶ谷区") ActiveCell = buf End Sub
そのままEnterキーを押すと
となります。
・XPosとYPos
ダイアログボックスを表示する位置を座標で指定します。
Sub Sample4() Dim buf As String buf = InputBox(Prompt:="住所を入力してください。", XPos:=1000, YPos:=2000) ActiveCell = buf End Sub
XPosとYPosに指定する座標の単位はTwipsです。注意しなければならないのは、座標の起点(0,0)はワークシートの左上でも、Excelの左上でもなく、Windowsのスクリーン左上になるということです。したがって、Excelをウィンドウ状態にして実行すると次のようなことになります。
引数XPosを省略するとダイアログボックスは水平方向に対して中央に表示されます。引数YPosを省略するとダイアログボックスは垂直方向に対して上から1/3の位置に表示されます。
表示するダイアログボックスの位置を厳密に指定するのは難しいです。そんな苦労をするよりは、そもそも厳密に位置を指定しなければならないのか…と仕様を検討するべきでしょう。
・HelpFileとContext(HelpContextId)
HelpFileにはヘルプファイルを指定します。ContextまたはHelpContextIdにはヘルプファイル内のページを示すコンテキストIDを指定します。
InputBox関数の場合、引数HelpFileを指定すると[ヘルプ]ボタンが表示されます。クリックすると、指定したヘルプファイルの指定したページを表示します。ヘルプファイルにはパスを指定できます。ヘルプファイルが見つからない場合、InputBox関数では「ヘルプファイルが見つからない」というメッセージが表示されます。
Sub Sample5() Dim buf As String buf = InputBox(Prompt:="住所を入力してください。", HelpFile:="test.hlp", Context:=2) ActiveCell = buf End Sub
InputBoxメソッドでは[ヘルプ]ボタンが表示されません。[F1]キーを押すとヘルプが表示されるらしいです。
関数とメソッドで引数の名前が異なります。名前付き引数を使用するときは注意してください。
実際に試してみました。
やってみたこと | 関数 | メソッド |
---|---|---|
1.[ヘルプ]ボタンをクリック | 表示される | 表示されない |
2.[F1]キーを押す | 動作しなかった | 動作しなかった |
3.Context(HelpContextId)を省略 | エラーになる | エラーにならない |
4.HTML形式のヘルプ(*chm)を指定 | エラーになる | エラーにならない |
関数もメソッドも[F1]キーを押してもヘルプが表示されませんでした。これはもしかすると、私の環境が問題なのかもしれません。
3.は、引数HelpFileだけを指定して引数Context(HelpContextId)を省略した場合です。関数では実行時にエラーが発生します。
関数で指定できるヘルプの形式は昔のHLPファイルだけのようです。最近主流のHTML形式ヘルプ(*.chm)を指定すると、やはり実行時にエラーが発生しました。
上記のテストを見ると、要するにInputBoxメソッドではヘルプ機能が使えないようです。繰り返しますが、これは私の環境が原因なのかもしれません。いずれにしても[ヘルプ]ボタンで独自のヘルプを表示するには独自のヘルプファイルを作成しなければなりません。ヘルプファイルを自作できない人は、まず自作できるようになってください。また、ヘルプを起動するにはShell関数など他の方法もあります。どうしても自作のヘルプを表示したいのでしたら、UserFormを使った方がいいでしょう。
・Type
InputBoxメソッド最大の武器は、この引数Typeです。InputBox関数では引数Typeを指定できません。引数Typeには、InputBoxメソッドが返すデータの型を指定します。指定できるのは次の値です。
値 | 型 |
---|---|
0 | 数式 |
1 | 数値 |
2 | 文字列 |
4 | 論理値(True/False) |
8 | セル参照(Rangeオブジェクト) |
16 | エラー値(#N/Aなど) |
64 | 数値配列 |
複数のデータ型を受け取りたいときは値を加算します。文字列または論理値を受け取りたいときは「2+4」で「6」を指定します。
Type:=0
数式を受け取ります。数式とは、セルに入力したときに計算可能な文字列のことです。たとえば「=SUM(A1:B2)」のような関数や「=A1+B2」みたいな参照式を指します。InputBoxメソッドのダイアログボックスに入力する数式も「=」で始まらなければなりません。「SUM(A1:B2)」のように「=」を付けないで入力すると、セルには「="SUM(A1:B2)"」と間抜けな結果が入力されてしまいます。また、セルに数式を入力したいのですから、本来はRangeオブジェクトのFormulaLocalプロパティに代入するべきです。しかしExcelでは、先頭が「=」で始まる文字列を自動的に数式と認識してくれますので、文字列や数値を代入するのと同じようにValueプロパティを使うこともできます。
Type:=1
数値を受け取ります。これと次の「2」が一般的によく使われます。「1」を指定すると数値以外は受け取れなくなります。たとえばユーザーが数値以外のデータを入力して[OK]ボタンを押すとアラートが表示されます。
もちろん「12人」や「第3」のように純粋な数値でない場合も入力できません。「=1+2」や「=A1+10」のような数式も入力できますが、返り値は計算結果となります。簡易電卓代わりにも使えますが、先頭に「=」または「+」を付けるのを忘れないでください。
Type:=2
文字列を受け取ります。ここでいう文字列とは「純粋な数値でない」という意味です。「12人」「第3」のように数値を含んでいても文字列に該当します。Type:=0でも書きましたが、Excelは先頭が「=」で始まる文字列を自動的に数式と判断します。したがって、Type:=2で文字列を受け取るようにしておいても「=1+2」や「=A1+10」などは計算されて結果が文字列として返ります。
Type:=4
論理値を受け取ります。Excelでは、Falseは0と定義されていますが、Trueは「Falseでない数値」となります。したがって、Type:=4を指定して数値の「1」を入力するとTrueと判断されます。Falseになる数値は「0」だけです。論理値に変換できない文字列を指定するとアラートが表示されます。
「=A1」のような参照式も入力できますが、参照するセルの値が論理値に変換できないと入力できません。
Type:=8
Rangeオブジェクトを受け取ります。セルを指定させるときに便利です。返り値はセルの値ではなくRangeオブジェクトですので次のようなことができます。
Sub Sample6() Dim buf As Range Set buf = Application.InputBox(Prompt:="セルを選択してください。", Type:=8) MsgBox buf.Address(False, False) & "の文字色は" & buf.Font.ColorIndex & "です" End Sub受け取ったRangeオブジェクトを変数に代入するときは、Setステートメントを忘れないでください。
セルを選択するときは、選択したいセルをクリックするか、選択したいセル範囲をドラッグできます。
Type:=16
エラー値を受け取ります。どんなときに便利なのか、良い例が思い浮かびません。とにかくエラー値だけを受け取ります。
Type:=64
数値配列を受け取ります。たとえば次のような感じですけど、こんな使い方をする人は少ないでしょうね。
Sub Sample7() Dim buf As Variant buf = Application.InputBox(Prompt:="データを入力してください。", Type:=64) Selection = buf End Sub
実際のマクロでは、InputBoxの返り値を判定する処理が必要です。なぜなら、[キャンセル]ボタンがクリックされた場合は処理を中止しなければならないからです。では[キャンセル]ボタンがクリックされると、どんな値が返るのでしょうか。
InputBox関数の場合
空欄("")が返ります。次のように判定するといいでしょう。
Sub Sample8() Dim buf As String buf = InputBox(Prompt:="住所を入力してください。") If buf = "" Then Exit Sub ActiveCell = buf End Sub
もし[キャンセル]ボタンによるスキップを認めず、必ず何かしらのデータを入力してもらうのでしたら、次のようにする手もあります。
Sub Sample8() Dim buf As String Do buf = InputBox(Prompt:="住所を入力してください。") If buf = "" Then MsgBox "この項目は省略できません", vbExclamation Loop While buf = "" ActiveCell = buf End Sub
InputBoxメソッドの場合
InputBoxメソッドでは[キャンセル]ボタンがクリックされるとFalse(論理値)が返ります。
Sub Sample9() Dim buf As String buf = Application.InputBox(Prompt:="住所を入力してください。") If buf = "False" Then Exit Sub ActiveCell = buf End Sub
[キャンセル]ボタンがクリックされたかどうかを判定するには、「buf = "False"」と文字列で比較する方法と「buf = False」のように論理値として比較する方法があります。しかし、受け取る値を制限する引数Typeを指定している場合には、受け取る変数の型もさまざまでしょう。たとえば次のコードは、当然ですがエラーになります。
Sub Sample10() Dim buf As Long buf = Application.InputBox(Prompt:="数字を入力してください。", Type:=1) If buf = "False" Then Exit Sub ActiveCell = buf End Sub
数字だけを受け取れるようにと引数Typeに「1」を指定しました。返り値は数字のはずですから、受け取る変数の型は長整数型(Long)にしています。しかし[キャンセル]ボタンがクリックされたかどうかの判定で「"False"」を使ってます。Long型の変数と文字列を比較することはできませんから、このコードはエラーになります。できるだけ論理値「False」と比較する方がいいでしょう。
上述したように、InputBox関数で[キャンセル]ボタンをクリックすると空欄("")が返ります。なので、[キャンセル]ボタンがクリックされたかどうかは、返り値が空欄("")かどうかを判定すればいいです。しかし、こんな使い方は滅多にありませんが、万が一、何らかの事情で、[キャンセル]ボタンがクリックされたことによる空欄("")と、空欄("")のまま[OK]ボタンがクリックされたことを区別したいとしたらどうでしょう。
難しく考える必要は何もありません。InputBoxメソッドを使ってください。InputBoxメソッドなら、[キャンセル]ボタンがクリックされた結果のFalseと、空欄("")のまま[OK]ボタンがクリックされたのを判別できます。それだけの話です。
どうです?InputBoxもけっこう奥が深いでしょ。