今回はExcelのユーザーフォームのコマンドボタンで、Back(戻る)ボタンの作成方法をご説明します。
ユーザーフォームを使用してツールを作成していく上で、ある程度の規模のツールになると、ユーザーフォームの数が増えてくるかと思います。
毎回1つ手前のユーザーフォームに戻るコードを、オブジェクト名を変えて登録していては面倒なので、汎用性のあるコマンドボタンを作成してみました。
1つロジックを用意してしまえばあとはコピペのみです。
こちらは自作ロジックですので、必ずしもこの方法しかないというわけではございません。
また、今回の内容はコードの挙動が複雑になり、説明ではうまく伝わらない可能性もありますのでサンプルデータをダウンロードして、コードの挙動を確認して頂いた方がわかりやすいかもしれまん。
コマンドボタンの追加や設定については「コマンドボタンの追加と詳細設定する方法」をご覧ください。
1.Back(戻る)ボタンの動作とサンプルデータ
まずはじめに、Backボタンの動作をご説明します。
今回のサンプルは次のような4階層のユーザーフォームを用意しました。
1階層はTOPとして次のフォームを開くコマンドボタン2つ、2~3階層は次のフォームを開くためのコマンドボタン2つとTOP/Backボタンです。
4階層目はTOP/Backのみとなっています。
少し見づらいですが、この様な構築になっています。(フォームを使いまわせというのはなしで)
各ユーザーフォームのCommand1、2をクリックすると次の階層のユーザーフォームが開きます。
遷移時にはもともと開いていたユーザーフォームは閉じられ新しいフォームが開きます。
ユーザーフォームを遷移した後、Backボタンを押すと1つ手前にオープンしていたユーザーフォームに戻ります。
ユーザーフォームの動作はこちらです。(マウスが写っていませんでした。)
2.Back(戻る)ボタンのロジック
ロジック自体は簡単です。
Commandボタンがクリックされると、次のフォームを開くOpenコードが実行されます。
Openコードが実行されると、「Initialize」イベントが実行されます。
「Initialize」イベント時に開こうとしているフォームオブジェクトとフォームの名前を、それぞれObject変数とString変数に格納します。
そして、フォームの名前をDictionaryのKeyに記録します。
あとは、BackボタンとTOPボタンのクリックイベントにそれぞれのコードを記載します。
Backボタンがクリックされたら、最後に記録したユーザーフォームを記録から削除します。
記録の最後のユーザーフォームをオープンします。(最後の記録を消したので1つ手前が最後になっています。)
TOPボタンは全てのユーザーフォームを閉じて(念のため)、TOPの「UserForm1」を開きます。
TOPが開かれると、リストはリセットされるようにしています。
これにより、どのようなパターンでも順路を保持出来るので、Backボタンをクリックする事で、1つ手前に戻れます。
今回はフォームの右上の「×」ボタンで閉じた場合は、考慮されていないので、×で閉じれないようにするか、×で閉じた場合にも動くようになどの工夫が必要です。
3.サンプルデータのコードと説明
今回のサンプルは「標準モジュール」と「フォームモジュール」の2つにコードを記載しています。
VBAProjectは以下の様になっています。
それぞれご説明します。
標準モジュール
まず、標準モジュールに作成したユーザーフォームを開くコードと、TOPボタン、Backボタンの
コードを記載します。
下記コードは全て標準モジュールのModule1に書かれたコードです。
ユーザーフォームをオープンするコード
長いですが、単純に各フォームのオープンをしているだけです。
ユーザーフォームを指定して、「Show vbModeless」でオープンしています。
「StartUpPosition = 1」は表示位置で、1はアプリケーションの中央です。
Option Explicit
Public FormDic As Object
Public ActUserForm As Object
Sub UserForm1Open()
With UserForm1
.Show vbModeless
.StartUpPosition = 1
End With
End Sub
Sub UserForm1_1Open()
With UserForm1_1
.Show vbModeless
.StartUpPosition = 1
End With
End Sub
Sub UserForm1_2Open()
With UserForm1_2
.Show vbModeless
.StartUpPosition = 1
End With
End Sub
'================中略=========================
'UserForm2_1~UserForm3_7は同じなので省略
Sub UserForm3_8Open()
With UserForm3_8
.Show vbModeless
.StartUpPosition = 1
End With
End Sub
TOPとBack、フォームを記録するコード
次にTOPとBackボタンと、開いたフォームの順路を記録するコードです。
こちらも全て標準モジュールのModule1に書かれています。
TOP
TOPを開く際には、もし閉じることを失敗したフォームが存在していた場合を想定して、念のためすべてのフォームを一度閉じています。
全て閉じた後、TOPの「UserForm1」を開いています。
Function TOPForm()
'TOPを開くコード
Dim UF As UserForm
For Each UF In UserForms
Unload UF
Next
Call UserForm1Open
End Function
Back
Backは最後(現在)に開いているユーザーフォームを「FormDic.Remove (FormName)」で記録から削除して、フォームを閉じます。
そのあとに、一つ前に記録しているフォームを「Show vbModeless」で表示しています。
Function BackForm(ByVal FormName As String)
'Backするコード
FormDic.Remove (FormName)
Unload ActUserForm
UserForms.Add(FormDic.keys()(FormDic.Count - 1)).Show vbModeless
End Function
開いたユーザーフォームの順路を記録する
こちらは各Commandボタンがクリックされた際に、実行されるコードです。
なので、次のフォームが開く前に現在開いているフォームを先に閉じています。
閉じたあとに、次のフォームが未登録であれば、Dictionaryに記録します。
Function NewAddForm(ByVal FormName As String)
'開いたフォームを記録するコード
If FormDic.Count <> 0 Then
Unload ActUserForm
End If
If Not FormDic.Exists(FormName) Then
FormDic.Add FormName, 1
End If
End Function
フォームモジュール
フォームモジュールは大きく分けて2つのパターンがあります。
TOPのUserForm1が開かれたパターンと、それ以外のフォームが開かれたパターンです。
TOPのUserForm1
TOPはCommandボタン1と2が押された際のOpenの呼び出しを、それぞれクリックイベントに記載します。
InitializeイベントにはTOPのフォームをObject変数、フォーム名をString変数に格納します。
そして、順路記録用のDictionaryをリセットの意味も含め、ここでSetします。
最後に標準モジュールに記載した、順路を記録するコードを実行します。
Option Explicit
Private Sub CommandButton1_Click()
Call UserForm1_1Open
End Sub
Private Sub CommandButton2_Click()
Call UserForm1_2Open
End Sub
Private Sub UserForm_Initialize()
Dim FormName As String
Set ActUserForm = Me
FormName = Me.Name
Set FormDic = CreateObject("Scripting.Dictionary")
NewAddForm (FormName)
End Sub
TOP以外のフォームのコード
TOP以外のコードは、Command1と2のクリックイベントに、次のフォームを開くためのコードの呼び出しを記載します。
TOP、Backのクリックイベントには、それぞれ「BackForm 」と「TOPForm」の呼び出しを記載します。
最後にInitializeイベントで新しく開くフォーム名をString変数に格納して、DictionaryのKeyに順路を記録します。
そして、記録後に新しいフォームオブジェクトをObject変数に格納します。
TOPのUserForm1と違い、フォームのオブジェクトを最後に記載しています。
Option Explicit
Dim FormName As String
Private Sub CommandButton1_Click()
Call UserForm2_1Open
End Sub
Private Sub CommandButton2_Click()
Call UserForm2_2Open
End Sub
Private Sub BackButton_Click()
BackForm (FormName)
End Sub
Private Sub TOPButton_Click()
TOPForm
End Sub
Private Sub UserForm_Initialize()
FormName = Me.Name
NewAddForm (FormName)
Set ActUserForm = Me
End Sub
4階層目はCommandボタンがないため、TOPとBackと順路の記録のみ記載です。
4.サンプルデータのダウンロード
こちらから今回使用したサンプルデータをダウンロードできます。
それぞれの環境に合わせるのは簡単ではないかもしれませんが、TOP、Back、順路記録をそれぞれうまく組み込んで頂けると、使用できるかもしれません。
5.まとめ
今回は難しいVBAを使用したわけではなく、1つ1つを見ると簡単なコードのみ使用しています。
簡単なコードを組み合わせて、ロジック部分を構築する際にいろいろ工夫してみました。
僕も難しいVBAはわかりませんが、ロジックを考えて組み合わせる事で、大抵のことはできると思います。