在宅仕事日記

在宅仕事日記

PR

Keyword Search

▼キーワード検索

Calendar

Profile

tanpopo3416

tanpopo3416

Comments

tanpopo3416 @ Re:Excel VBA グループ解除、再グループ化(07/20) 「VBA オートシェイプのグループを再帰的…
jone@ Re:Excel VBA グループ解除、再グループ化(07/20) ワークシート版も考えてみました。 図A,…
tanpopo3416 @ Re[1]:Excel VBA グループ解除、再グループ化(07/20) jone さんへ 「オートシェイプをコピーし…
tanpopo3416 @ Re:Excel VBA グループ解除、再グループ化(07/20) コメントありがとうございます。 >図…
jone@ Re:Excel VBA グループ解除、再グループ化(07/20) 私も挑戦していました。久しぶりのプログ…

Freepage List

Jul 20, 2020
XML
カテゴリ: Excel VBA
エクセルマクロを勉強始めてから、年数が経ちました。
はじめは、メールマガジンの教材を購入しました(Wordマクロの書籍は購入していたのですが、Excelマクロの勉強は初めてでした)。
教材で勉強して、簡単なマクロは作れるようになったのです。
勉強のため、グループ化されたオートシェイプを解除して、再グループ化して元に戻すマクロに挑戦しました。

はっきり言って、無謀でした。VBAの基礎を学んだだけで、配列の勉強もしていなかったのです。
でも、難しいマクロに挑戦することが、勉強になると思いました。
セルを扱うマクロは、サンプルも多くて、情報があふれています。
あえて、図形を扱いたかったのですが、異常にむずかしくて・・・。
一重グループ化なら、何とかなりそうですが、実用的ではありません。


まずは、ネット検索して、方法を調べました。
グループ化解除は、サンプルマクロがたくさんありました。
再帰という方法で、多重グループ化にも対応でするそうです。再帰が何かすら初めて知りました。
グループ解除のコードは、ほぼサンプル丸写ししました。
ワンパターンなので、サンプルはみな似ていました。

問題は、どうやって再グループ化するかです。
ネットでさがしても、サンプルが見つかりません。
再グループ化のコードを求めている人はいるのですが、解決法は見当たらなくて。
その後、オンライン講座を受講したり、書籍でも勉強しました。
新たな勉強をするたび、このマクロに挑戦するのですが、毎回途中で挫折です。

どのような手順ですればよいのか、頭の中で組み立てられないと、コードを書くことはできません。


今年に入って、2冊のVBA本でさらに勉強しました。。
VBAの勉強もかなりしてきましたので、最低限の知識はあるはず。
配列も知らなかった頃とは違います。
学んだことを工夫すれば、完成できるはずでした。

はっきり言って、このマクロは勉強のためで、完成しても使わないと思います。

他のもっと簡単なマクロに挑戦しても、未完成なこのマクロが気にかかります。

これまでネットで検索しても有益な情報がなかったのですが、ヒントらしきものは見つかりました。
図形を階層式にワークシートに書き出して、それを復元するというものです。
これをヒントに、ワークシートへ図のIDを書き出しました。
図のIDは変わらないと聞きました。これは使えそうだと思いました。
グループ化を解除するときに、ワークシートに図のIDを書いていくコードを記述しました。
ここまでできて、ようやく完成が可能に思えてきました。
あとは、ワークシートをもとに、グループ化していくだけです。
といっても、簡単ではありませんでした。
迷ったのが、複数にグループ化されている場合です。
グループ化解除マクロを実行すると、ワークシートに図のIDが書き出されます。

グループ1 13 14 12 119 118
グループ2 13 14 12 * *

グループ1とグループ2は、同じ図のIDがあります。
13、14、12は二重にグループ化されているのです。
ワークシートには、グループ化を解除する順に上の行から表示されます。

再グループ化するときは、下の行からグループ化を行います。
まず、グループ2の図のID13、14、12をまずグループ化します。

次は、グループ1の5つの図形をグループ化します。
ワークシートの図のIDと一致する図形をさがしていきます。
でも、13、14、12の図形は、グループ化されていしまっています。
そこで、グループ化された図形の中に、一致するIDの図形がないかさがします。
見つかったら、グループ化された図形を選択します。
13、14、12は同じグループです。
3つとも同じグループ名なので、1回グループ化された図形を選択すればOKです。
でも、同じグループ名ならどうするかコードがないと、3回選択することになります
(エラーになります)。
それに対応するため、配列にIDを入れています。
既にIDにグループ化されたIDが入っていたら、何もしません。最初に一致した場合のみ、選択するようにします。

