Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows XP全般 : Excel 2003)
フレームの階層(包含関係)が知りたい
投稿日時: 19/03/14 20:15:41
投稿者: 富山の初心者

お願いします。
 
フォーム内に各種コントロールを配置し、
そのなかには、フレームも配置、かつ、
そのフレーム内に、フレームを配置した場合、
 
下記のコディングで各コントロールの属性がわかります。
Top/Left/Tabindexもわかります。
しかし、フレーム内にあるコントロールだと、
フレーム内のTop/Leftはフレームの左上端からの値ですし、
TabIndexもフレーム内の連番です。
そこで、質問なのですが、
フレームの階層(包含関係)を知る手順をしりたいのですが、
どのような、手順/方法で得ることができますか?
 
Option Explicit
Dim frmFFF As UserForm
Dim ctlCCC As Control
Sub AAA()
    Set frmFFF = ActiveWorkbook.VBProject.VBComponents.Item("UserForm1").Designer
    For Each ctlCCC In frmFFF.Controls
        Debug.Print "TypeName:"; TypeName(ctlCCC)
        Debug.Print "Name :"; ctlCCC.Name
        Debug.Print "Top :"; ctlCCC.Top
        Debug.Print "Left :"; ctlCCC.Left
        Debug.Print "Width :"; ctlCCC.Width
        Debug.Print "Heigh :"; ctlCCC.Height
        Debug.Print "TabIndex:"; ctlCCC.TabIndex
    Next
End Sub

回答
投稿日時: 19/03/14 20:36:02
投稿者: WinArrow
投稿者のウェブサイトに移動

ヒント
 
全てのコントロールと、その親コントロールの親子関係をシートに作成します。
親がユーザーフォームになると、親子関係検索は終了です。
 

子    親
TextBox1 Frame3
Frame3 Useform1
TextBox2 Frame4
Frame4 Frame3
Frame3 Userform1
 
のようになると仮定すると
 
ユーザーフォーム内での「TextBox1」のTopは、Frame3.Top + TextOx1.Top になります。
 
更に、Userform1.Topを加算すると、スクリーン内の絶対位置が求められます。
 
この親子関係をたどって、位置情報が求められます。
 

回答
投稿日時: 19/03/14 21:31:17
投稿者: simple

引用:
フレームの階層(包含関係)を知る手順をしりたいのですが、
どのような、手順/方法で得ることができますか?

Frame1の子にFrame2,Frame3があるとすると
Frame1.ControlsコレクションにFrame2,Frame3が入るようですね。
再帰的に下降していけばたどれそうです。

回答
投稿日時: 19/03/14 21:34:12
投稿者: WinArrow
投稿者のウェブサイトに移動

面白そうなのでコードを作成してみました。
 
Option Explicit
 
Dim TopLeft(1)
 
 
Private Sub UserForm_Click()
Dim CTRL As MSForms.Control
    Cells.ClearContents
    Range("A1").Value = "CtrlName"
    Range("B1").Value = "Ctrl.Top"
    Range("C1").Value = "Ctrl.Left"
    Range("D1").Value = "All.Top"
    Range("E1").Value = "All.Left"
    For Each CTRL In Me.Controls
        TopLeft(0) = CTRL.Top
        TopLeft(1) = CTRL.Left
        Call getTopLeft(koCTRL:=CTRL)
        With Range("A" & Rows.Count).End(xlUp).Offset(1)
            .Value = CTRL.Name
            .Offset(, 1).Value = CTRL.Top
            .Offset(, 2).Value = CTRL.Left
            .Offset(, 3).Value = TopLeft(0)
            .Offset(, 4).Value = TopLeft(1)
     
        End With
    Next
End Sub
 
 
Sub getTopLeft(ByVal koCTRL As MSForms.Control)
    TopLeft(0) = TopLeft(0) + koCTRL.Parent.Top
    TopLeft(1) = TopLeft(1) + koCTRL.Parent.Left
    If LCase(koCTRL.Parent.Name) = "userform1" Then Exit Sub
    Call getTopLeft(koCTRL:=koCTRL.Parent)
End Sub
 

