---topics:変数、最終行の取得---

VBA編其ノ6 罫線を引いてみる

今日のテーマは、差し当たり「罫線を引く」です。 ですがそれは、あくまでも表向きのハナシ。 本当のところ伝えたいのは、「変数の使い方」です。 変数、前回(其ノ5で)登場しましたね。 本日は、表に罫線を引く作業を通して、変数の使い方に慣れていってもらおうと思っています。
でも、その前に。

無限ループ

前回、ループ処理を扱いましたね。 その際、ひとつ気を付けてほしいのが、無限ループってヤツです。 無限ループ…、字面からなんとなく想像はつくと思います、が、とりあえず下↓のコードを見てください※1
あ! せっかちな人のために言っておきますが、この↓マクロはダメマクロなので、止まりません。 止めるには緊急脱出を使いますので注意(緊急脱出の仕方は下記参照)。

Dim i As Integer

i = 1

Do Until i = 3

Cells(1, i).Select

i = i + 1

Cells(1, i).Select

i = i - 1

Loop

1増えて、1減って…
※1 1増えて、1減って…

さて、動かす前に、ちょっと立ち止まって考えてみましょう。
このマクロ、i3 になったら止まる仕様になっています。 で、i1 からスタート。 最初の「i = i + 1」で i2 になるのですが、次の「i = i - 1」のところで i1 に戻っちゃうんですよ。 と、いうことは…、i永遠に 3 にならない…
なので、このマクロ、止まりません。 動かした際は、Escキーで無理矢理ストップさせましょう※2 ※3

2つのセルを延々と往復する
※2 2つのセルを延々と往復する
Esc→終了で脱出
※3 Esc→終了で脱出

マクロ実行中にEscキーを押すと上↑のアラートが出るので、そこで「終了」を選べばマクロから脱出できます。

…このマクロ、そもそも無意味なマクロ(A1セルとB1セルを往復するだけ)なのであまり参考にならないのですが、無限ループがどんなものかはわかったと思います。
無限ループって、Excelゲーム作成の時なんかだと使うこともあるみたいですけど、仕事でマクロ使う分には絶対避けたいところです。 なので、ループ処理を組むときには、こうならないよう気をつけましょう。 基本的には、処理の終わり(Do Until~の「Until~」のところ)をきちんと設定してあげれば、避けられるはずなので。 以上、注意喚起でした。

罫線を引く

ハイ、では。罫線を引く話に。
まず、お手数ですが、サンプルとなる表を作ってほしいんですよ。 下↓みたいな※4

罫線のない表のサンプル
※4 罫線のない表のサンプル

これからマクロで罫線を描くので、当然罫線はナシですよ。
で、このサンプル表の作成場所なんですけど…、いつもマクロを書いているブック(「マクロ練習.xls」)のシート1に作っても良いのですが、今回は、別で新たなブックを作ってその中に作りましょうか※5。 そうすれば、「マクロなしExcelでマクロを動かす」練習にもなりますしね。

別Excelにする
※5 別Excelにする

では、マクロの中身の話に。
まず、ちょっと想像してみましょう。 「この表に罫線を引く」マクロって、どんなイメージでしょうか? コードを見る前に少し考えてみてください。 …もちろん、「罫線を引く」という命令をまだ紹介していないので、そこはわからない。 だけど…、この表には範囲があって、そこにに罫線を引くわけだから、Rangeを使いそうだな、というような予測が立てられるような気がします。 つまり、「Range(Cells(1,1),Cells(6,4))」が目的語になるだろう、と。 だからあとは、「罫線を引く」という部分の書き方が判明すれば…、イケそうですよね※6

罫線の引き方さえわかれば…
※6 罫線の引き方さえわかれば…

なので、ためしに、1コのセルだけに罫線を引いてみましょう。 マクロを書くのは「マクロ練習.xls」の方に、ですよ。
こんな感じ↓になります※7

With Cells(8, 3)
.Borders(xlEdgeTop).LineStyle = xlContinuous
.Borders(xlEdgeLeft).LineStyle = xlContinuous
.Borders(xlEdgeRight).LineStyle = xlContinuous
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With

罫線引くだけなのに、コード、長いな…
※7 罫線引くだけなのに、コード、長いな…