もともと、配列には図のIDではなく名前を入れていました。
これでグループ化するのは簡単だったのです。
でも、後で書きますが、図形によってはエラーになってしまいましたので、図を1つずつ選択する方法に変えたのです。だから、配列はもう不要かと思ったのですが、配列にIDを入れて、グループ化されたIDが入っているかどうかの確認に使いました。

連想配列にはオブジェクトを格納できるます。連想配列を使うと、普通の配列と1つずつ図形を選択するコードより、簡単にできるかもしれません。
最善の方法をさがす余裕はなく、どんなコードでも良いから、とりあえず完成させたいだけでした。
こんな面倒なことしなくても…と思われるような、未熟なコードだと思います。
でも、完成するまで考えたことは勉強になりました。
このコードをそのまま使わないとしても、部分的に今後も参考になりそうです。
今後のためにも、うまくいかなかったこと、考えたことを含めて、一応記録しています。

前述のとおり、配列(下のコードのmyAry)には、図のIDではなくIDを入れるようにした理由について書いておきます。

はじめは、グループ化するために、配列を使って、図の名前を入れていました。
  myAry(cnt) = shp.Name

そうすると、下記のコードで、13、14、12をグループ化できるのです。
                 shWs.Shapes.Range(myAry).Select


ところが、図形をグループ化して試したところ、上記のコードでエラーが出てしまいました。
図の名前が同じものがあると(図形をコピーすると、その方法によっては、図形の名前がおなじになります)、エラーが出るようです。
同じ名前がある場合のことを考えて、図のIDを書き出していたのに、これではうまくいかないですよね。

結局、配列には図の名前ではなく、図のIDを入れることにしました。
                            myAry(cnt) = shp.ID

図のIDがmyAryに入っていますので、下記のコードは使えません。。
myAryには図の名前が必要なのです。
                 shWs.Shapes.Range(myAry).Select
                 Selection.ShapeRange.Group

上記のコードはやめて、別の方法を考えました。
ワークシートの図のIDと一致する図形が見つかったら、その都度選択することにしました。
グループ2の3つの図形を1つずつ選択して追加していき、グループ化します。
配列のmyArmは、グループ化された図がすでに選択されているかの確認に使うことにしました。

ワークシードの図のIDと一致する図を見つけるたび、選択することにしたので、上記のコードも変更しました。既に図を選択しているので、Selectionから始めています。

                 Selection.ShapeRange.Group



グループ化解除マクロで、すべてのグループ化を解除して、ワークシーとに図のIDを書き出します。
再グループ化マクロで、ワークシートのリストをもとに、もとの状態に戻します。
図形を描いて試したのですが、グループ化の回数を増やすと、うまくいかなかったり。
ようやくエラーが出ないものが完成したのですが、複雑な図形で問題なく動作するかはわかりません。

Option Explicit
  Dim yoko As Long
  Dim tate As Long

Sub グループ化解除()
    yoko = 0
    tate = 0
    Dim flagWs As Boolean '図のIDシートの有無の判定
    Dim shp As Shape
    Dim ws As Worksheet
    flagWs = False
    Dim shWs As Worksheet
    Set shWs = ActiveSheet
    If shWs.Shapes.Count = 0 Then
        MsgBox "図形のあるシートを表示してから実行してください"
        Exit Sub
    End If
    For Each ws In Worksheets
        If ws.Name = "図のID" Then
            Worksheets("図のID").Range("A1").CurrentRegion.Delete
            flagWs = True
            Exit For
        End If
    Next   
    If flagWs = False Then
            Worksheets.Add
            ActiveSheet.Name = "図のID"
    End If
    shWs.Select
    For Each shp In ActiveSheet.Shapes
        Call UnGroup(shp)
    Next
End Sub
Sub UnGroup(sh As Shape)
  Dim has_grp As Boolean  'グループ化されているかどうか判定する
  Dim gShp As Shape
  Dim idWs As Worksheet
  Set idWs = Worksheets("図のID")
        If sh.Type = msoGroup Then
            has_grp = True
            For Each gShp In sh.GroupItems
                If yoko = 0 Then
                    idWs.Range("A1").Offset(tate, yoko).Value = "グループ" & tate + 1
                    yoko = yoko + 1
                End If
                idWs.Range("A1").Offset(tate, yoko).Value = gShp.ID
                yoko = yoko + 1
            Next
            tate = tate + 1
            yoko = 0
            Do While has_grp
                For Each gShp In sh.UnGroup
                    Call UnGroup(gShp)
                Next
                has_grp = False
            Loop
        End If
