本ページには広告が含まれています。

VBA Parentプロパティでコントロールをコンテナ単位で取得

VBAでParentプロパティでコントロールをコンテナ単位取得する

VBAでUserFormの全プロパティ値を取得するについての内容を少しづつバージョンアップしています。前回はVBAでコンテナ単位にまとめる動作を検討しました。今回は、いよいよコントロールのプロパティー値をコンテナ単位で取得していけるようにコーディングを見直していきます。

くるみこ
くるみこ

ザックリ言うと .Parent プロパティを使って親オブジェクトを確認し動作を分岐するような感じになります。コントロールのプロパティー値取得部分は別プロシージャにしていきたいと思ってます。それではさっそくやっていきましょう。

階層が深くなりそうなのをクリアしていく感じですか?
よろしくお願いしますm(__)m

以前の記事「VBA で Userform の全プロパティ値を取得する」のコードを修正しています。

【この記事でわかることは】
Parent プロパティを使って UserForm 内の全コントロールのプロパティ値をコンテナ単位にまとめて取得する方法

スポンサーリンク

Parent プロパティでコンテナ単位にまとめる

取得したプロパティ値を書き込むシートがこちらです(前回記事で設定)

変更後のプロパティ値保存シート

3行目の「Parent:」と2行目の「オブジェクト名:」を見れば、E列のオブジェクト名「Page1」は「MultiPage1」のコンテナ内にあることがわかります。

この関係をコーディングに反映していきます。

Parent プロパティを組み入れたコード

さっそくメインのコード下に表示します。かなり長くて深くなっちゃいました。
最深部は、UserForm > MultiPage > Page > Frame > Control

'UserFormと配置コントロールの全プロパティ値を取得する
Sub getCtrlProperty()
    Dim i As Long, j As Long, k As Long
    Dim n As Long: n = 0    'UserForm数カウント用
    Dim ufrm As Object      'UserFormオブジェクト用
    Dim Ctrl As Control, InCtrl As Control, InC As Control
    Dim fname As String     'UserFormオブジェクト名
    Dim wb As Workbook, ws As Worksheet
    Set wb = ActiveWorkbook
    Set ws = ThisWorkbook.Sheets("Ctrl_SetValue")
    Dim ctrlStr() As String 'プロパティ値保存用
    Dim c As Long           'Control用
    Dim p As Long           'Page用
    With wb.VBProject   'Typeがユーザーフォームの場合だけ処理する
        For i = 1 To .VBComponents.Count
            If .VBComponents(i).Type = 3 Then 'ユーザーフォーム
                n = n + 1   'ユーザーフォーム数をカウント
                '2つ目のUserFormの場合シートコピー
                If n > 1 Then ws.Copy after:=ws: ws.Activate
                'シートの入力データをクリアする
                ws.Columns(2).Clear
                ws.Range(Columns(4), Columns(4).End(xlToRight)).Clear
                ReDim ctrlStr(41)   '配列の要素数初期化
                fname = .VBComponents.Item(i).Name
                ctrlStr(0) = "UserForm"
                ctrlStr(1) = fname
                ctrlStr(2) = ""
                Set ufrm = UserForms.Add(fname) 'UserFormをオブジェクトに
                On Error Resume Next
                With ufrm
                    For k = 3 To 41 '↓プロパティ値取得関数使用
                        ctrlStr(k) = getPVal(ws.Cells(k + 1, 1), ufrm)
                    Next k
                End With
                For j = 0 To UBound(ctrlStr)
                    ws.Cells(j + 1, 2) = ctrlStr(j) 'セルに値を書き込む
                Next j
                On Error GoTo 0
                'ここからコントロールのプロパティ取得
                On Error Resume Next
                c = 0
                For Each Ctrl In ufrm.Controls
                    'すでに取得済みなら処理しない
                    If WorksheetFunction.CountIf(ws.Rows(2), Ctrl.Name) <> 0 Then
                        GoTo Next_Ctrl
                    End If
                    ReDim ctrlStr(125)  '配列の要素数を初期化
                    c = c + 1
                    'Typeで処理を分岐
                    Select Case TypeName(Ctrl)
                        Case "MultiPage"    'MultiPageだった場合の処理
                            With Ctrl
                                'プロパティ値取得処理へ
                                Call getP(ctrlStr(), Ctrl, ws)
                                'MultiPageだけはPage数をカウントして上書き
                                ctrlStr(125) = .Pages.Count 'Page数を書き込む
                                For j = 0 To UBound(ctrlStr)
                                    ws.Cells(j + 1, c + 3) = ctrlStr(j) 'セルに値を保存
                                Next j
                                'Page毎に取得していく
                                For p = 0 To .Pages.Count - 1
                                    'Page のプロパティ値取得へ
                                    Call getP(ctrlStr(), .Pages(p), ws)
                                    c = c + 1
                                    For j = 0 To UBound(ctrlStr)
                                        ws.Cells(j + 1, c + 3) = ctrlStr(j)
                                    Next j
                                    '対象Page内のコントロール分ループ
                                    For Each InCtrl In .Pages(p).Controls
                                        '親がフレームだった場合は処理せずパスする
                                        If InCtrl.Parent.Name Like "Frame*" Then GoTo the_next
                                        'プロパティ値取得処理へ
                                        Call getP(ctrlStr(), InCtrl, ws)
                                        c = c + 1
                                        For j = 0 To UBound(ctrlStr)
                                            ws.Cells(j + 1, c + 3) = ctrlStr(j)
                                        Next j
                                        'Page内にフレームがあった場合の処理
                                        If InCtrl.Name Like "Frame*" Then
                                            'フレーム内のコントロール分ループ
                                            For Each InC In InCtrl.Controls
                                                'プロパティ値取得処理へ
                                                Call getP(ctrlStr(), InC, ws)
                                                c = c + 1
                                                For j = 0 To UBound(ctrlStr)
                                                    ws.Cells(j + 1, c + 3) = ctrlStr(j)
                                                Next j
                                            Next
                                        End If
