名づけて、「ダルマ落とし方式」! (Word)
名づけて「ダルマ落とし方式」!
前回
のマヌケ記事の続き。
シェイプが一掃されない理由
どうも、For Each
でループさせる際に、VBAは内部でShapes
コレクションに番号を振って、その番号順に処理をしているらしい。
ただ、Delete
メソッドで削除した場合に、その番号が自動的に繰り上がる仕組みのようだ。
つまり、普通にExcelで行とか列とかを削除したら、番号が繰り上がるのと同じ。
だから、For Each
で回しながらDelete
メソッドでコレクションの要素を削除すると歯抜け状態になる。
前回のアレの場合、もともと9
個あったシェイプが実行後4
個になったのは、
初期状態
①ア②ホ③か④ボ⑤ケ⑥か⑦カ⑧ス⑨か
コレクション番号①を削除
①ホ②か③ボ④ケ⑤か⑥カ⑦ス⑧か
コレクション番号②を削除
①ホ②ボ③ケ④か⑤カ⑥ス⑦か
コレクション番号③を削除
①ホ②ボ③か④カ⑤ス⑥か
コレクション番号④を削除
①ホ②ボ③か④ス⑤か
コレクション番号⑤を削除
①ホ②ボ③か④ス
というわけで、コレクション番号⑤まで削除したところで最後の要素に達してしまうので、4
個残る、ということなのだ。
一般化すると、要素数 \ 2
個分、つまり2で割った商 個残る計算。
歯抜け回避の方法
Twitterで ことりちゅん (id:Kotori-ChunChun) 氏が教えてくださった〈ケツから削除方式〉と、前回記事へのコメントで thom (id:t-hom) 氏が教えてくださった〈全滅するまでひたすら先頭要素を削除方式〉の二通りがある。
前者は、通常のExcelで行やら列やらを削除する場合でもおなじみなので、後者の方式を採用。
名づけて、〈ダルマ落とし方式〉!
ダルマ落とし方式を可視化する
ただ、〈ダルマ落とし方式〉のコード自体は、thom (id:t-hom) 氏がすでにコメント欄に書いてくださっているので、ダルマ落とし風に見えるようにアレンジ。
準備
まずは、「ダルマ」を準備。
ドキュメント上に、こんな風に「ダルマ」を設置。
一番下のシェイプのTop
プロパティを170
に、それぞれのシェイプのHeight
プロパティを25
にしている。
コード
マジックナンバーだらけの場当たりコードですが、一応全掲載。一般化するのがめんどくさいだけw
リスト1 標準モジュール
Option Explicit Private winAPI As WindowsAPI Public Sub droppingDharma() Dim Doc As Document Set Doc = ActiveDocument Dim targetShapes As Word.Shapes Set targetShapes = getTextAllocatedShapes(Doc.Shapes) Dim shapesTopArray As Variant shapesTopArray = getShapesTopArray(targetShapes) Set winAPI = New WindowsAPI Dim i As Long Do While Doc.Shapes.Count <> 0 Doc.Shapes(1).Delete Call winAPI.waitFor(100) For i = 1 To Doc.Shapes.Count Doc.Shapes(i).Top = shapesTopArray(i - 1) Call winAPI.waitFor(100) Next Loop End Sub Private Function getShapesTopArray( _ ByVal targetShapes As Shapes) As Variant Dim ret As Variant ReDim ret(targetShapes.Count - 1) Dim i As Long For i = LBound(ret) To UBound(ret) ret(i) = targetShapes(i + 1).Top Next getShapesTopArray = ret End Function Private Function getTextAllocatedShapes( _ ByVal targetShapes As Shapes) As Shapes Dim ar As Variant ar = Split("お,前,は,ア,ホ,か,(゚Д゚)", ",") Dim i As Long For i = LBound(ar) To UBound(ar) With targetShapes(i + 1) .Top = 170 - (25 * i) .TextFrame.TextRange.Text = ar(i) & vbCr .TextFrame.TextRange.ParagraphFormat.LineSpacing = 18 End With Next Set getTextAllocatedShapes = targetShapes End Function
あ、自作のWindowsAPI
クラスを使用しています。
詳しいことは、
コチラをどうぞ。
未確認だが、どうもShapes
コレクションのインデックス番号は常に一定というわけでもない?
実行するたびに順番が変わっているような気がしたので、getTextAllocatedShapes
というメソッドによって、毎回インデックス番号1
~7
にテキストを設定し直し、下から順に並ぶようにした。
Shape.TextFrame.TextRange.Text
プロパティの値をセットし直すと、段落設定がリセットされる(? 少なくとも「行間」の設定は解除されてしまっていた。詳しいことは調べていないので、鵜呑みにしないでください。)ので、Shape.TextFrame.TextRange.ParagraphFormat.LineSpacing
プロパティを設定し直すようにしている。
これが「ダルマ落とし」だ!
実行すると、
こうなります。まさに「ダルマ落とし」!!!!!!!!
おわりに
もちろん、作り込めば、もっとなめらかにアニメーションさせたりすることもできることでしょう。