ユーザーフォームへのコントロール配置――ControlsコレクションのAddメソッド

ユーザーフォームへのコントロール配置――ControlsコレクションのAddメソッド

ControlsコレクションのAddメソッドによるコントロールの動的配置

f:id:akashi_keirin:20170923211800j:plain

この本の162ページによると、

ユーザーフォームにコントロールを追加するには、Controlsコレクション(集合体)のAddメソッドを使います。第1引数には、コントロールのクラス文字列を指定します。第2引数はコントロール名を指定します。

とのこと。ふむふむ、なるほど。

んで、

クラス文字列

については、同書184ページによると、

コントロール クラス文字列
コマンドボタン Forms.CommandButton.1
テキストボックス Forms.TextBox.1
ラベル Forms.Label.1
コンボボックス Forms.ComboBox.1
リストボックス Forms.ListBox.1
チェックボックス Forms.CheckBox.1
オプションボタン Forms.OptionButton.1
イメージ Forms.Image.1
トグルボタン Forms.ToggleButton.1
スクロールバー Forms.ScrollBar.1
スピンボタン Forms.SpinButton.1
タブストリップ Forms.TabStrip.1
マルチページ Forms.MultiPage.1

とのこと。(ふう。しっかしHTMLで表書くのしんどいな。)

つまり、

Dim ctrl As Control
Set ctrl = Controls.Add("Forms.CommandButton.1","BtnHogeHoge")

としてやれば、「BtnHogeHoge」という名前のCommandButtonコントロールを追加することができる、ということだ。

で、やってみた。

前回

akashi-keirin.hatenablog.com

のフォーム及びコードを流用する。

まずは、前回のリスト1の書き換えから。

リスト1 フォームモジュール
Public Sub init_ver2(ByVal numOfBtns As Integer, _
                     ByVal btnHeight As Double, _
                     ByVal btnWidth As Double, _
                     ByVal offsetBtnsRow As Double, _
                     ByVal offsetBtnsCol As Double, _
                     ByVal numOfCols As Integer, _
                     ByVal btnName As String)
  numOfBtns_ = numOfBtns
  Dim i As Integer
  Dim ctrl As Control    '……(1)'
  For i = 1 To numOfBtns_
    Set ctrl = Controls.Add("Forms.CommandButton.1", _
                            "Btn" & Format(i, "0#"))    '……(2)'
    With ctrl
      .height = btnHeight
      .width = btnWidth
      .Top = offsetBtnsRow + ((offsetBtnsRow + btnHeight) * ((i - 1) \ numOfCols))
      .Left = offsetBtnsCol + ((offsetBtnsCol + btnWidth) * ((i - 1) Mod numOfCols))
      .Caption = btnName & StrConv(i, vbWide) & "号"
    End With
  Next
  With Me
    .height = offsetBtnsRow _
              + ((btnHeight + offsetBtnsRow) * (((numOfBtns_ - 1) \ numOfCols) + 1)) _
              + TOP_BOTTOM_MARGIN
    .width = offsetBtnsCol + ((btnWidth + offsetBtnsCol) * numOfCols) + offsetBtnsCol
  End With
End Sub

変えたのは(1)からの12行(実質11行)

Dim ctrl As Control
For i = 1 To numOfBtns_
  Set ctrl = Controls.Add("Forms.CommandButton.1", _
                          "Btn" & Format(i, "0#"))    '……(2)'
  With ctrl
    .height = btnHeight
    .width = btnWidth
    .Top = offsetBtnsRow + ((offsetBtnsRow + btnHeight) * ((i - 1) \ numOfCols))
    .Left = offsetBtnsCol + ((offsetBtnsCol + btnWidth) * ((i - 1) Mod numOfCols))
    .Caption = btnName & StrConv(i, vbWide) & "号"
  End With
Next

ctrl型の変数に、ControlsコレクションのAddメソッドを用いて、新しいボタンをセット。With以下のところは前回同様。Forループでボタンの数だけ繰り返している。

