【入門編】【再帰的探索】”Slide.Shapes”を深く掘り下げる:グループ化されたシェイプ(GroupShapes)の内部まで巡回して特定テキストを一括置換する堅牢ロジック – PowerPoint VBA解析バイブル

スポンサーリンク

こんにちは!PowerPointの自動化の世界へようこそ。
マクロの記録ボタンを押して生成されたコードを眺めて、「よし、これで完璧だ!」と思った矢先、実際の業務でグループ化された図形(テキストボックスなど)の文字が一切置換されずに絶望した経験はありませんか?

「あれ? コードは合っているはずなのに、なぜかスルーされる……」

そう、PowerPoint VBAにおける最大の罠、それが「グループ化(`GroupShapes`)の壁」です。通常の`ActivePresentation.Slides(1).Shapes`という浅いループだけでは、グループの「中に隠れた」シェイプたちには一生アクセスできません。

今回は、この厄介な壁を軽々と突破し、何重にもネストされたグループの奥底まで完全踏破する「再帰的探索(Recursive Search)」の極意を、優しく、そして徹底的に解説していきます。
ここをクリアすれば、あなたのPowerPoint VBAスキルは確実に「初学者」のステージを卒業し、「プロフェッショナル・アーキテクト」の領域に到達しますよ!

1. なぜ通常のループではグループ内のテキストが見つからないのか?

まずは、PowerPointのオブジェクト構造(DOMのようなもの)の基本を確認しておきましょう。

PowerPointの各スライド(`Slide`)は、その直下に配置されたシェイプのコレクションである `Shapes` を持っています。しかし、ユーザーが複数の図形を「グループ化」すると、そのグループ自体が1つの「親シェイプ」となり、本当のターゲットであるテキストボックスや図形たちは、その親のお腹の中にある別世界のコレクション――`GroupItems`(または `GroupShapes`)の中に隠れてしまいます。

[ スライド (Slide) ]
└─ [ Shapes コレクション ]
├─ 通常のテキストボックス (置換できる!)
└─ グループ化されたシェイプ (親)
└─ [ GroupItems コレクション ] ← 通常のループではここに入れない!
├─ 四角形
└─ ターゲットのテキストボックス (置換できない!)

もしグループの中にさらにグループがあった場合(多重グループ)、構造はマトリョーシカのようにさらに深くなります。
これを力技で `For` 文を何重にも書こうとすると、無限のネスト地獄に陥り、コードはメンテ不能のゴミと化します。

ここで登場するのが、「自分自身を呼び出す関数」=再帰関数(Recursive Function)です。

2. 再帰的探索のメカニズム:関数が自分自身を呼ぶ理由

再帰(Recursive)とは、平たく言えば「ロシアの民芸品・マトリョーシカの解体作業」です。

1. 箱を開ける(現在の `Shapes` を上から順に見る)。
2. もしそれが「普通の図形」なら、目的の処理(テキスト置換など)を行う。
3. もしそれが「グループ」だったなら、「よし、今開けた箱の中身に対して、もう一度まったく同じ解体作業(関数)を実行しよう!」と、自分自身を呼び出す。

これだけです。このアルゴリズムを使えば、グループが何重にネストしていようとも、わずか数十行の美しいコードで全シェイプを漏れなく網羅できます。

3. 【実践コード】グループ内部まで完全巡回するテキスト一括置換マクロ

それでは、実務でそのままコピペして使える完全版のコードを公開します。
標準モジュールにこのコードを貼り付けて実行してみてください。

Option Explicit

‘ ==============================================================================
‘ メインプロシージャ:スライドを巡回し、再帰探索を開始する
‘ ==============================================================================
Sub ExecuteDeepTextReplace()
Dim sld As Slide
Dim targetText As String
Dim replaceText As String

‘ — ユーザー設定 —
targetText = “【旧VBA】”
replaceText = “【極限VBA】”
‘ ——————

Dim totalCount As Long
totalCount = 0

‘ アクティブなプレゼンテーションの全スライドを走査
For Each sld In ActivePresentation.Slides
‘ 各スライドのShapesコレクションを「再帰関数」に送り込む
Call ProcessShapesRecursive(sld.Shapes, targetText, replaceText, totalCount)
Next sld