投稿日時: 19/03/14 21:44:33
投稿者: 富山の初心者

WinArrow 様 夜分にも関わらず回答ありがとうございます。
 
 

引用:
全てのコントロールと、その親コントロールの親子関係をシートに作成します。
親がユーザーフォームになると、親子関係検索は終了です。
  

 子    親
TextBox1 Frame3
 Frame3 Useform1
 TextBox2 Frame4
 Frame4 Frame3
 Frame3 Userform1
のようになると仮定すると

この部分ですが、
親子の関係を知る方法を知りたいのです。
どのようにして、TextBox1の親が Frame3なのかを知りたいのです。
 
 
以下の部分は理解できました。
引用:

ユーザーフォーム内での「TextBox1」のTopは、Frame3.Top + TextOx1.Top になります。
  
更に、Userform1.Topを加算すると、スクリーン内の絶対位置が求められます。

投稿日時: 19/03/14 21:50:20
投稿者: 富山の初心者

WinArrow 様
simple 様 ありがとうございます。
 
再質問のコメントを書いている途中に回答が付いたみたいです。
 
WinArrow 様
simple 様 の回答を確認してみます。
 
解決は確認してからとしますにで、しばらく未解決とさせてください。

回答
投稿日時: 19/03/14 23:20:18
投稿者: WinArrow
投稿者のウェブサイトに移動

掲示のコードは、
 
ユーザーフォーム内のすべてのコントロールを対象に
当該コントロールの「Top/Left」 に、親コントロールの「Top/Left」を加算(親がユーザーフォームになるまで再起処理)し、シートに書き出す処理です。
 
A列左から
当該コントロール名
当該温トロールのTOP
当該温トロールのLEFT
ユーザーフォームまでのTOP
ユーザーフォームまでのLEFT
 
ループの最後に
ユーザーフォームのTOPとLEFTを出力する処理を
追加すれば、検証しやすいと思います。
 
※親コントロールの名前は
 階層がいくつにあるか想定出来合いため、取得していません。
 
なお、マルチページコントロールの場合は、
CTRL.Parentが「Page1」のようになって今うので(Page1」というコントロールは存在しない)
若干、手直しが必要と思います。
 

回答
投稿日時: 19/03/15 00:31:03
投稿者: simple

┌─frame1────────────┐
│                │
│ ┌─frame2────────┐ │
│ │            │ │
│ │  ●textbox1     │ │
│ │            │ │
│ └────────────┘ │
│ ┌─frame3────────┐ │
│ │            │ │
│ │ ┌─frame4───┐  │ │
│ │ │       │  │ │
│ │ │ ●textbox2 │  │ │
│ │ │ ●textbox4 │  │ │
│ │ │       │  │ │
│ │ └───────┘  │ │
│ │            │ │
│ └────────────┘ │
│  ●textbox5          │
└────────────────┘
 ●textbox3

  
仮に、以上のような構造のとき、
例えば、
top=>Frame1=>Frame2=>TextBox1
top=>Frame1=>Frame3=>Frame4=>TextBox2
top=>Frame1=>Frame3=>Frame4=>TextBox4
top=>Frame1=>TextBox5
top=>TextBox3

のようなものを出力するのであれば、可能ですね。
 
どんな出力を考えているのでしょうか?

回答
投稿日時: 19/03/15 11:59:51
投稿者: WinArrow
投稿者のウェブサイトに移動

マルチページ
加算式
コントロールの階層
 
表示したコードに変更しました。
 
Option Explicit
   
