【入門編】【上級者向け】段落の「書式設定」をクラスモジュールで抽象化し、再利用可能な書式適用エンジンを作る – Word VBA解析バイブル

スポンサーリンク

こんにちは!Word VBAの世界へようこそ。いつも業務効率化のためにマクロを書いてくれてありがとうございます。

「マクロの記録」を卒業し、一歩進んだプログラムを書き始めると、誰もが一度は「書式設定のコードがスパゲッティのように複雑になってしまう」という壁にぶつかります。

「文字サイズを大きくして、フォントを游ゴシックにして、行間を少し空けて……」というコードを何箇所にも書いているうちに、コードがどんどん長くなり、後から「やっぱりフォントは明朝体にして」と言われたとき、修正箇所が多すぎて頭を抱えてしまう。そんな経験はありませんか?

ここをクリアすれば、Word VBAの基本(そして応用)はバッチリです!

今回は、複雑になりがちな段落の書式設定を「クラスモジュール」という仕組みを使って美しくカプセル化(抽象化)し、まるでスタンプを押すように簡単かつ高速に書式を適用できる「自作の書式適用エンジン」を作ってみましょう。

優しく、丁寧に、そしてプロの現場で役立つ極限の知見を交えて解説します。一緒にステップアップしていきましょう!

1. なぜ直接書式を設定すると「コードが壊れる」のか?

まずは、よくある「マクロの記録」から一歩進んだだけのコードを見てみましょう。

‘ 読みにくく、メンテナンスが難しいコードの例
Sub ApplyDirectFormat()
With ActiveDocument.Paragraphs(1).Range
.Font.NameFarEast = “游ゴシック”
.Font.Size = 18
.Font.Bold = True
.Font.Color = wdColorDarkBlue
.ParagraphFormat.SpaceBefore = 12
.ParagraphFormat.SpaceAfter = 6
.ParagraphFormat.Alignment = wdAlignParagraphCenter
End With
End Sub

この書き方でも動きはします。しかし、もしドキュメント内に「大見出し」「小見出し」「本文」「引用」といった複数のスタイルがあり、それぞれにこの数行のコードを何回も書いていたらどうなるでしょうか?

直接指定の3つの罠

1. 仕様変更に耐えられない(メンテナンス性の崩壊)
「見出しのフォントをやっぱり『メイリオ』にして、サイズを16ptにしてほしい」と言われた瞬間、プログラム全体に散らばった `.Font.Size = 18` などを手作業で書き直す羽目になります。
2. パフォーマンスの著しい低下(Wordを重くする原因)
Wordの内部では、プロパティ(`.Size` や `.Color` など)を変更するたびに、ドキュメントの再計算と描画処理が走ります。直接何度もプロパティを叩くのは、Wordを疲れさせる原因になります。
3. 可読性の低下
「何を目的としてこの設定をしているのか」という意図が、大量のプロパティ設定コードに埋もれて見えなくなってしまいます。

2. クラスモジュールで「書式」を抽象化する設計図

そこで登場するのが「クラスモジュール」です。
クラスモジュールとは、一言で言えば「オリジナルのデータ型(オブジェクト)を作る機能」です。

今回は、「書式」という概念そのものをクラスに閉じ込め、「段落書式テンプレート(ParagraphFormatter)」というオブジェクトを作ります。

イメージとしては、以下のような図になります。

【従来の直接指定】
[段落1] ──> 直接 Font.Size / SpaceBefore / Alignment を個別に書き換える(毎回Wordが再描画)
[段落2] ──> 直接 Font.Size / SpaceBefore / Alignment を個別に書き換える

【クラスによる抽象化】
[書式テンプレート (Formatter)] ── (設定値を保持)

├─ Applyメソッド ──> [段落1に一括適用!]
└─ Applyメソッド ──> [段落2に一括適用!]

書式の設定値(状態)をクラスの中に保持しておき、適用するときは `Apply` というメソッドを1回呼ぶだけにします。こうすることで、メインの処理コードは驚くほどスッキリします。

3. 【実践】書式適用エンジンの構築

さあ、実際に手を動かして作ってみましょう!
WordのVBAエディタ(Alt + F11)を開き、プロジェクトエクスプローラーから 「挿入」 > 「クラスモジュール」 を選択してください。

作成されたクラスモジュールのオブジェクト名を `ParagraphFormatter` に変更し、以下のコードを貼り付けます。

① クラスモジュール:`ParagraphFormatter` のコード

‘ =========================================================================
‘ クラス名: ParagraphFormatter
‘ 役割: 段落とフォントの書式情報を保持し、指定されたRangeへ高速・安全に適用する
‘ =========================================================================
Option Explicit