たかが罫線引くだけなのに、コード長いなー。 …これね、4行あって、「Top」「Left」「Right」「Bottom」とあることからもわかるとおり、上下左右それぞれに罫線を引く、という命令を1コ1コ与えてるんですよ。 だから、こんなんなってしまうワケで…。
あ、そうそう。「.LineStyle = xlContinuous」の「xlContinuous」は、実線(破線じゃない、1本のちゃんとした直線)のことね。 「continuous」は「連続的」とか、そんな意味。 破線を入れたいときは、「xlContinuous」のところを「xlDash」「xlDot」「xlDashDot」などにしてみてください。 破線にはいくつか種類があるので、それにともなってLineStyleの値もいろいろあるんですよね。

では、動かしてみましょう。
サンプル表のExcelと、マクロの入っている「マクロ練習.xls」の2つを両方開いてください※8。 そして、サンプル表のExcelの方(これから罫線を入れたい側)をアクティブにします。

両方のExcelを開きます
※8 両方のExcelを開きます

で、サンプル表Excelの方で「開発」→「マクロ」を見ると…、さっき作ったマクロがいますよね※9

サンプル表の方をアクティブにして、「マクロ」を見ると…、いた
※9 サンプル表の方をアクティブにして、「マクロ」を見ると…、いた

あとは、コイツを動かしてやればOK。 こうなります※10

別エクセルでもマクロ動いた
※10 別エクセルでもマクロ動いた

お。罫線が引けました。
で、あれば、「Cells(8, 3)」のところを「Range~」にしてやれば…※11 ※12

rangeに書き換えた
※11 rangeに書き換えた
あれ?外枠しかない…
※12 あれ?外枠しかない…

あれ? 格子状にならないや…。

…大丈夫です。 考え方は間違っていないです。
ですが、範囲を格子状にするためには、4行じゃ足りないんですよ。 あと2行、インサイドに線を引く命令を追加してやらないといけない。 なので、下記↓のように手直ししてあげてください、お手数ですが※13

With Range(Cells(1, 1), Cells(6, 4))
.Borders(xlEdgeTop).LineStyle = xlContinuous
.Borders(xlEdgeLeft).LineStyle = xlContinuous
.Borders(xlEdgeRight).LineStyle = xlContinuous
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.Borders(xlInsideVertical).LineStyle = xlContinuous
.Borders(xlInsideHorizontal).LineStyle = xlContinuous
End With

insideにも線を引く命令を書いた
※13 insideにも線を引く命令を書いた

edgeは「端っこ」、insideは「内側」って意味ですもんね。 さっきは、insideに対する命令がなかったもんだから、内側に罫線が引かれなかったんですね。
ちなみに、verticalは「垂直方向」、horizontalは「水平方向」です。 僕の場合だと、verticalは「グラディウスⅢ」、horizontalはホライゾン(『ダッシュ!四駆郎』)のおかげで覚えられました。

では、さっき引いた罫線を消した上で、このマクロを動かしてみましょう※14

ちゃんと引けた
※14 ちゃんと引けた

罫線引けました。

…でもやっぱり、6行書くのはダルいですよね…。 上下左右別々に罫線を引ける、というのはそれはそれで便利な時もあると思いますが、 まとめて引きたいときにはめんどくさく感じると思います。 そういうときは、下↓の省略形、

Range(Cells(1, 1), Cells(6, 4)).Borders.LineStyle = xlContinuous

でもOKです。 この1行で、さきほどと同じ効果が現れます※15

こちらの方が手短だよね
※15 こちらの方が手短だよね

なので、ここから先はコイツを使います。 で、ここまでが実は話の前段。

行数が変わったときの対応

さて。ではここで、今引いた罫線を一度消して、1行レコードを追加してみましょうか※16。ためしに。

1行追加した
※16 1行追加した

たった1行追加しただけで、さっきのマクロが役に立たないことは、もうおわかりですよね※17

増えた分は罫線が引かれません。もちろん。
※17 増えた分は罫線が引かれません。もちろん。

たとえレコードが増減しても、表の範囲に合わせて罫線を引いてくれると助かるんですが…、では、どうしたらよいでしょうか。

 ・
 ・
ひとつは、下↓のマクロを1行書いて、で、自分で範囲を選択するやり方※18

Selection.Borders.LineStyle = xlContinuous

あらかじめ選択された範囲に罫線を引くマクロ
※18 あらかじめ選択された範囲に罫線を引くマクロ