Dim TopLeft(2)
   
   
Private Sub UserForm_Click()
Dim CTRL As MSForms.Control
    Cells.ClearContents
    Range("A1").Value = "CtrlName"
    Range("B1").Value = "Ctrl.Top"
    Range("C1").Value = "Ctrl.Left"
    Range("D1").Value = "All.Top"
    Range("E1").Value = "All.Left"
    Range("F1").Value = "TOP加算式"
    Range("G1").Value = "LEFT加算式"
    Range("H1").Value = "コントロール階層"
    For Each CTRL In Me.Controls
        TopLeft(0) = CTRL.Top
        TopLeft(1) = CTRL.Left
        TopLeft(2) = CTRL.Name
        Call getTopLeft(koCTRL:=CTRL)
        With Range("A" & Rows.Count).End(xlUp).Offset(1)
            .Value = CTRL.Name
            .Offset(, 1).Value = CTRL.Top
            .Offset(, 2).Value = CTRL.Left
            .Offset(, 3).Value = Application.Evaluate(TopLeft(0))
            .Offset(, 4).Value = Application.Evaluate(TopLeft(1))
            .Offset(, 5).Value = TopLeft(0)
            .Offset(, 6).Value = TopLeft(1)
            .Offset(, 7).Value = TopLeft(2)
        End With
    Next
    With Range("A" & Rows.Count).End(xlUp).Offset(1)
        .Value = Me.Name
        .Offset(, 1).Value = Me.Top
        .Offset(, 2).Value = Me.Left
    End With
     
End Sub
   
   
Sub getTopLeft(ByVal koCTRL As MSForms.Control)
    If TypeName(koCTRL.Parent) = "Page" Then
       TopLeft(0) = TopLeft(0) & "+" & koCTRL.Parent.Parent.Top
       TopLeft(1) = TopLeft(1) & "+" & koCTRL.Parent.Parent.Left
       TopLeft(2) = TopLeft(2) & ":" & koCTRL.Parent.Parent.Name
    Else
        TopLeft(0) = TopLeft(0) & "+" & koCTRL.Parent.Top
        TopLeft(1) = TopLeft(1) & "+" & koCTRL.Parent.Left
       TopLeft(2) = TopLeft(2) & ":" & koCTRL.Parent.Name
    End If
    If LCase(koCTRL.Parent.Name) = "userform1" Then Exit Sub
    If TypeName(koCTRL.Parent) = "Page" Then
        Call getTopLeft(koCTRL:=koCTRL.Parent.Parent)
    Else
        Call getTopLeft(koCTRL:=koCTRL.Parent)
    End If
End Sub

投稿日時: 19/03/16 21:42:32
投稿者: 富山の初心者

WinArrow 様
simple 様 ありがとうございます。
  
WinArrow 様 のコーディングを参考に
下記コーディングを作成しました。
 
再帰的なコーディングは初めてだったので、随分戸惑いました。
また、「マルチページ」には、不慣れかつ不要でしたので、削除しました。
また、Top/Leftも今回は省きました。
その結果、要望していた結果になりました。
 

Option Explicit
'-----------------------------------------------------------
Dim lngLEVEL     As Long
Dim strKaisou    As String
Dim CTRL         As MSForms.Control
'-----------------------------------------------------------
Sub UserForm1_Click()
    Cells.ClearContents
    Range("A1").Value = "CtrlName"
    Range("B1").Value = "レベル"
    Range("C1").Value = "コントロール階層"
    For Each CTRL In UserForm1.Controls
        lngLEVEL = 1
        strKaisou = CTRL.Name
        Call getstrKaisou(koCTRL:=CTRL)
        With Range("A" & Rows.Count).End(xlUp).Offset(1)
            .Value = CTRL.Name
            .Offset(, 1).Value = lngLEVEL
            .Offset(, 2).Value = strKaisou
        End With
    Next
End Sub
'-----------------------------------------------------------
Sub getstrKaisou(ByVal koCTRL As MSForms.Control)
    strKaisou = koCTRL.Parent.Name & ":" & strKaisou
    If LCase(koCTRL.Parent.Name) = "userform1" Then Exit Sub
    lngLEVEL = lngLEVEL + 1
    Call getstrKaisou(koCTRL:=koCTRL.Parent)
End Sub
'-----------------------------------------------------------

以下、実行結果
 
CtrlName	レベル	コントロール階層
L1	1	L1
L11	2	L1:L11
T0	1	T0
T11	2	L1:T11
T12	2	L1:T12
L12	2	L1:L12
T111	3	L1:L11:T111
T112	3	L1:L11:T112
T122	3	L1:L12:T122
T121	3	L1:L12:T121

ありがとうございました。
以上で解決とさせていただきます。