End Sub
Sub グループ化解除した図形を再グループ化()
    Dim shp As Shape
    Dim gyo As Long
    Dim maxCol As Long
    Dim ret As Long
    Dim maxGyo As Long
    Dim gp() As Variant
    Dim cnt As Long
    Dim myAry() As Variant
    Dim c As Long
    Dim flg As Boolean
    Dim gShp As Shape
    Dim n As Long
    Dim idWs As Worksheet
    Set idWs = Worksheets("図のID")
    If ActiveSheet.Shapes.Count = 0 Then
        MsgBox "図形のあるシートを表示してから実行してください"
        Exit Sub
    End If
    '★もし図形を選択していたら、選択を解除するコードを入れる
    maxGyo = idWs.Range("A" & Rows.Count).End(xlUp).Row
        For gyo = maxGyo To 1 Step -1
               maxCol = idWs.Range("A" & gyo).End(xlToRight).Column - 1
            cnt = 0
            Erase myAry
            For ret = 1 To maxCol
                For Each shp In ActiveSheet.Shapes
                      If shp.Type <> msoGroup Then
                        If shp.ID = idWs.Range("A" & gyo).Offset(, ret).Value Then
                            ReDim Preserve myAry(cnt)
                            myAry(cnt) = shp.ID
                            shp.Select Replace:=False
                            cnt = cnt + 1
                            Exit For
                        End If
                      Else
                      flg = False
                        For Each gShp In shp.GroupItems
                                If gShp.ID = idWs.Range("A" & gyo).Offset(, ret).Value Then
                                    '同じグループ化された図形が既に配列に入っていないか確認する                 
                                    If Not Not myAry Then '配列か空かどうかを判定す
                                            For n = LBound(myAry) To UBound(myAry)
                                                If myAry(n) = shp.ID Then
                                                    flg = True
                                                End If
                                            Next
                                    End If
                                      'グループ化がまだ配列に入っていないときの時、配列に図形名を入れる。
                                    If flg = False Then
                                         ReDim Preserve myAry(cnt) 'この位置?
                                         myAry(cnt) = shp.ID
                                          shp.Select Replace:=False
                                          cnt = cnt + 1
                                          Exit For
                                    End If                                
                                 End If
                            Next
                        End If
                      Next
            Next
                Selection.ShapeRange.Group
        Next
        Application.DisplayAlerts = False
           idWs.Delete
        Application.DisplayAlerts = True
End Sub





お気に入りの記事を「いいね!」で応援しよう

Last updated  Jul 21, 2020 04:12:18 PM
コメント(5) | コメントを書く


■コメント

お名前
タイトル
メッセージ
画像認証
上の画像で表示されている数字を入力して下さい。


利用規約 に同意してコメントを
※コメントに関するよくある質問は、 こちら をご確認ください。


Re:Excel VBA グループ解除、再グループ化(07/20)  
jone さん
私も挑戦していました。久しぶりのプログラミングでした。
その過程で本HPを知りました。初めて投稿します。
=====
図のIDとは、shape.idのことでしょうか?
このidだけでは、図は一意に決まりません。というのは、長方形や吹き流しを入力すると、同じidが現れました。
多分、shape.typeとshape.idの組で一意に決まると思います。
=====
再グループ化には、
1)Groupメソッドの順番、2)各Groupにおける対象メンバを決める必要があります。
Idをエクセルシートに出力し、1)、2)を決定しようとして苦労してますね...
視点を変えて、
・グループ解除は、再帰呼び出しなので、その呼び出し順番から1) が
・UnGroupメソッドで、メンバのオブジェクトが返却されるので2) が 分かります。
=====
再グループ化には、まだまだハードルがありますが、今回はここまで (Sep 17, 2021 09:48:50 PM)

Re:Excel VBA グループ解除、再グループ化(07/20)  
tanpopo3416  さん
コメントありがとうございます。

>図のIDとは、shape.idのことでしょうか?
>このidだけでは、図は一意に決まりません。というのは、長方形や吹き流しを入力すると、同じidが現れました。