こうすると、あらかじめ選択した範囲に、線を引くことができます※19 ※20

前もって範囲を選択しておくと…
※19 前もって範囲を選択しておくと…
そこに線が引かれる
※20 そこに線が引かれる

Selection」は「選択範囲」ぐらいの意味で、目的語として使えます。
…でも、このやり方だと、手作業で罫線引くのとほとんど変わらないんですよね。 もちろん、「Selection」は、これはこれで便利なので、頭の片隅に置いといてください。

で、実は、今日の本命はコチラ↓。 表の最終行を判断して罫線を引いてくれます※21

Dim i As Integer

i = Cells(Rows.Count, 1).End(xlUp).Row

Range(Cells(1, 1), Cells(i, 4)).Borders.LineStyle = xlContinuous

最終行を取得するバージョン
※21 最終行を取得するバージョン

もう1行追加したとしても、対応してくれます※22

行が増えても安心
※22 行が増えても安心

ハイ、では、解説。
罫線を引くときには範囲を指定してやるんですけど、Rangeを使うには左上のセルと右下のセルを入れてやらないといけない。 で、今回のケースだと、左上は(1, 1)って決まってる…って、僕は自分で表を作るときはきっちり左上から始めることにしているんだけど、人によっては(2, 2)から始める人もいて、何でよけいなアキを作るのか僕には理由がよくわからないんだけど、まあ、とにかく今回は(1, 1)から始まる、と。 で、あとは、右下のセルがどこにあるのかがわかれば、罫線を引くことができますよね。
だけど、この一番右下、ってのが、不確定要素なワケですよ。 表の行数の増減があるので。
差し当たり、一番右の列数は「4」固定を想定しているので、右下セルの列番号は「4」。 あとわからないのは一番右下のセルの行番号ですね。 なので、そこを変数 i とします※23

不確定要素を差し当たり i とする
※23 不確定要素を差し当たり i とする

変数の宣言(其ノ5参照)も忘れずに。
あとは、変数 i の中身ですね。
まず、Cells(Rows.Count, 1)の部分から見ていきましょう。 実はここは、ふつうのセル位置指定、Cells(1, 1)とかCells(6, 4)とかと同じです。 ただ、行番号が「Rows.Count」になっているだけで。
んで、「Rows.Count」の「Count」は「数」って意味ね。 で、「Rows」っていうのは「すべての行」の意味。 「Row」は英語で「行」という意味で、 VBAだと「Rows」はふつう、Rows(3)とかRows(5)のように、数を指定してやることで「3行目」「5行目」を指すワードになるんだけど、()かっこに何も指定しない「Rows」だと、「すべての行」の意味になります。 すべての行っていうのは、「Excelが限界まで使用できるすべての行」のことで、Excel2003だと65536行、2007以降だと1048576行、だっけかな? そこまですべて、ということになります。

()かっこを省略すると「すべて」になる、というのはCellsも(そして、Columnsも)同じで、いつもCells(4, 2)とか使っているコレの()かっこを付けずに「Cells」とすると、「すべてのセル」を指します。

なので、「Rows.Count」だと「すべての行の数」の数値をもってきてくれます。 だから、Cells(Rows.Count, 1)は、Cells(65536, 1)(ないし、Cells(1048576, 1))と同じことになります。 これはつまり、こういう↓ことね※24

Excel最下段 僕のExcelは2007だけど、2003形式のファイルなので限界は65536
※24 Excel最下段 僕のExcelは2007だけど、2003形式のファイルなので限界は65536

ここまではオケイ?
で、次の.End(xlUp)は、ctrl + ↑[上キー]と似たようなものだと思ってください。 つまり、情報が入っているセルにぶつかるまで上に進み、ぶつかるとそこで止まる、という感じです。 さっきのExcel最下段からctrl + ↑[上キー]を押すと、ココ、つまりテーブルの一番下に辿り着きますよね※25

Cells(Rows.Count, 1).End(xlUp)でココに辿り着く
※25 Cells(Rows.Count, 1).End(xlUp)でココに辿り着く

そして、最後の「Row」は行番号を返すワードです。 なので、Cells(Rows.Count, 1).End(xlUp)で辿り着いた先のセルの行番号、下画面で言うと「8」が、返されます※26
それが、変数 i に格納されるんですね。

Cells(Rows.Count, 1).End(xlUp).Row
※26 Cells(Rows.Count, 1).End(xlUp).Row

