はじめに
最近は、Excel VBAでプログラミングをしておらず、作成済みのものを使用していました。
ふとした用途でユーザーフォームを使用してみたところ、ユーザーフォームとモジュールのデータ引き渡しタイミングで悩んでしまったので、備忘として残しておこうかと思いました。
どうでもいいですが、QiitaのMarkDown記載変わったんですね。本当に投稿していなかったなと今更に気づきました。
今回実施する内容
ユーザーフォーム(モーダル)を使用して、そこに何かの値を設定し、「OK」ボタンを押したときに、その値を取得するものを作ります。
今回は、テキストボックスの値を取得して、OKボタンを押すとMsgBoxを開くものにします。
その値をどこに設定すべきなのか方法は色々ありますが、あまりPublic変数は使いたくないため、関数の戻り値として取得する方法でおこないます。
ソースコード(Git Hub)
環境
OS:Windows 10 JP
Excel: Excel 2021 (64bit)
参考
用語
特になし
標準モジュールのソース
Option Explicit
Public Sub FormDataTransfer()
Dim strTextValues() As String
Dim i As Long
Dim strMsg As String
strTextValues = UserForm1.GetTextValues
For i = LBound(strTextValues) To UBound(strTextValues)
strMsg = strMsg & strTextValues(i) & vbCrLf
Next
MsgBox strMsg
End Sub
特に説明すべきこともないのですが、UserForm1.GetTextValuesで、ユーザーフォームを表示させて、そこからの戻り値をstrTextValues
の配列に代入しています。
その後のFor分は単なる取得したデータをつなげて表示しているだけです。
UserForm1の構成
ユーザーフォーム(UserForm1)は、今回のケースでは、テキストボックスを2つ作り(TextBox1
とTextBox2
)、OKボタン(OKButton
)ときキャンセルボタン(CancelButton
)を作るシンプルなものです。
- OKボタンを押すと、ユーザーフォームが閉じられ、2つのテキストボックスの値を応答します。
- Cancelボタンを押すと、ユーザーフォームが閉じられ、やはり2つのテキストボックスの値を応答します。
- 右上の「x」ボタンを押すと、ユーザーフォームが閉じられ、やはり2つのテキストボックスの値を応答します。
UserForm1のソース
Option Explicit
Private strTextValues(1 To 2) As String
'OKボタンを押下したときに実行する。
Private Sub OKButton_Click()
strTextValues(1) = TextBox1.Value
strTextValues(2) = TextBox2.Value
Me.Hide
End Sub
'Cancelボタンを押下したときに実行する。
Private Sub CancelButton_Click()
Me.Hide
End Sub
'xボタンを押下したときに実行する。
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
Cancel = True
Me.Hide
End If
End Sub
'モジュールから呼び出される。
'ユーザーフォームを開いて、テキストボックスに値を入れた後、OKボタン、もしくはCancelボタンを
'押下すると、そのテキストボックスの値を戻り値として返す。
Public Function GetTextValues() As String()
Me.Show
GetTextValues = strTextValues
End Function
OKButton_Click
とCancelButton_Click
のSub関数は、OKボタンを押したとき、Cancelボタンを押したときに実施されます。
OKボタンを押したときは、テキストボックスの値を取得して、strTextValues
の配列に値を代入します。
その後、UserForm1を非表示にします。
Cancelボタンを押したときは、単にUserForm1を非表示にします。
UserForm_QueryClose
のSub関数は、「x」ボタンを押したときに実施されます。
CloseMode
がvbFormControlMenu
の場合は「x」ボタンを押したときのため、Cancel = True
として、Me.Hide
でUserForm1を非表示にします。
GetTextValues
の関数は、標準モジュールから呼び出され、UserForm1を表示し、UserForm1が閉じられると、strTextValuesの配列の値を応答します。
#データ引き渡しのタイミングについて
ユーザーフォームのデータ引き渡しで何に迷ったかといえば、2つありました。
- UserForm1を閉じたときにどうやって値を戻すのか?
UserForm1.Show
ってどうなると終わるのか? - OKボタンやCancelボタンを押したときにSub関数が発生しますが、これはSub関数であって戻り値がないので、どうやって値を渡すのか?
久しぶりに作った時は???となって困ったんです。考えれば大したことはないのですが。
Show メソッドをみると以下の記載があります。
UserForm がモーダルの場合、ユーザーはアプリケーションの他の部分を使用する前に応答する必要があります。 UserForm が非表示またはアンロードされるまで、その後のコードは実行されません。 UserForm が表示されたとき、アプリケーション内のその他のフォームになっていますが、その他のアプリケーションは無効ではありません。
上記の通り、モーダルの場合UserForm.Show
が非表示、アンロードされるまではその後のコードは実行されませんから、それが終わってからテキストの値を取り出せばいいんだなということでした。
UserFormがアンロードされる場合は、Public変数のstrTextValues
も消えてしまうため、Unload
は使えないですね。したがって、Me.Hide
で非表示にするようにしました。
また、OKボタンを押したとき、CanCelボタンを押したときについては、これもすでに上に記載した通りで、Me.Hide
すれば、UserForm.Show
が終わるわけですからそのあとに値を取得するだけでよかったのです。
「x」ボタンを押したときも同じですね。
GetTextValues
でMe.Show
の後にstrTextValues
を取得してその値を応答します。
GetTextValues
は、関数名で言えば値取得だけなのに、UserFormも表示させているため、思わしくないかもしれません。UserForm.Show
はそれだけにして、あとからプロパティとして、strTextValues
を取得するでもよかったかもしれません。
いずれにせよ、ポイントは、
- OKボタンを押したときに値を取得してPublic変数に保存しておく。
-
UserForm.Show
が終わったらそのPublic変数の値を取得する。
ってことかなと思いました。
UserForm.Show
の動きを忘れなければ問題ないはずなんですが・・・
#おわりに
ユーザーフォームとモジュールのデータ引き渡しタイミングについて記載しました。
モードれすで実施すると、Me.Showで止まらずすぐさまGetTextValues = strTextValues
が実行されてしまうため、問題です。まあなんとかすればいいんでしょう。
本ソースは、単なるポイントを記載したかっただけなので、MsgBoxがCancelでも表示されるとかは気にしていません。