はじめに
本記事では、Excelにおいて、あるシート内に複数のグループ化された図形(あるいはテキストボックス、画像など)のグループを、多階層にわたって一斉に解除するマクロをご案内します。何故この記事を書こうと思ったかと言うと、自分が仕事でそのような処理が必要になってネットを調べてみたところ、1階層のみのグループを解除する方法は多く見つかったのですが、紹介されていた多くの方法ですと、何度もグループ化を連続して行った複雑な図形に対しては、最後のグループ化しか解除できないことが多かったためです。
ちなみに、動作確認はExcel2013でのみ行っております。
多階層のグループ化って?
そもそも、記事の対象にしている多階層のグループ化とはどのようなことを表しているかについて、説明します。
例えば、テキストボックスと矢印の図形を同時に選択して、右クリック→「グループ化」を選択すると、2つの図形が連結され、以後、移動や拡大・縮小したい時などには、図形のうち1つを選択すれば、同時に複数の図形に対して処理ができるようになります。これがグループ化です。
グループ化した図形は、グループ化した図形同士、あるいは、グループ化した図形と単独の図形、と言った具合に、更に新しいグループを何度も作成することができます。このことを、この記事では多階層のグループと呼んでいます。
エクセルで図面などを設計していくと、新たに矢印やら注釈やら加えていくうちに、グループが何階層にもなってしまうことがあります。
上の図が例で、四角やら星やら矢印やら合計7つのオブジェクトを何段階かを経てグループ化を行いました。1つの大きなオブジェクトとして全体が選択された状態になっています。
これを全て解除したくなった場合、もし手動で行うとなると、右クリック→「グループ化の解除」を何度も行う必要があります。特に、グループ化を何段階にも分けて行っていた場合、それぞれのパーツごとにグループ化を解除しなければいけません。オブジェクトを2つに分解して、その両方を3つずつに分解して、更にその3つ(計6つ)を分解…といった具合です。手動で行っていたら解除漏れなどのミスも出てくるでしょうし、何百ページにもわたるシートの場合は、そんなことやってられません。
そのような操作を自動で行える構文を考えた、というのが今回の内容の趣旨というわけです。
ちなみに、なぜグループを全解除したくなったかと言いますと、僕の場合は、実務において、全てのオブジェクトに対して文字列操作が必要になり、グループ化されたままだと、個々のオブジェクトに書かれた文字にアクセスするのが非常に難しいことがわかっためです。そこで、いったんグループ化されたオブジェクトを全てバラバラにする必要が出てきました。他にも、ごちゃごちゃになった図をいったん整理して作り直したいなどの理由もあるかと思います。
全グループ解除のコード例
グループ化を階層も考慮して全て解除するVBAのコードは以下の通りです。
Sub グループ解除() Dim bGrp As Boolean, shp As Shape 'メインループ Do bGrp= False 'グループ存在フラグをFalseで初期化 '現在のシートの全てのオブジェクトでループ For Each shp In ActiveSheet.Shapes 'グループ化されたオブジェクトの場合、グループ存在フラグをTrueにし、 'グループを1階層だけ解除 If shp.Type = msoGroup Then bGrp= True shp.Ungroup 'グループ解除 End If Next shp '次のオブジェクトへ 'グループのオブジェクトが1つも見つからない場合は、メインループを終了する If bGrp= False Then Exit Do End If Loop End Sub
構文の説明
上記のコードは、2重ループの構文となっています。
内側のループは、9行目から17行目までの部分で、シート内にある全てのオブジェクトについて、それがグループ化されたオブジェクトかどうかを検査し、もしそうであればグループ化を1階層分だけ解除し、フラグ(このソースコードでは、変数bGrp)を立てる、という処理を行っています。
何故1階層分だけしか解除しないかというと、For Each shp in shapesの構文では、グループ内にあるオブジェクトまではアクセスできないからです(もしかしたら方法は存在するかもしれませんが…。調べた限りはわかりませんでした)。
したがって、まずは一番階層の浅いグループを解除し、それがいったん終わったら次の階層を解除・・・という具合に、階層ごとにループを解除しているのです。その階層ごとのループが、外側のループであるDoから Loopまで(5行目~23行目)の部分です。
このようにどんどん解除を続けていって、ついにグループ化されたオブジェクトが1つも無くなると、21行目のExit Doが実行され、ループを抜けることになります。
6行目のbGrp= Falseがないと、無限ループしますので注意してください。
上の例は全ての階層のグループを解除する構文ですが、第一階層まで解除したくない場合などは、Do~Loopループをなくして、その中身のループだけ記述すれば可能ですし、回数を指定してループを回せば、浅い方から第3階層までのみ、解除、といったことも可能です。
最後に
上の構文で、かなり複雑なグループオブジェクトも全解除できることも確認しました。(図は少しわかりにくいかもしれませんが、マクロ実行後の状態です。先ほどはグループ化されていたものが、バラバラの状態で選択できていることがわかるかと思います。)
ただ、スピード(効率)に関しては、これが最善の方法かどうかはわかりません。いや、もっと効率的な方法はあるでしょう。というのも、僕の方法だと、グループ化されていないオブジェクトが、何度もアクセスされてしまうからです。その多くは、12行目の条件文を満たさず、実質的な処理は何もしないため、処理速度的にほとんど影響はないと考えていますが、判定文に入ること自体が無駄であることには変わりがありません。
グループ化されているオブジェクトを発見したら、その中に再帰的に入り込んでいくといったコーディングができれば、その方がよいプログラムと言えるでしょう。その辺は方法がわかり次第記事を上げようかと思います。
また、冒頭でネットを見てもよい方法が見つからなかったと書きましたが、これに関しても、単に僕が見つけられなかっただけかもしれませんので、その点は何卒ご了承ください。
もし、本記事がどなたか目に留まり、お役に立てたのなら幸いです。