名簿作成マクロのスタイル(1)
名簿作成マクロのスタイル(1)
Excelで名簿を扱うことが多い私。
これまで数多くの名簿を作成してきたなかで、最近だいぶスタイルが固まってきたので、一旦まとめておくことにした。
この先また考え方が変わるかも知れないが。
シートの役割に関するものはシートモジュールに書く
シートの役割を明確化する
そもそものExcelの使い方に関わる部分。多くの達人の皆さんが共通して〈シートごとに役割をハッキリさせよ〉的なことを述べておられると思う。
シートの役割をハッキリさせると、そのシートが請け負うべき機能もハッキリする。
その機能を実現するコードはそのシートのシートモジュールに書けば良いのだ。
たとえば、名簿作成の元になるデータを入れておくシートが必要だろう。
これは、〈1行1レコード・1列に1データ〉の原則に基づいて作るものだろう。
こんなふうに。
オブジェクト名をつける
私は、このような元データのシートには、
このように、オブジェクト名を「Sh01Data
」として、シート名を「Data
」とすることが多い。
オブジェクト名は、デフォルトだと「Sheet1
」となっているが、このまま使っていると、大規模なプロジェクトでシート数が9
を超えたときに、「プロジェクト エクスプローラー」上での並び順がイマイチになる。
このように実にうっとうしい並び順になってしまう。
さすがにシート数が99を超すことはないと思うので、シートモジュールのオブジェクト名は〈Sh
プラスゼロ埋め2桁〉を接頭辞にして命名するようにしている。
シートモジュールに書くコード
さて、では、このシートのシートモジュールには何を書くか。
このシートの役割は、
VBAにデータを提供する
ことである。それ以上でもそれ以下でもない。
その役割を考えたとき、大切なのは、、
データの位置が指し示しやすいこと
及び、、
データが取り出しやすいこと
であろう。
そのために、元データ用のシートモジュールには、だいたい次のコードを書くことが多い。
- 列番号を表す列挙体
- 表全体(項目ラベル含む)の
Range
オブジェクトを返すプロパティ - 表の正味のデータ部分(項目ラベルを含まない表全体)の
Range
オブジェクトを返すプロパティ
こいつら。
実際のコード
先に挙げた
を例に、実際にシートモジュールに書くコードをお目にかけよう。
列番号を表す列挙体
表では、
- 1列目:名前
- 2列目:名前ふりがな
- 3列目:所属
- 4列目:卒業期
- 5列目:級
- 6列目:班
- 7列目:戦法
- 8列目:失格
となっている。
そこで、シートモジュールの宣言セクションに次のように列挙体を定義する。
Public Enum Sh01ColumnName sh01Name = 1 sh01Phonetic sh01Prefecture sh01Generation sh01Grade sh01Class sh01Style sh01Unable End Enum
ポイントは、列挙体の各要素に「sh01
」という接頭辞をつけている点。
名簿作成作業の場合、元データから必要なデータだけを抽出して、表示用の別シートに転記する、という作業が重要になることが多い。
となると、転記先のシートでも同じように列番号を列挙体で定義する、という機会が生ずる。
そうなったときに、接頭辞をつけるようにすれば、同名被りをほとんど気にしなくてもよくなるのだ。
たとえば、Sh02View1
というシートオブジェクトの1列目に名前、2列目に所属、3列目に戦法を転記するような場合なら、Sh02View1
モジュールには、たとえば
Public Enum Sh02ColumnName sh02Name = 1 sh02Prefecture sh02Style End Enum
という列挙体を定義しておけばよい。
Sh01Data
オブジェクトのsh01Name
列目のデータはSh02View1
オブジェクトのsh02Name
列目に転記するということになり、実にわかりやすい。
表全体(項目ラベル含む)のRangeオブジェクトを返すプロパティ
元データからデータを抽出するときには、[Range].AdvancedFilter
メソッドを使うと便利。
[Range].AdvancedFilter
メソッドを使う際には、[Range]
オブジェクトが項目ラベルを持っていないとダメなので、表全体を手軽に取得できればコードが読みやすくなるだろう。
私は次のようなコードで表全体のRange
オブジェクトをシートオブジェクトのプロパティにして、取得しやすくしている。
Public Property Get WholeList() As Range Set WholeList = Me.Range("A1").CurrentRegion End Property
こうしておくことで、項目ラベルを含む表全体を、
Sh01Data.WholeList
という式で他モジュールから参照することができて便利。
表の正味のデータ部分(項目ラベルを含まない表全体)のRangeオブジェクトを返すプロパティ
項目ラベルを含む表全体とは別に、項目ラベルを除いた表の正味のデータ部分も必要。Range
オブジェクトの1
行目がデータの1行目になるから。
[Range].Value
プロパティの返り値をVariant
変数に突っ込んだときにできる2次元配列が1
始まりの配列になる挙動とも実に相性がよい。
そこで、次のようなプロパティを別途設定する。
Public Property Get DataList() As Range Dim ret As Range Set ret = Me.Range("A1").CurrentRegion If ret.Rows.Count = 1 Then Exit Property With ret Set ret = .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count) End With Set DataList = ret End Property
Offset
プロパティとResize
プロパティを用いて、正味のデータ部分を取り出している。
まあ、元データについてはユーザが触ることは基本ないので、正味の元データ部分に、たとえば「選手データリスト」とでも名前を定義しておいて、
'Declarations Section' Private Const RACER_DATA_LIST As String = "選手データリスト" 'Properties' Public Property Get DataList() As Range Set DataList = Me.Range(RACER_DATA_LIST) End Property
で十分だとは思う。もちろん、この場合、表の末尾にデータを追加した場合は範囲の名前定義を修正する必要があるが。
おわりに
シートの列番号と列挙体との相性はきわめてよいので、おすすめです。
つづく……かなあ???