(2)の

Set ctrl = Controls.Add("Forms.CommandButton.1", _
                        "Btn" & Format(i, "0#"))

が今回の主役。

Addメソッドの第1引数に、コマンドボタンのクラス変数「Forms.CommandButton.1」を指定。最後の「1」まで必要なので注意。最初、オブジェクトの通し番号だと思って

Controls.Add("Forms.CommandButton." & i,"Btn" & Format(i, "0#"))

と書いて、i = 2 になったところでエラーが出たw

第2引数では、変数 i を用いて、名前が「Btn01」、「Btn02」、……となるようにした。

実行

ついでに実行方法も変更した。

f:id:akashi_keirin:20170923211847j:plain

ワークシート上に、こんなふうにボタンと表を作って、次のコードで実行するようにした。

スト2 標準モジュール
Public Sub controlTest2()
  Dim testFrm As TestForm
  Set testFrm = New TestForm
  Dim num As Integer
  Dim str As String
  Dim height As Double
  Dim width As Double
  Dim frmName As String
  Dim numOfButtons As Integer
  With ActiveSheet    '……(1)'
    num = .Range("C2").Value
    str = .Range("C3").Value
    height = .Range("C4").Value
    width = .Range("C5").Value
    frmName = .Range("C6").Value
    numOfButtons = .Range("C7").Value
  End With
  If num = 0 Or num > 10 Then Exit Sub    '……(2)'
  If height < 10 Or height > 400 Then Exit Sub
  If width < 10 Or width > 400 Then Exit Sub
  If numOfButtons < 1 Or numOfButtons > 100 Then Exit Sub
  With testFrm
    .init_ver2 numOfBtns:=numOfButtons, _
               btnHeight:=height, _
               btnWidth:=width, _
               offsetBtnsRow:=10, _
               offsetBtnsCol:=5, _
               numOfCols:=num, _
               btnName:=str
    .Caption = frmName
    .Show
  End With
End Sub

(1)からの8行

With ActiveSheet
  num = .Range("C2").Value
  str = .Range("C3").Value
  height = .Range("C4").Value
  width = .Range("C5").Value
  frmName = .Range("C6").Value
  numOfButtons = .Range("C7").Value
End With

で、init_ver2メソッドに渡す引数のうち5つと、フォームのCaptionプロパティに渡す文字列をシートから取得する。

変数の名前を見てもらったら、どれが何に当たるかは分かると思う。

(2)からの4行

If num = 0 Or num > 10 Then Exit Sub
If height < 10 Or height > 400 Then Exit Sub
If width < 10 Or width > 400 Then Exit Sub
If numOfButtons < 1 Or numOfButtons > 100 Then Exit Sub

はガード節。不適切な値が設定されていたら処理をやめるようにしている。

ただし、条件設定はかなりテキトー。

実行結果

f:id:akashi_keirin:20170923211813j:plain

なんと、最初に設置した10個のコマンドボタンが設置されたまま、今回、Addメソッドで追加したコマンドボタンが上から配置されている……。

試しに、リスト1の最後に

For Each ctrl In Controls
  Debug.Print ctrl.name
Next

を追加して実行してみると、イミディエイト・ウインドウは、

f:id:akashi_keirin:20170923211909j:plain

こうなる。

どうやら、「Btn01」とか「Btn02」というのは「オブジェクト名」のことではないらしい。ま、当たり前か。

f:id:akashi_keirin:20170923211923j:plain

こんなふうに、もともと置いてあったコマンドボタンを全部削除してから実行すると、

f:id:akashi_keirin:20170923211933j:plain

無事、思い通りの結果が得られた。

おわりに

ついついムキになってユーザーフォーム・コントロール関係にハマってしまったが、実用的なものにするには、動的に配置したコントロールのイベントをどうするか、ということだと思う。ただ、ちょっとまだ今の私の力では手に負えない気もする。

まだまだ勉強が足りないね。

@akashi_keirin on Twitter