ExcelVBAのマクロにおいて、処理の主役はもちろんセルですが、
このセルを処理するためには、どのシートのセルなのかを指定する必要があります。
この「ワークシートの指定方法」はいろいろなやり方があり、
どれがいいというわけではなく、一長一短があります。
ワークシートの指定は、ExcelVBAのコードおいて素地となる重要な要素ですので、
上手く書けるようになると、マクロが書きやすく、そして読みやすくなります。
「基本的だけど効果の高い方法」に厳選して解説していきますので、
是非習得していってください。
- 選択中のシート:ActiveSheet
- シートの名称で指定する:Worksheets("シート名")
- シートの並び順で指定する:Worksheets(シート番号)
- シートのオブジェクト名で指定する
- ワークシートを変数にSetする
- Withステートメントを使う
- おまけ:ワークシート処理専用のSubをもう1つ作る
- まとめ
選択中のシート:ActiveSheet
VBAを始めた方が、まず真っ先に使うのが、このActiveSheetです。
選択中、すなわちアクティブになっているシートですね。
ただしActiveSheetを使うと言っても、この書き方を使っているわけではなく、
Range("A1") = 1 Cells(1, 1) = 1
このように、セルの親シート指定を省略することで、
(半ば無意識に)ActiveSheetへの処理を書いています。
このActiveSheetの指定では、
- 選択していないといけない
- ユーザーが違うシートをアクティブにすると動かない
- 複数のシートを扱うことができない
と、書けるマクロに限界がありますので、この壁に当たって、
ActiveSheetを卒業するためにこのページを訪れた方も多いかもしれません。
当たり前のように書いてきて、意識していなかった方も多いかもしれませんので、
卒業するためにも、ここで一度しっかりと意識しておきましょう。
Cells(1, 1) = 1
このコードは、
ActiveSheet.Cells(1, 1) = 1
これを省略した書き方であり、この時ActiveSheetという指定を使っています。
シートの名称で指定する:Worksheets("シート名")
ここから本題に入っていきます。
まずは一番わかりやすい「シート名での指定」です。
Worksheets("売上データ").Range("A1") = 1 Worksheets("集計表").Cells(1, 1) = 1
このように、Worksheets()のカッコ内にシート名を記載することで、
その名前のシートを指定することができます。
この方法のメリットは、とにかく分かりやすいことです。
マクロを書く際に、
↑これと、
Worksheets("集計表").Range("A1") = Worksheets("売上データ").Cells(R, 1)
↑これを見比べながらマクロを書けるので、
シート指定をミスしにくい、マクロ作成者に優しい方法です。
ぱっと見でも「なんかデータを集計してるマクロだな」と分かるのがいいですね。
一方デメリットは、シート名を変えられると動かなくなることです。
この方法でシートを指定する場合は、ユーザーのシート名変更に注意してください。
シートの並び順で指定する:Worksheets(シート番号)
続いてワークシートの並んでいる順番で指定する方法です。
Worksheet(1).Range("A1") = 1 Worksheet(2).Cells(1, 1) = 1
このように、Worksheets()のカッコ内に、シートの番号を記載することで、
左から数えてその番号にあるシートを指定できます。
このシート番号のことを、WorksheetのIndex番号と呼びますので、
気が向いたら覚えてあげてください。
この方法の超重要なメリットは、ループ処理に使えるという点です。
' 2~5番目の4つのシートすべてに同じ処理 Dim シート番号 As Long For シート番号 = 2 To 5 Worksheets(シート番号).Range("A1") = 1 Next
と、シート番号を変数にして、たくさんのシートに同じ処理を行うことができます。
' 2~10番目の中で、偶数番のシートだけを処理 Dim シート番号 As Long For シート番号 = 2 To 10 Step 2 ' ←For文は増えていくカウントをStepで指定できる Worksheets(シート番号).Range("A1") = 1 Next
このように飛び飛びのシートをループしたりと、自在に組むことができます。
これはどんなに上達しても一生使うコードですので、きっちり習得しておきましょう。
対してデメリットが、
- シートの並び順が変わるとちがうシートを処理してしまう。
- どのシートを指しているのかコードだけではわからない
になります。
特に、違うシートを処理してしまうのは要注意です。
シート名指定のマクロで「シート名を変えちゃった」場合は、
せめてマクロはエラーで止まります。
しかし、シート番号指定で「シートの順番を変えちゃった」場合は、
下手するとエラーが出ないまま処理が進み、シートを破壊しかねません。
初心者の内は、エラーが出るのは嫌だと感じるかもしれませんが、
実は本当の恐怖は「エラーが出ないこと」です。
十分に注意してください。
この「読みづらい」「あぶない」の2大デメリットがあるので、
Worksheets(1).Range("A1") = Worksheets(2).Cells(R, 1)
と、ただベタ打ちの数字でこの指定方法を使うのはなるべくやめましょう。
確かに書きやすいので、即席マクロづくりに便利ではあるのですが、
長く使うマクロでこれをやるのは、後で読み返すのが困難な上に危険です。
ご利用は計画的に。
シート番号に限らず、プログラミングにおいて「書きやすさ」と「読みやすさ」がトレードオフだったときは、できるだけ「読みやすさ」の方を取るようにしましょう。
プログラムは書く時間よりも読む時間や書き換える時間の方が長く、
そして苦戦しますからね。
✔ 小ネタ:シート番号の変数名に「i」はダメ
この「インデックス番号のループ処理」を用いるコードで、
Dim i As Long For i = 2 To 5 Worksheets(i).Range("A1") = 1 Next
と、変数名に「i」を使っているコードをよく見かけますが、
これはなるべくやめた方が良いです。
理由は単純で、
' 2~5番目のシートの、A1~A4セルを処理 Dim i As Long For i = 2 To 5 For i = 1 To 4 Worksheets(i).Cells(i, 1) = 1 ' ←これはダメ。シートもセルもiを使おうとしてダブってる Next Next
と、あとあと「同じ名前の変数は1個まで」という決まりに困ることになるからです。
ワークシートは「何かの親オブジェクト」として登場することが多く、
「シートを○○するマクロ」
ではなく、
「シート内の△△を○○するマクロ」
を作ることが多いです。
△△は当然セルが多いですね。
この時、親側で一番使いやすい「i」を使ってしまうと、
もっとたくさん処理を書かなければいけないセルなどに「i」が使えないのです。
一番ありがちなやらかしとしては、
「今まで作ったコード同士をコピペ合体できない。゚(゚´ω`゚)゚。」
なんて事態を往々にして招きます。
そんでもって、シートを「i」、行を「j」、列を「k」みたいに、
今まで書いたコードの「置換ができない1文字カウンタ」を、
ひとつひとつ手で書き換えてツギハギ対応していくと、
たぶん、いつか崩壊します(笑)
For シート番号 = 2 To 5 For R = 1 To 4 For C = 2 To 3 Worksheets(シート番号).Cells(R, C) = 1 Next Next Next
このように、シート・行・列それぞれのカウンタを分かりやすい変数名にしておけば、
変数の取り違えも減らせますし、コピペ合体もとても楽になります。
なにより読みやすいですしね!
変数名にiを使うのは、真っ黒な画面で、メモリの中でガチャガチャやる、
普通の?プログラミング言語の文化です。
ワークシートやセルなど、目に見えているものを扱うExcelVBAでは、
その見えているものの名前をそのまま変数に使っちゃいましょう。
「カウンタ変数は、何のカウンタかわかるように命名する」と、
読みやすいコードが書けますよ。
シートのオブジェクト名で指定する
さてWorksheets("シート名")、Worksheets(シート番号)と来て、
三大シート指定方法の残りひとつが「シートのオブジェクト名」です。
簡単に言うと、
「プログラマ専用のシート名をつけることができる機能」を利用します。
VBE(マクロを書くところ)を見渡してみてください。
こんな場所がありますよね。
この「Sheet1 (集計表)」や「Sheet2 (売上データ)」という部分こそ、
シートのオブジェクト名が書いてあるところです。
- Sheet2 がシートのオブジェクト名
- 売上データ が実際のシート名
となります。
さて、このオブジェクト名であるSheet2は、自由に変更ができます。
↑ここをクリックすると出てくる、
↑ここの(オブジェクト名)の部分を書き換えることで変更します。
分かりやすい様に同じ名前にしてみました↓
この設定において、実際にマクロを書く際は、
売上データ.Range("A1") = 1 売上データ.Cells(1, 1) = 1 集計表.Range("A1") = 売上データ.Cells(R, 1)
と、このオブジェクト名をそのまま使ってWorksheetを指定することができるようになります。
短い上に超分かりやすいですね!
この機能はメリット盛りだくさんで、特に、
- シートの指定が短く明確
- シート名が変更されても大丈夫
が強力です。
このシートのオブジェクト名を使えるようになると、
とても分かりやすいコードを、スピーディーに書けるようになります。
長く使うようなマクロは、シートのオブジェクト名をきっちり設定しておきましょう。
ちなみにこのオブジェクト名ですが、ちょっとカッコつけて、
↑こんな風にオブジェクト名の頭文字に「WS」をつけると、
WS集計表.Range("A1") = WS売上データ.Cells(R, 1)
と、どのシートを指しているかを分かりやすく保ちつつ、
この変数が「Worksheetオブジェクト」であることを明確にできます。
また、入力する際に、wsまで打って「Ctrl+Space」を押すと、
↑このように、頭文字をそろえた効果で入力予測の選択肢から入力できるようになります。
この選択肢は、
↑これと候補が一致しますからね。
取り違えも起きづらく、読みやすく、書きやすいという、
最強のシート指定方法となります。
この命名法も、是非使ってみて下さい。
✔ シート指定を短縮する方法を考える
さて、ワークシートを指定する方法は以上の3つとなります。
- Worksheets(シート名)
- Worksheets(シート番号)
- シートのオブジェクト名
でしたね。
では早速、これでコードを書いてみましょう。
Worksheets("売上データ").Cells(R, 1) = ○○ ' 商品名 Worksheets("売上データ").Cells(R, 2) = ○○ ' 価格 Worksheets("売上データ").Cells(R, 3) = ○○ ' 個数 Worksheets("売上データ").Cells(R, 4) = Worksheets("売上データ").Cells(R, 2) * Worksheets("売上データ").Cells(R, 3) ' 金額
うーんしつこい(笑)
何度も何度も「売上データ」とうるさいですね。
シートのオブジェクト名を使うと、これよりは多少マシになりますが、
それでもしつこいことに変わりはありません。
ということで、シートを指定できたら、
その次は指定したシートを短縮で呼び出す方法を考えなければいけません。
↑のコードのしつこさを嫌って、
ActiveSheetに戻ってしまっては元の木阿弥ですからね。
その方法を3つ紹介していきます。
ワークシートを変数にSetする
上記のコードを、変数を使って以下のように書き替えます。
Dim ws As Worksheet Set ws = Worksheets("売上データ") ws.Cells(R, 1) = ○○ ' 商品名 ws.Cells(R, 2) = ○○ ' 価格 ws.Cells(R, 3) = ○○ ' 個数 ws.Cells(R, 4) = ws.Cells(R, 2) * ws.Cells(R, 3) ' 金額
素晴らしく見やすくなりましたね!
変数「ws」をWorksheet用の変数として宣言(Dim)し、
Worksheets("売上データ")を、その変数にSetするコードです。
まず「このシートに処理をするよ!」と宣言をして、
そのあとで、処理を書いていくイメージのコードに出来ますね。
この変数へのSetは、オブジェクト名に対しても同じように使えます。
Set ws = WS売上データ
変数のいいところは、何個も作れて、名前も好きに付けられることです。
例えば2つのシートを扱う
Worksheets("集計表").Range("A1") = Worksheets("売上データ").Cells(R, 1) Worksheets("集計表").Range("B1") = Worksheets("売上データ").Cells(R, 2) Worksheets("集計表").Range("C1") = Worksheets("売上データ").Cells(R, 3) Worksheets("集計表").Range("D1") = Worksheets("売上データ").Cells(R, 4) Worksheets("集計表").Range("E1") = Worksheets("売上データ").Cells(R, 5)
↑このようなコードがあったときに、これを、
Dim ws取込 As Worksheet Set ws取込 = Worksheets("売上データ") Dim ws出力 As Worksheet Set ws出力 = Worksheets("集計表") ws出力.Range("A1") = ws取込.Cells(R, 1) ws出力.Range("B1") = ws取込.Cells(R, 2) ws出力.Range("C1") = ws取込.Cells(R, 3) ws出力.Range("D1") = ws取込.Cells(R, 4) ws出力.Range("E1") = ws取込.Cells(R, 5)
↑こんな風に変数を使って書きかえることができます。
指定したシートを「マクロの処理上での役割で名付ける」ことができましたね!
取込→出力というデータの流れが非常にわかりやすくなりますし、
ws取込.Range("E1") = ws出力.Cells(R, 5)
↑これを見て「あれ?バグじゃね?」と思えるのも大事なメリットです。
ワークシートに限らず、注目しているオブジェクトを変数にSetして扱う手法はとても便利です。
Worksheetはオブジェクト変数に慣れるとてもいい題材だと思いますので、
是非身につけていってください。
Withステートメントを使う
元コードを再掲します。
Worksheets("売上データ").Cells(R, 1) = ○○ ' 商品名 Worksheets("売上データ").Cells(R, 2) = ○○ ' 価格 Worksheets("売上データ").Cells(R, 3) = ○○ ' 個数 Worksheets("売上データ").Cells(R, 4) = Worksheets("売上データ").Cells(R, 2) * Worksheets("売上データ").Cells(R, 3) ' 金額
このコードは、Withステートメントを使って、↓のように書き替えることができます。
With Worksheets("売上データ") .Cells(R, 1) = ○○ ' 商品名 .Cells(R, 2) = ○○ ' 価格 .Cells(R, 3) = ○○ ' 個数 .Cells(R, 4) = .Cells(R, 2) * .Cells(R, 3) ' 金額 End With
こちらもすごくわかりやすいですね。
Withステートメント「 With ○○ ~ End With 」に囲まれたエリアでは、
「.」1文字が○○を表すようになります。
↑のコードでは、「.」が「Worksheets("売上データ").」を表しています。
変数よりさらに短くなるので、見やすさで言ったら一番ですね!
Withステートメントは、Worksheetと非常に相性が良いので、
じゃんじゃん使っていきましょう。
ただし、デメリットは1つしか短縮できないことです。
2つのシートを扱う場合は、
With Worksheets("集計表") .Range("A1") = Worksheets("売上データ").Cells(R, 1) .Range("B1") = Worksheets("売上データ").Cells(R, 2) .Range("C1") = Worksheets("売上データ").Cells(R, 3) .Range("D1") = Worksheets("売上データ").Cells(R, 4) .Range("E1") = Worksheets("売上データ").Cells(R, 5) End With
このように片方だけをWithすることしかできません。
これでもかなり見やすいのは間違いないのですが、
もっと長いマクロだと「どっちがWithだっけ?」問題が出てきます。
ご利用は計画的に。
おまけ:ワークシート処理専用のSubをもう1つ作る
最後の方法は難易度が上がりますので、
いきなり完璧に理解しようとしないでください。
なんとなく見ておくだけでも、いつか必ず役に立つと思いますので、
適当に流し読みしてみてください。
例えば上記のコードが、こんなマクロの一部分だったとします。
Sub 長い処理を行うマクロサンプル ~~下準備などの処理 ' 売上データへの登録部分 Dim ws As Worksheet Set ws = Worksheets("売上データ") ws.Cells(R, 1) = ○○ ' 商品名 ws.Cells(R, 2) = ○○ ' 価格 ws.Cells(R, 3) = ○○ ' 個数 ws.Cells(R, 4) = ws.Cells(R, 2) * ws.Cells(R, 3) ' 金額 ~~この後もマクロが続きます End Sub
長いマクロのある一部分の処理だった場合ですね。
変数に入れているのでこの部分だけならわかりやすいですが、
他の部分で使った変数と取り違えたりするのが怖いですし、
長大なマクロだとここを探すのも一苦労だったりします。
こういった長いマクロの中で、対象シートを処理する部分が決まっている場合は、
↓のようなコードに書き換えることができます。
Sub 長い処理を行うマクロサンプル ~~下準備などの処理 Call 売上データに登録する(Worksheets("売上データ")) ~~この後もマクロが続きます End Sub Sub 売上データに登録する(ws As Worksheet) ws.Cells(R, 1) = ○○ ' 商品名 ws.Cells(R, 2) = ○○ ' 価格 ws.Cells(R, 3) = ○○ ' 個数 ws.Cells(R, 4) = ws.Cells(R, 2) * ws.Cells(R, 3) ' 金額 End Sub
Subが2つに増えており、2つ目のSubを1つめのSubで呼出(Call)していますね。
- 1つめのSubが処理全体を行う、いわゆる「マクロの本体」
- 2つめのSubが「売上データシートへの処理の詳細部分」
です。
Sub1つでやるパターンと見比べるとわかりますが、
第2のSub内のコードは変わっていません。
変数wsを用意する部分(Dim)がなくなって、代わりに第2のSubのタイトル行に、
Sub 売上データに登録する(ws As Worksheet)
と、カッコの中に変数宣言っぽい部分が増えています。
このwsをSubの「引数」とよび、
「売上データに登録する」というSubに変数を渡す仕組みです。
実際の本体マクロ(第1のSub)を見ると、
Call 売上データに登録する(Worksheets("売上データ"))
このように、カッコの中身(=ws)にWorksheets("売上データ")を渡していますね。
マクロを作るときに使うSubのことを正確には「Subプロシージャ」と呼び、
今回のようにSubを複数使うことを「プロシージャ分割」と呼びます。
このプロシージャ分割を行う目的は、
Sub 長い処理を行うマクロサンプル ~~下準備などの処理 Call 売上データに登録する(Worksheets("売上データ")) ~~この後もマクロが続きます End Sub
このように長い処理を本体マクロ上でたった1行にすることです。
マクロが複雑になってくると、例えばFor~NextとかIf~EndIfとかが口を大きく開けたカバさんみたいに広がって、何をやってるのかわけがわからなくなることがあります。
この時、カバさんの口の中だけを別のSubプロシージャにすることで、
メインコードを極端に短くすることができます。
プロシージャ分割というと難しい印象を受けますし、実際抽象的な話になるので、
「よし学ぼう!」と思っても、いい題材が無いとかなり難しい勉強を強いられます。
しかし、「シートの処理部分のパッケージ化」みたいな簡単な事例があると、
ほぼ同じコードの書き替えで作れてしまうため、案外とっつきやすいかもしれません。
今いきなり始める必要はありませんが、今日の話を心の片隅に置いておくと、
将来プロシージャ分割に取り組む時の糧になると思います。
いつかでっかいカバさんに困った時に思い出してみてください。
まとめ
以上でWorksheetの指定方法の解説を終わります。
まずは「シートを指定する方法」として、
- ActiveSheet
- Worksheets("シート名")
- Worksheets(シート番号)
- シートのオブジェクト名
を紹介しました。
そして、指定できたシートを短縮で記述するための、
- 変数にSetする
- Withでくくる
- 専用プロシージャに分離する
を紹介しました。
WorksheetはExcelそのものといってもいいオブジェクトです。
ExcelVBAをやる上で、一生の付き合いになります。
仲良く二人三脚で、VBAライフを歩んでいきましょう。