the_next:
                                    Next
                                Next p
                            End With
                        Case "Frame"    'Frameだった場合の処理
                            'プロパティ値取得処理へ
                            Call getP(ctrlStr(), Ctrl, ws)
                            For j = 0 To UBound(ctrlStr)
                                ws.Cells(j + 1, c + 3) = ctrlStr(j)
                            Next j
                            'フレーム内のコントロール分ループ
                            For Each InCtrl In Ctrl.Controls
                                'プロパティ値取得処理へ
                                Call getP(ctrlStr(), InCtrl, ws)
                                c = c + 1
                                For j = 0 To UBound(ctrlStr)
                                    ws.Cells(j + 1, c + 3) = ctrlStr(j)
                                Next j
                            Next
                        Case Else
                            'プロパティ値取得処理へ
                            Call getP(ctrlStr(), Ctrl, ws)
                            For j = 0 To UBound(ctrlStr)
                                ws.Cells(j + 1, c + 3) = ctrlStr(j)
                            Next j
                    End Select
Next_Ctrl:
                Next Ctrl
                On Error GoTo 0
                Set ufrm = Nothing
            End If
        Next i
        If n = 0 Then MsgBox "UserFormはありません!"
    End With
    Set ws = Nothing
    Set wb = Nothing
End Sub

コード内にコメントは入れていますが、必要な部分を解説していきます。

15~16行目で、VBProjectの全要素数を調べて Type = 3 ユーザーフォームだった場合に処理を進めます。

17~19行目で、ユーザーフォーム数をカウントして2個目以降からは保存用シート「Ctrl_SetValue」をコピーして保存しています。

20~22行目で、保存用のシート「Ctrl_SetValue」の既存データをクリアしています。

23行目で、動的配列の要素数を(41に)初期化しています。

31~33行目で、プロパティ値取得を関数 getPVal へ引数を渡して処理をさせ配列に格納しています。
※ getPVal についてはリンク先の記事で説明していますので参照願います。

35~37行目で、取得したプロパティ値をセルに書き込んでいます。

//////ここまでがユーザーフォーム、これ以降がコントロールの処理です//////

42行目、For Each Ctrl In ufrm.Controls UserForm のコントロールコレクションから一つづつコントロールをループ処理していきます。

43~46行目は、処理の重複を避ける必要があるため、既に処理済みかどうか確認し処理済だった場合はGoToステートメントで処理を飛ばすためジャンプさせています。
※この部分が無いと、処理の途中でコンテナ内のコントロールを先に処理してしまうので、UserForm 内の全コントロールを処理していく段階で重複してしまうことになります。

47行目、動的配列の要素数を(125に)初期化しています。

50行目、Typeによって処理をCaseステートメントで分岐させます。

51~93行目までが MultiPage の処理です。

54行目で、Call getP(ctrlStr(), Ctrl, ws) と別プロシージャ処理を呼び出しています。
※別プロシージャの getP については次項で説明しています。

60行目からが、Page 毎の処理です。Page数をカウントしてループ処理します。

68行目からは、Page 内のコントロールの処理です。

