レイアウト崩れを根絶せよ:Word VBAにおける「動的テーブル制御」の設計思想
業務自動化の現場において、もっとも「現場の信頼」を失墜させるミスは何だと思うか?
それは、計算が間違っていることでも、処理が遅いことでもない。
「出力された帳票のレイアウトが、データ量によってグチャグチャになること」だ。
Excelであれば、セルはデータの器として機能し、表示範囲を広げれば済む話だ。しかし、Wordは「文書」である。文書には「美しさ」と「整合性」が求められる。セル内に溢れ出したテキストが隣の行を侵食したり、表がページを突き抜けたりするマクロは、プロフェッショナルの仕事とは呼べない。
今回は、初心者から脱却し、実務で通用する「堅牢な表生成ロジック」を構築するための、AutoFit(自動調整)の極意を伝授する。
1. なぜ、あなたのマクロはレイアウトを破壊するのか?
多くの初心者が陥るアンチパターンがある。それは、「テキストを流し込んだ後に、場当たり的にAutoFitを呼び出す」という手法だ。
' --- アンチパターン:素人の書き方 ---
For i = 1 To rowCount
targetTable.Cell(i, 1).Range.Text = dataArray(i)
targetTable.Rows(i).AutoFit ' ← 毎回これを呼ぶのは非効率かつ危険
Next i
なぜこれがダメなのか。理由は3つある。
1. 計算コストの増大: `AutoFit` はWordのレイアウトエンジンを再計算させる重い処理だ。ループ内で毎回呼び出すのは、エンジンに「一歩歩くたびに地図を書き直せ」と命令するようなもので、パフォーマンスを著しく低下させる。
2. 固定値との衝突: 行の高さ(`Height`)に数値が設定されている場合、AutoFitは無力化される。
3. 意図しない幅の変化: `AutoFit` の設定次第では、テキスト量に応じて「列の幅」まで勝手に変わってしまう。ユーザーが求めているのは「行の高さの調整」であって、「列幅の変動」ではないことが多い。
2. 堅牢な設計:プロフェッショナルが守る「3つの鉄則」
プロのアーキテクトは、データを流し込む「前」と「後」で、オブジェクトの状態を厳格に管理する。
鉄則①:RowHeightRule を「Auto」に固定せよ
Wordの行の高さには「ルール」がある。`wdRowHeightExactly`(固定値)が設定されていると、どれだけテキストが増えても行は広がりません。動的な調整を前提とするなら、必ず `wdRowHeightAuto` を指定しておく必要がある。
鉄則②:AutoFitBehavior を戦略的に使い分けよ
- `wdAutoFitContent`: セル内のコンテンツに合わせて、列の幅を調整する。
- `wdAutoFitWindow`: 表全体の幅を、ページ(ウィンドウ)の幅に合わせる。
実務では、多くの場合「列幅は固定し、行の高さだけを自動調整したい」という要求が来る。この場合、まず `wdAutoFitWindow` で表の骨格を決め、その後、行の高さルールを制御するのが正解だ。
鉄則③:オブジェクトのライフサイクルを意識せよ
大量のデータを流し込む際は、まず「器(テーブル)」の構造を完成させ、プロパティ(幅やルール)を確定させてから、最後にデータを流し込む。あるいは、流し込み完了後に「一括で」レイアウトを整える。
3. 【プロダクションコード】動的テーブル生成の決定版
以下に、実務でそのまま使える、保守性の高い設計パターンを示す。このコードは、単に動くだけでなく、後からの仕様変更(例:特定の列だけ幅を変えたい等)にも耐えうる構造にしている。
Option Explicit
''' <summary>
''' 実務レベルの表生成メインルーチン
''' </summary>
Public Sub GenerateRobustReport()
Dim doc As Document
Set doc = ActiveDocument
' 1. サンプルデータの準備 (本来はDBやExcelから取得)
Dim data As Variant
data = Array( _
Array("ID", "項目名", "詳細説明"), _
Array("001", "短文", "これは短いテキストです。"), _
Array("002", "長文", "ここには非常に長いテキストが入ります。改行が含まれる場合や、" & _
"一文が極端に長い場合でも、行の高さが自動的に拡張され、" & _
"テキストがセルからはみ出さないように設計されています。"), _
Array("003", "境界テスト", "連続する改行" & vbCrLf & vbCrLf & "による高さの変化を確認。") _
)
' 2. テーブルの生成
Dim rowCount As Long, colCount As Long
rowCount = UBound(data) + 1
colCount = UBound(data(0)) + 1
Dim tbl As Table
Set tbl = doc.Tables.Add(Range:=doc.Range(doc.Content.End - 1), _
NumRows:=rowCount, _
NumColumns:=colCount)
' 3. テーブルの基本プロパティ設定(構築フェーズ)
With tbl
.AllowAutoFit = True ' AutoFit機能を有効化
.AutoFitBehavior wdAutoFitWindow ' まずはページ幅に合わせる
.Borders.Enable = True ' 罫線を表示
' ヘッダー行のスタイル設定(実務ではここを分離する)
Dim i As Long
For i = 1 To colCount
.Cell(1, i).Range.Text = data(0)(i - 1)
.Cell(1, i).Range.Font.Bold = True
.Cell(1, i).Shading.BackgroundPatternColor = wdColorGray10
Next i
' 4. データの流し込み(注入フェーズ)
Dim r As Long, c As Long
For r = 2 To rowCount
For c = 1 To colCount
.Cell(r, c).Range.Text = data(r)(c - 1)
Next c
Next r
' 5. レイアウトの最終確定(最適化フェーズ)
' ここで「行の高さ」を自動調整ルールに強制設定する
ApplyDynamicRowHeight tbl
End With
MsgBox "レポートの生成が完了しました。", vbInformation
End Sub
''' <summary>
''' テーブルの行高さをコンテンツに応じて自動調整する専用メソッド
''' </summary>
''' <param name="targetTable">対象となるTableオブジェクト</param>
Private Sub ApplyDynamicRowHeight(ByRef targetTable As Table)
Dim rowIdx As Long
On Error GoTo ErrorHandler
' 行ごとに高さルールを「自動」に設定することで、
' セル内のテキスト量に応じてWordが計算を行うように仕向ける
For rowIdx = 1 To targetTable.Rows.Count
With targetTable.Rows(rowIdx)
.HeightRule = wdRowHeightAuto ' これが最重要:固定値(Exactly)を解除する
.Height = 0 ' 高さを0に設定することで、最小値からの自動計算を促す
End With
Next rowIdx
Exit Sub
ErrorHandler:
Debug.Print "Error in ApplyDynamicRowHeight: " & Err.Description
' 実務ではここでログ出力やユーザーへの通知を行う
End Sub
4. アーキテクトからのアドバイス:運用のための注意点
このコードを実戦投入するにあたり、以下の2点を肝に銘じておけ。
データベース・外部連携時の「型」の罠
ExcelやSQL Serverからデータを取得する場合、`Null` 値や `Empty` 値が混入することがある。Wordの `Cell.Range.Text` に `Null` を直接渡そうとすると、実行時エラーで即死する。必ず `CStr()` 関数などで文字列型へ明示的にキャストするか、空文字 `””` への置換ロジックを挟め。
パフォーマンスの限界を知る
もし、生成するテーブルが数百行、数千行に及ぶ場合、`Table.Rows` コレクションへのアクセス自体がボトルネックになる。その場合は、テーブル全体に対して一度だけ `AutoFit` を適用し、個別の行操作を最小限にする設計へシフトせよ。
結論
「動くコード」を書くのは誰にでもできる。しかし、「壊れないコード」を書くには、オブジェクトがどのように描画され、どのようなルールに従って制御されているかという、内部メカニズムへの深い理解が必要だ。
`HeightRule = wdRowHeightAuto`。
この一行に込められた意味を、忘れるな。
