Excel VBA UserFormのコマンドボタンでBack(戻る)機能を自作

ExcelVBA-実用編

今回は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はわかりませんが、ロジックを考えて組み合わせる事で、大抵のことはできると思います。

タイトルとURLをコピーしました