スポンサーリンク

【Excel/VBA】テーブルデータをもとに複数のファイル作成①~クラス シートモジュール ディクショナリの実験的試行錯誤

vba
スポンサーリンク
※当サイトは広告を含みます

エクセルVBAのクラスを使うってなんかできる人っぽくて使いたがり。だがしかし、いまいち利用価値が分かっていないし、しょっちゅうエラーを出してしまう。

仕事で同じ書式のファイルを店舗数作成し、各ファイルの中身は各店舗のデータを入れる、というのがあった。これをクラスを使ってやってみようというもの。

クラスのproperty let / property get ってどうやって使うんだろう、というのがあったので使ってみる(あまり意味はない。自分が理解するための実験)

■■ 流れ ■■

①データSTORE

クラス用意:no、tenpo、area

テーブルシートのデータ
   ↓
1行分のデータをクラスにセット
   ↓
ディクショナリ
に追加(※コレクションでもいいと思う。なんとなくディクショナリが好きなので)

②ファイルを作成

~データの数だけ繰り返し~
 原本シートコピー
    ↓
 データ入力
    ↓
 名前を付けて保存
~データの数だけ繰り返し~

■■ コード、テーブル ■■ 

(1)テーブルデータ

「TB」シートにデータを入力しておく

no ,  tenpo     ,  area
1   , 北海道店 ,  北海道
2   , 福島店     ,  東北  
3   , 青森     , 東北  
4   , 東京     ,  関東  
5   , 埼玉     ,  関東
6   , 福岡     ,  九州  
7   , 長崎     九州  

(2)テーブルデータを入れるクラス

プロパティLet/Getを使ってみる

①クラスモジュールを作成する

VBEにて  クラスモジュール を挿入

プロパティでクラスモジュールのオブジェクト名をcDataとする

②LETで使うプライベート変数を用意する。変数の最後にアンダーバーをつけるのが流儀らしい。

Private no_ As String
Private tenpo_ As String
Private area_ As String
 

③LETの部分

Property Let no(ByVal vNo As String)
    no_ = vNo
End Property

ほかの二つも同様に

④GETの部分

Property Get no() As String
    no = no_
End Property

ほかの二つも同様に

Property Let でTBシートからデータをno_に取り込み、Property Get  で no にデータを入れる。なぜ二段階でやるのか初心者の私には利点がわからない。この例では意味がないが、役に立つことがきっとあるのであろう。

(3)部品1:テーブルデータをディクショナリに格納

ここも実験的にクラスでもなく標準モジュールでもなくシートモジュールを使ってみる。いつも隣にITのお仕事のタカハシさんが、シートモジュールを使ってクラスのデータを入れていたのを見たことがあったので。

Sheet1(TB) のモジュール(※1ディクショナリを利用しやすくするためにMicrosoft Scripting Runtimeを事前に参照設定しています)

1  Dim myDic As Collection
2
  Sub getdataDic()    
3      Dim i As Long
4      Set myDic = New Dictionary
5      For i = 2 To Cells(Rows.Count, 1).End(xlUp).row
6          Dim c As cData
7          Set c = New cData
8          c.no = Cells(i, 1).Value
9          c.tenpo = Cells(i, 2).Value
10        c.area = Cells(i, 3).Value
11        myDic.Add c.no, c
12        Set c = Nothing
13     Next i
14 End Sub
 
自分のシートのことなのでCellsと書くと自分自身のシートのセルを見に行ってくれる。
1:モジュール変数としてディクショナリmyDicを宣言
6、7:クラスcDataをcとして実体化
8~10:cにデータセット
11:myDicに追加。キーはno、アイテムはcの構造体(no,tenpo,area)
 
(4)部品2:データインプット
データをインプットする部品。シートモジュール
1  Sub inputdataDic(ByVal trgNo As String)
2      With ActiveSheet
3          .Range(“A2”).Value = myDic(trgNo).no
4          .Range(“B2”).Value = myDic(trgNo).tenpo
5          .Range(“C2”).Value = myDic(trgNo).area
6          .Name = myDic(trgNo).tenpo
7      End With
8  End Sub
 
引数trgNo = ディクショナリのキー を受け取り対応するキーに対応する店舗no、店舗名(tenpo)、地域(area)をアクティブシート(コピーしたシートはアクティブになっている)のセルA2,B2,C2に入力。例えばキー2の場合noは2、tenpoは福島店、areaは東北がセルに入力される。
 
(5)ファイル複製
シートモジュールに書く。標準モジュールでもいいような気がするが。
 
1  Sub ファイル複製()
2      Application.ScreenUpdating = False

 

3      Dim path As String             ‘ファイルを保存するフォルダ
4      path = ThisWorkbook.path & “\”

5      Dim wsGen As Worksheet          ‘原本シートセット
6      Set wsGen = Worksheets(“原本”)

7      Me.getdataDic         ‘テーブルデータセット (3)のプロシージャ

8      Dim v As Variant
9      For Each v In myDic.Keys          ‘ディクショナリのキー(no)すべて処理
10          wsGen.Copy       ’原本シートコピー
11          Me.inputdataDic v            ‘データインプット((4)のプロシージャ)
12          With ActiveWorkbook            ‘ファイルを名前を付けて保存
13              .SaveAs Filename:=path & myDic(v).tenpo & “.xlsx”
14              .Close                  ‘ファイル閉じる
15          End With
16      Next

17      Application.ScreenUpdating = True
18  End Sub
 
 
 2・17:画面更新ストップ・解除
 4:複製したファイルを保存するフォルダの場所を変数pathに代入
5・6:原本シートをwsGenにセット
7 :(3)のプロシージャを呼び出し。TBシートのデータをディクショナリに代入
9 :For Eachでディクショナリのキーを一つずつ取り出して変数vに代入。10~16をディクショナリのキーの数だけループ処理
11 :(4)のプロシージャに変数vを渡して呼び出し。変数vはディクショナリのキー(店舗no)
12~14:アクティブなブック(原本シートをコピーして作成された新規ファイル)にキー値に対応する店舗名(例:v(キー)2の場合は「福島店」)をファイル名として保存。保存後新規ファイルを閉じる。
 
指定したフォルダ(ここではマクロファイルと同じフォルダ)に店舗のファイルが作成され、各ファイルのセルA2~C2に各店舗のデータ(店舗no、店舗名、地域)が入力されています。
f:id:mwke:20200530164743p:plain

マクロ実行後

 

 
※1:ディクショナリ利用時に参照設定をしない場合、ディクショナリの宣言部分とインスタンス作成部分を以下のようにします。
Dim myDic As Object 
Set myDic = CreateObject(“Scripting.Dictionary”) 
 

~シートモジュールについての余談~

これまでにシートモジュールを意図的に使ったことがあるのは、マクロでシートをコピーして別ファイルにするとき、いっしょにマクロもコピーしたかったとき。

ファイルのコピーだとマクロも一緒にコピーされるが、シートをコピーして新たなファイルとして保存すると、元ファイルの標準モジュールのマクロは新しいファイルについていかない。

マクロ元ファイルのシートをコピーし新たなファイルとして保存、その新たなファイルでボタンを押したら「指定の動き」をするマクロをつけたかった。

コピーされるシートモジュールに「指定の動き」のコードを書いておいたらそれもコピーされて一件落着、ということがあった(それに気が付くまでにかなり苦労した)

~余談終わり~

 
~余計な実験~
クラスのLetが3つあるのがわずらわしいのでLetをひとつにしてみた
クラス
Property Let gdata(ByVal vRow As Long)
    With WorkSheets(“TB”)
        no_ = .Cells(vRow, 1).Value
        tenpo_ = .Cells(vRow, 2).Value
        area_ = .Cells(vRow, 3).Value
    End With
End Property
シートモジュール
Set c = New cData
c.gdata = i
myDic.Add c.no, c
 
できることはできたがなんか違和感がおおありくい。
クラス内でシートを指定するのも違和感。Subの方がいいか
 
クラス
Sub getdata(ByVal ws As WorkSheet, ByVal vRow As Long)
    With ws
        no_ = .Cells(vRow, 1).Value
        tenpo_ = .Cells(vRow, 2).Value
        area_ = .Cells(vRow, 3).Value
    End With
End Sub

 

 

シートモジュール
Set c = New cData
c.gdata Me, i
myDic.Add c.no, c
自分自身のシートなので引数としてシートを渡すとき「Me」でOK
 
しかしLetとGetはセットで使った方がいいような気がするので、最初に戻る。
 
 

コメント

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