‘ 内部で保持するプライベート変数(メンバ変数)
Private m_FontName As String
Private m_FontSize As Single
Private m_Bold As Boolean
Private m_TextColor As WdColor
Private m_LineSpacing As Single
Private m_SpaceBefore As Single
Private m_SpaceAfter As Single
Private m_Alignment As WdParagraphAlignment

‘ ————————————————————————-
‘ 初期化イベント(インスタンス生成時にデフォルト値をセット)
‘ ————————————————————————-
Private Sub Class_Initialize()
m_FontName = “游明朝”
m_FontSize = 10.5
m_Bold = False
m_TextColor = wdColorAutomatic
m_LineSpacing = 16# ‘ 行間(ポイント指定用、少しゆったりめ)
m_SpaceBefore = 0#
m_SpaceAfter = 0#
m_Alignment = wdAlignParagraphLeft
End Sub

‘ ————————————————————————-
‘ プロパティの定義 (外部から安全に値を設定・取得するための窓口)
‘ ————————————————————————-
Public Property Get FontName() As String: FontName = m_FontName: End Property
Public Property Let FontName(ByVal val As String): m_FontName = val: End Property

Public Property Get FontSize() As Single: FontSize = m_FontSize: End Property
Public Property Let FontSize(ByVal val As Single): m_FontSize = val: End Property

Public Property Get Bold() As Boolean: Bold = m_Bold: End Property
Public Property Let Bold(ByVal val As Boolean): m_Bold = val: End Property

Public Property Get TextColor() As WdColor: TextColor = m_TextColor: End Property
Public Property Let TextColor(ByVal val As WdColor): m_TextColor = val: End Property

Public Property Get LineSpacing() As Single: LineSpacing = m_LineSpacing: End Property
Public Property Let LineSpacing(ByVal val As Single): m_LineSpacing = val: End Property

Public Property Get SpaceBefore() As Single: SpaceBefore = m_SpaceBefore: End Property
Public Property Let SpaceBefore(ByVal val As Single): m_SpaceBefore = val: End Property

Public Property Get SpaceAfter() As Single: SpaceAfter = m_SpaceAfter: End Property
Public Property Let SpaceAfter(ByVal val As Single): m_SpaceAfter = val: End Property

Public Property Get Alignment() As WdParagraphAlignment: Alignment = m_Alignment: End Property
Public Property Let Alignment(ByVal val As WdParagraphAlignment): m_Alignment = val: End Property

‘ ————————————————————————-
‘ 書式適用エンジンの中核(Applyメソッド)
‘ ※重要: Paragraphオブジェクトではなく「Range」を受け取ることがWord最速化の極意
‘ ————————————————————————-
Public Sub Apply(ByVal TargetRange As Word.Range)
If TargetRange Is Nothing Then Exit Sub

‘ Word VBAの黄金律:現在の設定値と異なる場合のみプロパティを更新することで、
‘ Wordの再計算・再描画コストを極限まで抑える

‘ 1. フォント書式の適用
With TargetRange.Font
‘ 日本語フォントと英数字フォントの両方に安全に適用
If .NameFarEast <> m_FontName Then .NameFarEast = m_FontName
If .NameAscii <> m_FontName Then .NameAscii = m_FontName

If .Size <> m_FontSize Then .Size = m_FontSize
If .Bold <> m_Bold Then .Bold = m_Bold
If .Color <> m_TextColor Then .Color = m_TextColor
End With

‘ 2. 段落書式の適用
With TargetRange.ParagraphFormat
If .Alignment <> m_Alignment Then .Alignment = m_Alignment

‘ 行間を「固定値」として美しく設定するためのルール指定
.LineSpacingRule = wdLineSpaceExactly
If .LineSpacing <> m_LineSpacing Then .LineSpacing = m_LineSpacing

If .SpaceBefore <> m_SpaceBefore Then .SpaceBefore = m_SpaceBefore
If .SpaceAfter <> m_SpaceAfter Then .SpaceAfter = m_SpaceAfter
End With
End Sub

② 標準モジュール:エンジンを呼び出すメインコード

次に、標準モジュール(「挿入」 > 「標準モジュール」)を作成し、作成した `ParagraphFormatter` クラスを実際に使うコードを書きましょう。

‘ =========================================================================
‘ モジュール名: Module1
‘ 役割: 自作した書式適用エンジンを使って、ドキュメント全体を美しく整える
‘ =========================================================================
Option Explicit

Public Sub ExecuteDocumentFormatting()
Dim doc As Word.Document
Set doc = ActiveDocument

‘ 画面更新と自動校正を一時停止して、マクロを劇的に高速化!
On Error GoTo ErrorHandler
TogglePerformanceMode True

‘ ———————————————————————
‘ 1. テンプレート(Formatterオブジェクト)の定義
‘ ———————————————————————