コードは昨年作ったものですので、どうやって作成したのか記憶が曖昧ですが、図のIDは、shape.idで合っています。
長方形や吹き流しを入力すると、同じidが現れるのですね。
私はオートシェイプをコピーした時、同じidが出現したことがあります。
同じidでももうまくいくように作成したつもりですが、今確認しようとしても、同じidがなかなか出現しません…。出現した時、試して結果を書きますね。

>再グループ化には、
>1)Groupメソッドの順番、2)各Groupにおける対象メンバを決める必要があります。
Idをエクセルシートに出力し、1)、2)を決定しようとして苦労してますね...
視点を変えて、
>・グループ解除は、再帰呼び出しなので、その呼び出し順番から1) が
・UnGroupメソッドで、メンバのオブジェクトが返却されるので2) が 分かります。

再グループは苦労しました。
提案される方法は私には難しいのですが、時間があればまた試してみたいと思います。 (Sep 18, 2021 05:30:28 PM)

Re[1]:Excel VBA グループ解除、再グループ化(07/20)  
tanpopo3416  さん
jone さんへ
「オートシェイプをコピーした時、同じidが出現したことがある」と書きましたが、idではなく同じ名前(Shape.name)と勘違いしていたかもしれません。
いずれにしても、同じidが出現したら、確認してみます。 (Sep 18, 2021 05:48:25 PM)

Re:Excel VBA グループ解除、再グループ化(07/20)  
jone さん
ワークシート版も考えてみました。

図A,B,Cがグループ化され、図D,Eがグループ化され、さらに、この2つがグループ化されているとします。これを便宜的に((A B C)(D E))と記すことにします。
エクセルではグループobjという特殊なオブジェクトでグループを表現します。
パソコンの内部では(☆3 (☆1 A B C) (☆2 D E))です。 ☆はグループobj

===>

再帰的にUnGroupしながら、ワークシートに各objのIDを書きこみます。


1行目=> ☆3 ☆1 ☆2
2行目=> ☆1 A B C
3行目=> ☆2 D E


これができれば、再グループ化の見通しも立ちます

まず、再帰については、「t-hom’s diary」の「VBA オートシェイプのグループを再帰的にグループ解除する。」によく書かれています。ソースも含めて

セル位置(TATE,YOKO)を決める必要がありますが、なんと
dim宣言,値の代入,+1という超基本な命令を数行追加するだけで完成です。
 ヒント:再帰部分は、functionに変更する
どこに何を追加するかは大いに悩むと思いますが、これより再帰処理の理解が深まります。 頑張ってください。
====
追記
・ワークシートは使わずに、1行分を","区切りの文字列とし、再グループ化の時にsplitで配列化する方法もあります。

・前回、一意性のためtypeも必要と書きましたが、私の勘違いだったかも、idだけでよさそうです。ですが、
グループ解除・再グループ化では、グループobjがキーになります。なので
個人的には、IDとして「.id * 100 + .type」を採用しています。 (Sep 21, 2021 10:47:51 AM)

Re:Excel VBA グループ解除、再グループ化(07/20)  
tanpopo3416  さん
「VBA オートシェイプのグループを再帰的にグループ解除する。」はコードを書く時の参考にさせてもらいました。

>再帰的にUnGroupしながら、ワークシートに各objのIDを書きこみます。

グループ化されたshpのIDを取得して、利用することはできそうですね。

>セル位置(TATE,YOKO)を決める必要がありますが、なんと
>dim宣言,値の代入,+1という超基本な命令を数行追加するだけで完成です。
> ヒント:再帰部分は、functionに変更する
>どこに何を追加するかは大いに悩むと思いますが、これより再帰処理の理解が深まります。 頑張ってください。

少し考えて見たものの、難しくて混乱します。
再帰的とか、あまり理解できていないので、もう一度勉強する必要がありそうです。

前のコメントの「UnGroupメソッドで、メンバのオブジェクトが返却される」のは、返却されたShpを、配列にオブジェクト型で入れる方法でしょうか。
途中まで試したものの、再グループ化の際、どうやって呼び出すのか、また躓いてしまいました。

コメントに書いてくださったこと、まだピンと来ないところもあります。
再起処理を復習してから、また挑戦したいともいます。 (Sep 22, 2021 11:25:46 AM)

【毎日開催】
15記事にいいね!で1ポイント
10秒滞在
いいね! -- / --
おめでとうございます!
ミッションを達成しました。
※「ポイントを獲得する」ボタンを押すと広告が表示されます。
x
X

© Rakuten Group, Inc.
Design a Mobile Website
スマートフォン版を閲覧 | PC版を閲覧
Share by: