2つのフォームを排他表示させるサンプル

ユーザーフォームを2つ交互に表示させるサンプルです。
このようなケースでうまく行かない方はけっこう多いようです。 元のフォーム(フォーム1)をそのままにして、2つ目のフォーム(フォーム2)を上に表示させるなら何の問題はありませんが、フォーム2を表示させる時に、フォーム1を消しておきたい場合の問題について説明します。(3種類のサンプルの違いを確認して下さい。)
皆さんは「Unload Me」とか「Me.Hide」の動作に誤解があるようです。例えば「Unload Me」をフォーム上のボタンの処理に書いて、その後に別フォームを表示させるように記述したら、 そのフォームがアンロードされても以降のコードは実行されるのでしょうか。



まず、明らかに間違っているコードを見てみましょう。
「フォーム1」→「フォーム2」→「フォーム1」・・・と交互に表示されるようなイメージです。
「フォーム2」は例えば検索画面のようなもので、「フォーム1」に戻る場合にその検索結果を「フォーム1」に返さなければなりません。
2つのフォームの排他表示サンプル
(画像をクリックすると、このサンプルがダウンロードできます)
ブックを起動させると、このように起動マクロで「フォーム1」が表示されます。切り替えの確認を明確にするため、「フォーム1」は大きめ、「フォーム2」は小さめにしてあります。
ここで、「フォーム2の表示」ボタンをクリックすれば、「フォーム2」に切り替わりますが、先にマクロのコード記述を説明しておきます。