‘ 完了メッセージ
MsgBox “置換が完了しました!” & vbCrLf & _
“総置換箇所数: ” & totalCount & ” 箇所”, vbInformation, “自動化完了”
End Sub

‘ ==============================================================================
‘ 再帰関数:シェイプのコレクションを受け取り、グループ内までくまなく探索する
‘ ==============================================================================
Sub ProcessShapesRecursive(ByVal targetShapes As Shapes, ByVal targetStr As String, ByVal replaceStr As String, ByRef count As Long)
Dim shp As Shape

‘ コレクション内の各シェイプをループ
For Each shp In targetShapes

‘ ① グループ化されているシェイプの場合
If shp.Type = msoGroup Then
‘ 【重要】グループの親自身もテキストを持っているケース(稀にある)を考慮しつつ、
‘ その中身(GroupItems)に対して自分自身を再度呼び出す(再帰!)
Call ProcessShapesRecursive(shp.GroupItems, targetStr, replaceStr, count)

‘ ② 通常のシェイプ(テキストフレームを持つもの)の場合
Else
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
‘ テキストの置換処理を実行
If InStr(shp.TextFrame.TextRange.Text, targetStr) > 0 Then
‘ Replace関数で文字列を置き換える
shp.TextFrame.TextRange.Text = Replace(shp.TextFrame.TextRange.Text, targetStr, replaceStr)
count = count + 1
End If
End If
End If
End If

Next shp
End Sub

4. コードの注目すべきポイントとエンジニアの知見

このコードには、現場で泥臭く培ってきた「壊れないためのノウハウ」がいくつか詰まっています。

ポイントA:`ByVal targetShapes As Shapes` の妙

再帰関数において、引数の渡し方は非常に重要です。ここではシェイプのコレクション自体をオブジェクトとして受け渡し、その階層構造を美しく維持しながら潜っていきます。

ポイントB:`shp.HasTextFrame` の安全確認

PowerPointのシェイプには、テキストを持たないもの(画像、コネクタ、直線など)が無数に存在します。これらに対して安易に `TextRange` へアクセスすると、容赦なく 「実行時エラー ‘-2147188160’: 指定された名前の項目が見つかりませんでした」 が発生します。
必ず `HasTextFrame` と `HasText` の二重チェックを行うのが、プロのディフェンシブ・プログラミングです。

ポイントC:参照渡し(`ByRef count`)によるカウント

置換が何回成功したかを正確にトラッキングするため、カウント変数を `ByRef`(参照渡し)で再帰関数に共有しています。これにより、ネストの何段階奥で置換が行われても、正確な総数を集計して最後にユーザーへ報告できます。

5. 陥りやすいエラーとトラブルシューティング

最後に、このコードやPowerPoint VBAを扱う際によくある「罠」と、その回避策を共有しておきます。

  • エラー:「この図形はグループ化されているため操作できません」

→ まさに今回のテーマである「グループの壁」にぶつかっている状態です。通常の `Shapes` ループを使っている証拠ですので、本記事の `ProcessShapesRecursive` 関数に置き換えてください。

  • プレースホルダー(タイトルの文字など)が置換されない?

→ スライドのマスターページ(スライドマスター)上に配置されたテキストは、通常の `Slide.Shapes` では取得できません。もしマスター上のテキストも一括置換したい場合は、`sld.Master.Shapes` や `ActivePresentation.Designs` を対象にした別の再帰ロジックが必要になります(今回はスライド本体に絞っています)。

まとめ:PowerPoint VBAの基本はバッチリです!

お疲れ様でした!
「グループ化されたシェイプの内部を再帰的に探索する」というテクニックは、一見すると難解に思えますが、本質を理解してしまえば非常にシンプルで美しい仕組みです。

ここをクリアできたあなたなら、もうマクロの記録に頼り切る必要はありません。どんなに複雑にレイアウトされ、何重にもグループ化された社内フォーマットであっても、一瞬で意のままに自動整形できるエンジニアとしての確かな武器を手に入れました。

日々の退屈なコピペ作業から解放され、より創造的な仕事に時間を使いましょう。それでは、良きVBAライフを!

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