#13 コントロールの配置をVBAで操作する

通常のデータベースアプリケーションでは実行中にフォームやレポート上のコントロールの位置を変更するということはあまりないかもしれませんが、そのデザイン作業ではAccessのメニューやツールバーから実行できる「左揃え」や「上揃え」、「グリッドに配置」などの機能を頻繁に利用するのではないでしょうか?。ここではそれらの機能を拡張したT'sKitの「コントロール等間拡大/縮小」ツールや「コントロール直線配置」ツールなどで行っている、VBAによるコントロールの配置操作について説明したいと思います。
 
こではサンプルとして右のようなフォームを用意してみました。このフォーム上に配置されたすべてのコントロールに対して、「横一列に直線配置」・「縦一列に直線配置」・「縦方向の間隔を削除して詰める」操作を行ってみます。直線配置についてはAccessの「左揃え」や「上揃え」でも一列に並べることはできますが、このサンプルフォームのようにコントロールの間隔が不揃いである場合、その間隔の調整までは行ってくれません。続けてメニューなどから「上下の間隔」の操作を実行する必要があります。そこでここでは「左揃え」や「上揃え」はもちろんのこと、コントロールの間隔が一定(等間隔)になるような操作も行うようにします。
元のサンプルフォーム
初に今回のサンプルコードの共通的な変数などを宣言します。ここでは対象とするフォーム名を「frmTest」としましたが、アドインとしてこのような機能を組み込む場合には、
    Application.CurrentObjectName
の値から現在アクティブになっているオブジェクト名を取得しそれを代入する、という方法をとるのがよいでしょう。

ここで、sngTmpTop と sngTmpLeft は各コントロールの再配置後の[上位置]プロパティと[左位置]プロパティの値を保持する作業用変数です。また、sngCntrlGap はコントロールの間隔を表す変数です。T'sKitの各ツールではこの値を画面上のテキストボックスで指定するようになっていますが、ここでは一定の値を代入することとします。

なお、[上位置]や[左位置]プロパティをVBAから設定する場合にはすべて『Twip値』で指定しなければなりません。Accessのプロパティシートのように『cm』で管理したい場合には "1cm=567Twip" として換算します。ここではそれをあらかじめ定数"TWIP_CM"として宣言しています。

  Dim frm As Form
  Dim ctl As Control
  Dim sngTmpTop As Single
  Dim sngTmpLeft As Single
  Dim sngCntrlGap As Single
  Dim iintLoop
  Const TWIP_CM = 567    '1cm当たりのTwip値

  '対象フォームをフォームオブジェクト変数にセット
  Set frm = Forms!frmTest
れでは最初にコントロールを横一列に直線配置してみます。一番左のテキストボックスの上位置と左位置をともに0.5cmに、またコントロールの間隔を0.2cmにします。

  sngTmpTop = 0.5 * TWIP_CM
  sngTmpLeft = 0.5 * TWIP_CM 
  sngCntrlGap = 0.2 * TWIP_CM 

  frm.Painting = False

  For iintLoop = 0 To frm.Controls.Count - 1
    Set ctl = frm.Controls(iintLoop)
    With ctl
      .Top = sngTmpTop
      .Left = sngTmpLeft
      sngTmpLeft = sngTmpLeft + .Width + sngCntrlGap
    End With
  Next iintLoop

  frm.Painting = True

このコードでは、テキストボックスのプロパティを変更する前にフォームの[Painting]プロパティを"False"に設定し、変更後に"True"に戻しています。これはテキストボックスのプロパティ値を1つ変えるたびにその値に基づいてテキストボックスが再描画される、つまりテキストボックスが左・上と順番に移動していく動作が見えてしまうのを防ぐためのものです。

いったん[Painting]プロパティを"False"に設定することによって、個々のプロパティを変更してもその時点では見た目にはテキストボックスの位置は変わらないようになります。そして最後に"True"にした時点で、すべてのテキストボックスが一斉に新しい位置に移動したように見せることができます。

For〜Nextのループはフォーム上のすべてのコントロールを列挙するループです。このループ内ではカウンタ用の変数iintLoopを使って frm.Controls(iintLoop) のような書式でiintLoop番目のコントロールを参照することができます。

個々のコントロールに対しては、現在の変数値を[Top/上位置]プロパティと[Left/左位置]プロパティそれぞれにセットして新しい位置を設定します。そして左位置を表す変数値(sngTmpLeft)にそのコントロールの幅(.Width)とコントロール間隔の指定値を加算することによって、次のコントロールの左位置を算出します。

横一列に再配置したサンプルフォーム
いてコントロールを縦一列に直線配置してみます。横一列の場合と異なるのは、1つのテキストボックスの位置を変更した後、左位置ではなく上位置を表す変数値(sngTmpTop)にそのコントロールの高さ(.Height)とコントロール間隔の指定値を加算する点だけです。

  sngTmpTop = 0.5 * TWIP_CM
  sngTmpLeft = 0.5 * TWIP_CM 
  sngCntrlGap = 0.2 * TWIP_CM 

  frm.Painting = False

  For iintLoop = 0 To frm.Controls.Count - 1
    Set ctl = frm.Controls(iintLoop)
    With ctl
      .Top = sngTmpTop
      .Left = sngTmpLeft
      sngTmpTop = sngTmpTop + .Height + sngCntrlGap
    End With
  Next iintLoop

  frm.Painting = True

縦一列に再配置したサンプルフォーム
後に、縦方向の間隔を削除してテキストボックスを詰めて再配置してみます。プロパティ変更を行う部分のコードは縦一列に直線配置する場合とまったく同じです。コントロールの間隔を削除するということは間隔をゼロにするということですので、コントロールの間隔を表す変数sngCntrlGap = 0 とするだけです。

  sngTmpTop = 0.5 * TWIP_CM
  sngTmpLeft = 0.5 * TWIP_CM 
  sngCntrlGap = 0
  
  frm.Painting = False
  
  For iintLoop = 0 To frm.Controls.Count - 1
    Set ctl = frm.Controls(iintLoop)
    With ctl
      .Top = sngTmpTop
      .Left = sngTmpLeft
      sngTmpTop = sngTmpTop + .Height + sngCntrlGap
    End With
  Next iintLoop

  frm.Painting = True

間隔を削除したサンプルフォーム
べてのコントロールを列挙するループ、For iintLoop = 0 To frm.Controls.Count - 1 の部分では、実際には画面上の配置順ではなく作成された順番でコントロールが取り出されます。したがって、一般的には上からあるいは左から順番にコントロールが作成されるとは限りませんので、T'sKitではコントロール情報をいったんテーブルに書き出してから再配置操作を行っています。まずこのループを使ってとにかく対象となるすべてのコントロールの[上位置]、[左位置]、[幅]、[高さ]などの関連するプロパティ値を取得してテーブルに書き出します。
そして、それをクエリーによって上位置の昇順あるいは左位置の昇順で取り出すようにします。後はそれらのレコードを順番にループで読み込んで、上あるいは左から順番に一定の間隔を空けて再配置していきます。

また、「#12 フォーム上の特定の種類のコントロールを選択する」で説明した[InSelection]プロパティを利用することで、T'sKitの各ツールで行っているような、フォームのデザインで現在選択されているコントロールだけを対象に配置の変更を行うこともできます。
| Index | Prev | Next |

 

Copyright © T'sWare All rights reserved