起動マクロ(Workbook_Open)はこのようになっています。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル①                        ThisWorkbook(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ ワークブックイベント ■■■
'***************************************************************************************************
'* 処理名 :Workbook_Open
'* 機能  :起動プロシージャ
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub Workbook_Open()
    '-----------------------------------------------------------------------------------------------
    ' フォーム1の表示
    UserForm1.Show
    ' 終了メッセージ
    MsgBox "終了です。"
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
このように、「フォーム1」がHideすると「終了です。」のメッセージが表示されるようになっています。

「フォーム1」のコードは「フォーム2の表示」ボタンのクリックイベントのみです。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル①                        UserForm1(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ コントロールイベント ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    ' 自フォームをHide
    Me.Hide
    ' フォーム2を表示
    UserForm2.Show
    ' フォーム2のHide後のメッセージ
    MsgBox "フォーム2が閉じられました。"
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
自フォームをHideさせて、「フォーム2」を表示、「フォーム2」がHideされた後の処理を想定して、メッセージを表示させるようにしておきます。

「フォーム2」のコードは「フォーム1に戻る」ボタンのクリックイベントのみです。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル①                        UserForm2(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ コントロールイベント ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    ' 自フォームをHide
    Me.Hide
    ' フォーム1を表示
    UserForm1.Show
    ' フォーム1のHide後のメッセージ
    MsgBox "フォーム1が閉じられました。"
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
自フォームをHideさせて、「フォーム1」を表示、「フォーム1」がHideされた後の処理を想定して、メッセージを表示させるようにしておきます。
※ここでは「Hide」で記述しています。繰り返し表示させることが分かっているので「Unload」では効率が悪いだろうとの判断ですが、「Unload」で記述してもこの先の説明の通りで動作結果は同じです。

では、起動させて、フォームが交互に表示するかを確認してみましょう。
「フォーム2」に切り替わったところ
「フォーム2の表示」ボタン→「フォーム1に戻る」ボタン→「フォーム2の表示」ボタン・・・を何回か繰り返してみて下さい。

ボタンのクリックを何回か繰り返したら、ちょっと考えてみましょう。
通常、ユーザーフォームの表示では、Hideメソッドや閉じる[×]ボタンで閉じられると、Showメソッド以降のコードが実行されるはずです。
ですが、上記で各ボタンのクリックイベントに記述させたメッセージは表示されません。

では、どちらかのフォームで閉じる[×]ボタンをクリックしてみて下さい。
閉じた後にメッセージが続く
このように、各ボタンのクリックイベントに記述させたメッセージが交互に表示されます。
つまり、「フォーム2」が検索画面だとして、「フォーム1」からのShowメソッドの後に検索結果を編集する記述があると、「フォーム1」に戻っても実行されないのです。
このような方法で交互にフォームを表示させる場合は、各Showメソッドの後に記述することは良くありません。フォームを切り替える度に後ろのコードがキューに貯められて有効メモリを食っていってしまいます。

では、そのコードをご紹介します。まずは「代替案」ですが。
では、各プロシージャがその場その場で終了するように記述するにはどうしたら良いでしょうか。
ということで、「代替案」をご紹介します。
この方法は、フォーム2種類を交互に表示させますが、実際には「フォーム1」にはHideメソッドを発行させない方法です。
「代替案」の実行
(画像をクリックすると、このサンプルがダウンロードできます)

この方法では、「フォーム2の表示」ボタンで「フォーム2」を表示させた後、「フォーム2」から「フォーム1に戻る」をクリックすると、即座に
フォーム2が閉じられました。
が表示されるので、Showメソッド以降のコードが実行されているのが確認できます。
ですが、「フォーム2」が表示されている間は、「フォーム1」は見えなくなります。

「フォーム1」の「フォーム2の表示」ボタンのクリックイベントの記述です。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル②                        UserForm1(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit
'===================================================================================================
' 位置、大きさを保持(退避)する変数
Private g_lngHeightSV As Long                                   ' フォームの高さ
Private g_lngWidthSV As Long                                    ' フォームの幅
Private g_lngTopSV As Long                                      ' フォームの縦位置
Private g_lngLeftSV As Long                                     ' フォームの横位置

'***************************************************************************************************
'   ■■■ コントロールイベント ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    ' 現在状態を退避
    g_lngHeightSV = Me.Height
    g_lngWidthSV = Me.Width
    g_lngTopSV = Me.Top
    g_lngLeftSV = Me.Left
    ' フォーム1をフォーム2の後ろに隠すため同じ大きさにする
    Me.Height = UserForm2.Height
    Me.Width = UserForm2.Width
    ' フォーム2を表示
    UserForm2.Show
    ' フォーム1を元の位置、大きさを再現
    Me.Height = g_lngHeightSV
    Me.Width = g_lngWidthSV
    Me.Top = g_lngTopSV
    Me.Left = g_lngLeftSV
    ' フォーム2のHide後のメッセージ
    MsgBox "フォーム2が閉じられました。"
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
ここでは、「Me.Hide」を記述しません。その代わり「フォーム1」を位置・大きさをモジュール変数に記録し、大きさを「フォーム2」に合わせてしまいます。
「フォーム2」が消えた後は、「フォーム1」を記録させていた位置・大きさに戻します。
つまり、「フォーム1」は「フォーム2」の後ろに隠れる形になります。

では、「フォーム2」の記述です。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル②                        UserForm2(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント ■■■
'***************************************************************************************************
'* 処理名 :UserForm_Activate
'* 機能  :フォーム初期表示イベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Activate()
    '-----------------------------------------------------------------------------------------------
    ' フォームの移動イベントのプロシージャを呼ぶ
    Call UserForm_Layout
End Sub

'***************************************************************************************************
'* 処理名 :UserForm_Layout
'* 機能  :フォームの移動イベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_Layout()
    '-----------------------------------------------------------------------------------------------
    On Error GoTo Layout_End
    ' UserForm1をUserForm2の後ろへ移動
    With UserForm1
        .Top = Me.Top
        .Left = Me.Left
    End With
'===================================================================================================
' 終了(エラー処理含む)
Layout_End:
    On Error GoTo 0
End Sub

'***************************************************************************************************
'   ■■■ コントロールイベント ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    ' フォーム2を閉じる
    Me.Hide
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
「フォーム1に戻る」ボタンのイベントは、「フォーム2」を閉じるだけです。これは「フォーム1」は消えていないからです。
ActivateイベントとLayoutイベントでは、「フォーム1」の位置を「フォーム2」に合わせています。
これにより、「フォーム2」をドラッグしても「フォーム1」は「フォーム2」の後ろに隠れたままになります。

無理にユーザーフォームを交互に表示させることは、制御が難しくなるばかりでなく、メモリに負担を与えてしまいます。このような方法を考えている場合にはこの「代替案」はいかがでしょう。

普通(本当に)に2つのフォームを交互に表示させる場合はこのようにします。
いままでのサンプルは「Workbook_Open」で自動実行でしたが、今度はボタンでの起動にしています。
2つのフォームの排他表示サンプル
(画像をクリックすると、このサンプルがダウンロードできます)
というのは、Publicな変数「終了フラグ(swEnd)」を作る関係で標準モジュールが必要だったからです。
実際は、この変数以外は「Workbook_Open」でも構わないのですが、説明上でも起動処理がまとまっている方が見やすいのでこうしています。
では、その「起動処理」です。この方法は単に一方のフォームを表示するのではありません。2つのフォームを排他的に表示させる「コントローラ」のプロシージャを用意して、これを起動させます。ここでは「TEST」としていますが、この中身を「Workbook_Open」に書いても構わないし、「Workbook_Open」から「TEST」を呼び出しても構いません。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル③                        Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit
'===================================================================================================
Public swEnd As Boolean                                         ' 終了フラグ

'***************************************************************************************************
'   ■■■ ワークシート側からの呼び出し処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST
'* 機能  :ユーザーフォームの表示コントロール
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub TEST()
    '-----------------------------------------------------------------------------------------------
    Dim intIx As Integer                                            ' フォーム種別判定(1 or 2)
    ' 初回はUserForm1を表示
    intIx = 1
    ' 終了フラグがTrueになるまで繰り返す
    Do Until swEnd
        ' フォーム種別判定
        If intIx <> 2 Then
            ' UserForm1の表示
            UserForm1.Show
            ' 次回はUserForm2が表示されるようにintIxをセット
            intIx = 2
        Else
            ' UserForm2の表示
            UserForm2.Show
            ' 次回はUserForm1が表示されるようにintIxをセット
            intIx = 1
        End If
    Loop
    ' 終了メッセージ
    MsgBox "終了です。"
    swEnd = False
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
このように、「UserForm1」「UserForm2」を交互に表示させるループ処理を作成します。 ただ、単にループ処理を作成すると終了させる方法がなくなってしまうので、Publicな変数「終了フラグ(swEnd)」を使って終了させるようにしています。

フォームの方は、2つとも同じ記述です。

'***************************************************************************************************
'   2つのフォームを排他表示させるサンプル③                        UserForm1(Class)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'04/02/13(1.00)新規作成
'05/12/04(1.01)初回修正
'20/02/25(1.10)*.xlsm化、他
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ フォームイベント ■■■
'***************************************************************************************************
'* 処理名 :UserForm_QueryClose
'* 機能  :フォーム閉鎖動作
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(既定)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    '-----------------------------------------------------------------------------------------------
    ' ユーザー操作等での終了か
    If CloseMode <> vbFormCode Then
        swEnd = True
    End If
End Sub

'***************************************************************************************************
'   ■■■ コントロールイベント ■■■
'***************************************************************************************************
'* 処理名 :CommandButton1_Click
'* 機能  :「もうひとつのフォームに移る」ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton1_Click()
    '-----------------------------------------------------------------------------------------------
    Me.Hide
End Sub

'***************************************************************************************************
'* 処理名 :CommandButton2_Click
'* 機能  :「終了する」ボタンクリックイベント
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2004年02月13日
'* 作成者 :井上 治
'* 更新日 :2020年02月25日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Private Sub CommandButton2_Click()
    '-----------------------------------------------------------------------------------------------
    swEnd = True
    Me.Hide
End Sub

'----------------------------------------<< End of Source >>----------------------------------------
単にHide(又はUnload)させる場合は、標準モジュールに戻ってループが継続されるので、もう一つのフォームが表示されます。 「終了する」の方のボタンは、Publicな変数「終了フラグ(swEnd)」に「True」をセットしているので、 標準モジュールに戻った後、ループのUntil条件が成立してループを抜けるという、単純なことです。
モーダルなユーザーフォームでは、「UserForm1.Show」と記述した場合、そのフォームの表示中は以降のコードは実行されないということに留意していただくと理解ができると思います。

では、起動させて、「もうひとつのフォームに移る」ボタンを繰り返しクリックしてから「終了する」をクリックしてみて下さい。最初のサンプルのようなことはないと思います。
Visual Basic であれば....   Excelの方が「手軽」なのかも知れませんが、ワークシートを全く利用しない仕組みまで何でもExcelでやろうとする方がいらっしゃいます。
せっかくVBAをある程度利用できるようになったのだから、積極的に利用する、という考え方なのだと思いますが、 このページのようなケースに当たって不要な「苦労」をされる場合もあります。
本来の開発言語であるVisualBasic(.NET)での「Windowsフォーム」であれば、このページのように2つのフォームを交互に表示させる場合で しかも最初に出現するスクリーン上の位置やサイズを記憶させて次回はその状態を再現することも簡単です。 このページでは結局、標準モジュール上でループを形成してしまいましたが、VisualBasic(.NET)ではこのループ処理をフォーム上で実現できます。
アプリケーションの起動時のフォームを「ベースフォーム」とするなら、これを「Opacityプロパティ」を使って透明にしてしまい、その上で必要なフォームを条件によって表示させるように制御します。
「ベースフォーム」はアプリケーションの終了まで常駐するので、そこで自在に制御できるわけです。