「Row」と「Rows」は別モノなので注意。 「Rows」の方は「行」という範囲を指すのに対して、今使った「Row」(sなし)の方は、「行番号」という数値(1とか2とか…)を返します、…と、一応は説明してみましたが、この辺りはおいおい慣れていってもらうとして。 ていうか、もうね、決まり文句として「Cells(Rows.Count, 1).End(xlUp).Row」覚えてください。

…けどね、一点注意しておくと、「Cells(Rows.Count, 1).End(xlUp).Row」は上述のような軌道で動いているので、こんなふう↓に途中にひっかかりがあると…、※27

途中に変なデータがあると…
※27 途中に変なデータがあると…

こんなふうに↓なっちゃう※28

そこまでが範囲として認識されちゃう
※28 そこまでが範囲として認識されちゃう

このような弱点はありますが、これは表の組み方を気を付けていれば、またCells(Rows.Count, 1).End(xlUp).Rowの挙動を理解していれば防げると思います。 Cells(Rows.Count, 1).End(xlUp).Rowは使えるので、 この機会に覚えてください。

あとね、もう1点。
Cells(Rows.Count, 1).End(xlUp).Rowの列番号は、何で「1」なんだろう?
範囲の右下のセルを見に行くのだから、Cells(Rows.Count, 4).End(xlUp).Rowでもよいのでは…※29

4列目じゃダメなの?
※29 4列目じゃダメなの?

お答えしますと、別に「4」でも良いんですよ、4列目が空白なくみっちり埋まっている、ということがわかっていれば
今回のサンプル表だと、4列目には「1」か「0」が必ず埋まっているみたいなので、問題はなさそうです。
しかし…。仮に、「4」でマクロを組んでみましょう※30

「1」→「4」にして組んでみた
※30 「1」→「4」にして組んでみた

それでね、もし仮に、4列目がアキのある列だったら…。
ためしに、サンプル表のデータを下↓のように変更して、今のマクロを動かしてみてください※31 ※32

4列目にアキがある、とすると…
※31 4列目にアキがある、とすると…
ま、こうなるようね
※32 ま、こうなるよね

End(xlUp)法は、上述のような挙動をするため、空白セルがところどころ存在するような列に対しては使えないんですよ。 だから、確実にデータがおしりまで埋まっている列に対して使いたい。
それでね、一番左の列だったら、きっちり埋まっているだろう、と…。
…え?そんなの確実かどうかわからない、って?
たしかにそうですね。 ただ、自分の場合は(あくまで僕個人の話なんですけど)、
・表は必ず左上[セル(1, 1)]から始める
・一番左の列と一番上の行[ヘッダー行]は必ずきっちり埋める
マイルールにしていますので…※33

マイルール
※33 マイルール

てゆーか、何で表を(2, 2)から始めたり、最左列にところどころアキがあったり、ヘッダー行がちゃんとしてないデータを作るんだ? フィルタかけるときとか、めんどくさくないのかな…。
愚痴っちゃいました。 スミマセン。 というわけで、End(xlUp)法は、各人の表の作り方を踏まえたうえで、アキのない列に対して使用するようにしてください。
以上、行数の増減に対応する方法の解説終わり。

列数の可変にも対応する

ここまで来たら、列数の増減にも対応したいですよね。 列数の変化に対応するには、変数をもう1コ増やして、こんなふう↓にします※34

Dim i As Integer
Dim j As Integer

i = Cells(Rows.Count, 1).End(xlUp).Row
j = Cells(1, Columns.Count).End(xlToLeft).Column

Range(Cells(1, 1), Cells(i, j)).Borders.LineStyle = xlContinuous

変数 j が増えた
※34 変数 j が増えた

変数 j が増えましたが、考え方はさっきのEnd(xlUp)法と同じです。 ただ今度は、いったん一番右の列に行って、左に戻ってくる、というだけで。
これで、たとえ列数が増えても※35、対応できます※36

1列増えたけど…
※35 1列増えたけど…
このマクロならOK
※36 このマクロならOK

いかがでしたでしょうか。 変数は、よくわからないもの、まだ情報が固まっていないもの、可変する可能性のあるものに対して使う、 ってことが、なんとなくわかっていただけましたかね。
あ、まだ「なんとなく」で良いですよ。徐々に、慣れていっていただければ。

Copyright(C)森田表計算