71行目で、Parent プロパティを使い親コントロールが Frame の場合は処理せず飛ばしています。

78~89行目では、Page 内の Frame に対しての処理を行っています。

94~108行目が、Frame の場合の処理です。

109~114行目が、その他のコントロールだった場合の処理です。

※以前の記事で解説しているところは省略している部分がありますので参照いただければ幸いです。

プロパティ値取得処理部分を別プロシージャに分けました

コントロールのプロパティ値を取得するためのプロシージャが次のコードです。

'コントロールのプロパティ値取得用プロシージャ
Sub getP(ByRef PStr() As String, Ctrl As Object, ws As Worksheet)
    Dim i As Long
    Dim c As Long
    Dim InC As Control

    ReDim PStr(125)    '要素数を初期化
    With Ctrl
        PStr(0) = TypeName(Ctrl)    'タイプ
        PStr(1) = .Name             'オブジェクト名
        PStr(2) = .Parent.Name      '親オブジェクト名
        On Error Resume Next
        For i = 3 To 124    '関数でプロパティ値取得
            PStr(i) = getPVal(ws.Cells(i + 1, 3), Ctrl)
        Next i
        On Error GoTo 0
        c = 0
        For Each InC In .Controls  'コンテナ内のコントロール分ループ
            c = c + 1
        Next
        PStr(125) = c       'コントロール数を書き込む
    End With
End Sub

受け取る引数は次の3つです
 ByRef PStr() As String ⇒ 動的配列を参照渡しで受け取っています。
 Ctrl As Object ⇒ コントロールオブジェクト
 ws As Worksheet ⇒ ワークシートオブジェクト

7行目で、配列の要素数を125に初期化しています。

8行目、Typeを取得して配列に格納しています。

9行目、オブジェクト名を取得し配列に格納しています。

10行目、.Parent プロパティで親オブジェクト名を取得し配列に格納しています。

13~15行目で、その他のプロパティー値取得処理を関数に渡しています。
※ getPVal についてはリンク先の記事で説明していますので参照願います。

18~20行目で、コンテナ内のコントロール数を取得します。
※コンテナではない場合には値は入りません。

21行目、18~20行目で取得した値を配列に格納しています。

配列を参照渡しにしているので、呼び出し元にそのまま取得できた値が帰ります。

スポンサーリンク

実行結果

実行結果をどのように示せるか考えましたが、思いつかなかったのでGIF画像貼っておきます。

コード実行動画(一部分のみ)

コンテナとなる MultiPage、Page、Frame などの処理の際に、そのコンテナ内に配置されたコントロールを処理することでコンテナ単位にまとめることができました。

コンテナ単位で処理することによって、コンテナ内のコントロールは順番より先に処理してしまうことになります。

そこで、順番がきた段階で処理済みかどうかを判別して重複して処理しないようにして回避する設定にしました。

リンク先に今回記事のサンプルファイルを登録しています!

まとめ(おわりに)

以上、UserForm の コントロールを Parent プロパティを使ってコンテナ単位でまとめながら取得するVBAコードの設定でした。

まとめと感想など

くるみこ
くるみこ

Type別にプロシージャを分ければもっとコードを短くできるけど、あえて一度に見られるようにしています(^^;
次回は、取得したデータを使って、MultiPage のある UserForm を復元できるかどうか試していきたいと思います。

うまくコンテナ単位でまとめることができましたね。
次回ちゃんと復元できるのかどうかが楽しみです(^^)

【今回わかったことは】
UserForm 内の全コントロールのプロパティ値を
Parent プロパティを使ってコンテナ単位にうまくまとめる方法がわかりました

★★★ ブログランキング参加中! クリックしてね(^^)/ ★★★

【今後の記事について】

今回の記事はいかがだったでしょうか。皆さまのお役に立てたなら幸いです(^^;
「汎用でだれでも使えて活用できるように考える」というポリシーで、記事を継続して書いていきたいと思っています。どうぞよろしくお願いしますm(_ _)m

【検討中の今後の記事内容は・・・・】
・実務に役立つものを提供できるよう常に検討しています(^^ゞ
・その他雑記的に「プチネタなど」もいろいろ考えていきたいと思っています・・・・
・今後の記事にご期待ください(^^)/

スポンサーリンク
スポンサーリンク

過去記事のサンプルファイルをダウンロードできます

リンク先に今回記事のサンプルファイルを登録しています!

過去の記事で使用したサンプルファイルをダウンロードできるようにページを設置していますので、こちら(このリンク先)からご利用ください