‘ ◆ タイトル用の書式
Dim TitleFormatter As ParagraphFormatter
Set TitleFormatter = New ParagraphFormatter
With TitleFormatter
.FontName = “游ゴシック”
.FontSize = 18
.Bold = True
.TextColor = wdColorDarkBlue
.SpaceBefore = 12
.SpaceAfter = 6
.Alignment = wdAlignParagraphCenter
End With

‘ ◆ 本文用の書式
Dim BodyFormatter As ParagraphFormatter
Set BodyFormatter = New ParagraphFormatter
With BodyFormatter
.FontName = “游明朝”
.FontSize = 10.5
.Bold = False
.TextColor = wdColorBlack
.LineSpacing = 18 ‘ 行間を広げて読みやすく
.SpaceAfter = 3
End With

‘ ———————————————————————
‘ 2. ドキュメントへの適用(ループ処理)
‘ ———————————————————————
Dim i As Long
Dim currentRange As Word.Range

For i = 1 To doc.Paragraphs.Count
‘ 【極意】Paragraphオブジェクトを直接弄らず、Rangeを切り出して操作する
Set currentRange = doc.Paragraphs(i).Range

‘ 空行(改行コードのみの段落)は処理をスキップして高速化
If Len(Trim(Replace(currentRange.Text, vbCr, “”))) > 0 Then

‘ 最初の段落は「タイトル」、それ以降は「本文」として適用
If i = 1 Then
TitleFormatter.Apply currentRange
Else
BodyFormatter.Apply currentRange
End If

End If
Next i

MsgBox “ドキュメントの書式設定が完了しました!”, vbInformation

CleanUp:
‘ 停止していたWordの描画機能を必ず元に戻す
TogglePerformanceMode False
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub

‘ ————————————————————————-
‘ 画面描画と校正機能の制御(Wordマクロ高速化の定番テクニック)
‘ ————————————————————————-
Private Sub TogglePerformanceMode(ByVal Enable As Boolean)
With Word.Application
If Enable Then
.ScreenUpdating = False
.Options.CheckSpellingAsYouType = False
.Options.CheckGrammarAsYouType = False
Else
.ScreenUpdating = True
.Options.CheckSpellingAsYouType = True
.Options.CheckGrammarAsYouType = True
End If
End With
End Sub

4. 知っておくべきプロの知恵&陥りやすい罠

このコードには、Word VBAを開発する上で避けては通れない「プロのノウハウ」が凝縮されています。特に重要なポイントを3つ、先輩としてお伝えしておきますね。

① `Paragraph` ではなく `Range` で操作せよ

コードの中で、以下のように書いた部分があります。

Set currentRange = doc.Paragraphs(i).Range

Word VBAにおいて、`Paragraph` オブジェクトそのものを直接操作するのは非常に重い処理になります。`Paragraph.Range` を取得して「範囲(Range)」として操作する方が、メモリへの負荷が小さく、格段に処理スピードが向上します。

② 値が「異なるときだけ」書き換える

クラスモジュール内の `Apply` メソッドでは、すべてのプロパティを以下のように判定してから代入しています。

If .Size <> m_FontSize Then .Size = m_FontSize

「同じ値であっても上書きすればいいじゃないか」と思うかもしれませんが、Wordはプロパティに値が代入されるたびに、内部で「ドキュメントの再レイアウト計算」を行います。値が変わっていないなら代入しない。これだけで、数百ページある文書での処理速度が数倍から数十倍に跳ね上がります。

③ 日本語フォントと英字フォントの区別

Wordには、日本語用のフォント(`.NameFarEast`)と英数字用のフォント(`.NameAscii`)が別々に存在します。片方だけを設定すると、英数字だけがMSゴシックのまま取り残されるといった不格好な現象が起きます。クラスモジュール側で両方に同じフォント名を設定することで、この問題をスマートに解決しています。

5. まとめ:ここをクリアすればWord VBAは完璧!

お疲れ様でした!
今回作成した「書式適用エンジン(クラスモジュール)」を使うことで、メインコード(`ExecuteDocumentFormatting`)がどれだけスッキリしたか実感できたでしょうか?

もし、今後「小見出し用の書式も追加したい!」となった場合は、標準モジュール側で `Dim SubTitleFormatter As ParagraphFormatter` を作り、プロパティをセットして、`Apply` を呼ぶだけです。既存のコードを汚すことなく、いくらでも拡張できます。

マクロの記録から一歩踏み出し、このように「オブジェクト(役割)に仕事を任せる書き方」ができるようになると、あなたの書くVBAコードの美しさと信頼性は劇的に向上します。

まずはこのコードをコピペして、お手元のテスト文書で動かしてみてください。パッと一瞬で美しい書式に整う快感を味わっていただければ幸いです。

何かわからないことがあれば、いつでも聞いてくださいね。あなたのVBA開発を心から応援しています!

タイトルとURLをコピーしました