Word VBAを掌握する極限の知見:無駄な改ページを駆逐する「次と分離しない」一括制御エンジンの設計
開発プロジェクトの現場で、Word書類のレイアウト崩れに頭を抱えた経験はないだろうか。
「見出しとそれに続く本文が、ページの上下で無残に引き裂かれている」
「表の途中で不自然な改ページが発生している」
これらを防ぐためにWordが備えているのが、段落プロパティの「次と分離しない(Keep with next)」という機能だ。手動で設定する分には非常に優秀だが、数百ページに及ぶ仕様書や契約書を相手にする時、この設定をポチポチと手動で変更するなどというのは、エンジニアの恥と知るべきだ。
今回は、Word VBAを駆使して、文書全体または選択範囲の「次と分離しない」設定を一括でトグル(ON/OFF切替)する、実務直結型の堅牢な自動化ツールを解説する。
—
1. なぜ「愚直なループ」は現場で破綻するのか
まず、素人が書く「動くだけの危険なコード」を見てみこう。
‘ 【アンチパターン】絶対に行ってはいけない実装
Sub BadExample()
Dim p As Paragraph
For Each p In ActiveDocument.Paragraphs
‘ 毎回プロパティにアクセスし、無条件に書き換える
p.KeepWithNext = True
Next p
End Sub
このコードの何が問題か。
1. トランザクションの概念がない: 文書全体の段落を1つずつCOMオブジェクト経由で書き換えるため、画面描画やイベントが走り、処理が重い(フリーズしたように見える)。
2. 状態の考慮(トグル)がない: すべて一律で`True`にしてしまい、本来改行されるべき最後の段落までくっついてしまう。
3. エラーハンドリングの欠如: 保護された領域や読み取り専用のストーリーが含まれている場合、容赦なく実行時エラーでクラッシュする。
我々プロフェッショナルが作るべきは、「現在の状態を判定し、ユーザーの意図通りにスマートに一括反転させ、エラーを完全に包み込む(Robust)」コードだ。
—
2. 堅牢なプロダクションコードの実装
以下のコードは、選択範囲(または文書全体)を対象にし、現在の「次と分離しない」設定の状況をスマートに判定して一括でトグル(反転)させる実用スクリプトである。
Option Explicit
‘ ==============================================================================
‘ 担当者: チーフアーキテクト
‘ 概要 : 段落の「次と分離しない」設定を一括トグルするプロダクションコード
‘ 備考 : 選択範囲がある場合はその中を、ない場合は文書全体を対象に処理する
‘ ==============================================================================
Public Sub ToggleKeepWithNext()
‘ 1. エラーハンドリングの要塞化
On Error GoTo ErrorHandler
‘ 2. 画面描画の凍結による爆速化(パフォーマンス最適化の基本)
Application.ScreenUpdating = False
Application.DisplayAlerts = wdAlertsNone
Dim targetRange As Range
Dim targetParagraphs As Paragraphs
Dim p As Paragraph
Dim currentSetting As Long
Dim targetState As Long
Dim processedCount As Long
‘ 3. 処理対象のスコープ決定(選択範囲 or 文書全体)
If Selection.Type = wdSelectionIP Then
‘ 選択されていない(カーソルのみ)場合は文書全体
Set targetParagraphs = ActiveDocument.Paragraphs
Else
‘ 選択範囲がある場合はその範囲内の段落
Set targetParagraphs = Selection.Paragraphs
End If
‘ 4. 判定ロジック:最初の段落の状態を見て、全体のトグル先(ON/OFF)を決める
‘ ※すべてが混在している場合は「ON(True)」に寄せる安全設計
processedCount = 0
If targetParagraphs.Count > 0 Then
currentSetting = targetParagraphs(1).KeepWithNext
‘ 999 (wdUndefined) または True の場合は Falseへ、Falseの場合は Trueへ反転
If currentSetting = True Then
targetState = False
Else
targetState = True
End If
‘ 5. ループ処理による一括適用
For Each p In targetParagraphs
‘ 変更が必要な場合のみ書き込み(無駄なCOM通信を避ける)
If p.KeepWithNext <> targetState Then
p.KeepWithNext = targetState
End If
processedCount = processedCount + 1
Next p
End If
‘ 6. 正常終了処理
Application.ScreenUpdating = True
Application.DisplayAlerts = wdAlertsAll
MsgBox “処理が完了しました。” & vbCrLf & _
“対象段落数: ” & processedCount & “件” & vbCrLf & _
“設定状態: ” & IIf(targetState = True, “「次と分離しない」を適用”, “「次と分離しない」を解除”), _
vbInformation, “一括処理ツール”
Exit Sub
ErrorHandler:
‘ 7. 異常系の確実な復旧
Application.ScreenUpdating = True
Application.DisplayAlerts = wdAlertsAll
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, _
vbCritical, “システムエラー”
End Sub
—
3. コードのアーキテクチャ解説:なぜこの設計なのか?
① 画面描画の凍結 (`ScreenUpdating = False`)
Word VBAにおいて、文書オブジェクトを1つずつ操作するたびにWordは画面の再描画を試みる。これが数千行のドキュメントになると数分単位の遅延を生む原因となる。処理の最初に画面をロックし、最後に解放することで、体感速度を数十倍〜数百倍に跳ね上げている。
② 選択範囲(Selection)と文書全体(Range/Paragraphs)の動的切替
ユーザーが「ここだけ直したい」と一部を選択している場合と、文書全体を直したい場合の両方に対応できるようにした。`Selection.Type = wdSelectionIP` を用いることで、単なるカーソル位置なのか、文字列や段落が選択されているのかを正確に判別している。
③ 変更が必要な場合のみ書き込む (`p.KeepWithNext targetState`)
Word VBAはCOM(Component Object Model)を介してWindowsのWordプロセスと通信している。そのため、既に`True`であるものに対して再度`True`を代入するだけでも、無駄なCOM境界を跨ぐコストが発生する。この「差分のみ書き込む」というエンジニアリング的配慮が、大規模文書を扱う際の安定性を担保する。
—
4. 現場へ導入する際の実務上の注意点
1. 変更履歴と保護機能
文書に「変更履歴の記録」や「文書の保護」がかかっている場合、VBAからの書式変更が拒絶される、あるいは意図しない変更履歴として残る場合がある。必要に応じてスクリプトの先頭で `ActiveDocument.TrackRevisions = False` などを制御に組み込むと、より実務で堅牢に動作する。
2. 表(Table)の中の段落への挙動
Wordの仕様上、表内のセルに含まれる段落にもこのプロパティは適用される。表の行がページをまたぐ際の挙動に影響を与えるため、表構造が複雑な文書に適用する場合は、事前にバックアップを取る運用ルールを徹底してほしい。
—
総括
「次と分離しない」一括設定ツールという、一見すると地味な自動化。しかし、ここに示した「パフォーマンス最適化」「スコープの動的判定」「差分書き込み」「完全なエラーハンドリング」というアーキテクチャは、あらゆるWord VBA開発に応用できる最高峰のスタンダードである。
退屈で不毛な手作業をコードで駆逐し、より本質的な創造的業務にリソースを集中させること。それこそが、我々エンジニアの存在意義である。
