VBA技術解説
RangeオブジェクトのFor EachとAreasについて

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
最終更新日:2021-04-08

RangeオブジェクトのFor EachとAreasについて


マクロ VBA Range Areas For Each


【VBA問題】
?Range("A1:B3,C2:E5").Columns.Count
さて結果は?

  • 2
  • 3
  • 5
  • にゃんともいえない


ツイッターでこのような出題をしました。
この出題にたいする解説記事になります。



正解は

2

RangeオブジェクトにはAreasがあります。
1つの矩形範囲が1つのArea(というのは無いが説明の便宜上)で、これが複数集まってAreasを形成しています。

Range("A1:B3,C2:E5")
この範囲は、以下の画像の選択範囲になります。

マクロ VBA Range Areas For Each

A1:B3
C2:E5
これらの1つ1つの範囲がAreaであり、2つのArea(Areas)でRangeオブジェクトが形成させています。

そこで、
Rangeオブジェクト.Columns.Count
これがなぜ2になるのかというと、既にお分かりだとは思いますが、
先の記述では、
Rangeオブジェクト.Areas(1)
このAreas(1)が省略されていることになります。

Areas(1)が"A1:B3"、Areas(2)が"C2:E5"になります。
つまり、
?Range("A1:B3,C2:E5").Areas(1).Columns.Count → 2
?Range("A1:B3,C2:E5").Areas(2).Columns.Count → 3


RangeオブジェクトをFor Eachで取得すると

Range("A1:B3,C2:E5")
このRangeオブジェクトをFor Eachで処理した場合にどうなるでしょうか。


Range("A1:B3,C2:E5")

Dim myRng As Range, rng As Range
Set myRng = Range("A1:B3,C2:E5")
For Each rng In myRng
  Debug.Print rng.Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

RangeのFor Eachでは、Areasの順に、その中で先に横方向に動きます。
そして、結果は予想通りに指定された範囲のセルが順に処理されていますね。

.Cellsを付けて全体を指定しても同じことになります。



For Each rng In myRng.Cells
  Debug.Print rng.Address(False, False)
Next

Cellsプロパティは、Rangeオブジェクトの全体なので、上のmyRngだけ指定した場合と同様になります。

以下、For Eachの部分のコードのみ掲載します。


Range("A1:B3,C2:E5").Rows

For Each rng In myRng.Rows
  Debug.Print rng.Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

"A1:B3,C2:E5"この2つの範囲を順に行単位で取得しています。


Range("A1:B3,C2:E5").Columns

For Each rng In myRng.Columns
  Debug.Print rng.Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

"A1:B3,C2:E5"この2つの範囲を順に列単位で取得しています。


Range("A1:B3,C2:E5").Areas



For Each rng In myRng.Areas
  Debug.Print rng.Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

Areas(1),Areas(2)の順に取得しています。


RangeオブジェクトをItemで取得すると

Range("A1:B3,C2:E5")
このRangeオブジェクトを1~CountまでItemで処理した場合にどうなるでしょうか。


Range("A1:B3,C2:E5").Item(i)

Dim myRng As Range, rng As Range, i As Long
Set myRng = Range("A1:B3,C2:E5")
For i = 1 To myRng.Count
  Debug.Print myRng.Item(i).Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

何やらおかしいですね。
"A1:B9"の範囲が取得されています。
myRng.Item(i)
これは、Areas(1)が省略されている状態で、
myRng.Areas(1).Item(i)
つまり、
"A1:B3"
このRangeオブジェクトの範囲が拡張されてしまっています。
RangeのItemでは、その範囲を超えてもエラーにならずに、範囲が縦に拡張されて取得されます。
(もちろん、シート範囲を超えてしまう場合はエラーになります。)


Range("A1:B3,C2:E5").Columns.Item(i)

最初の問題のように、
myRng.Columns.Count
これは2になってしまうので、以下では1~5で実行しました。

For i = 1 To 5
  Debug.Print myRng.Columns.Item(i).Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

やはりおかしいですね。
myRng.Columns.Item(i)
これは、Areas(1)が省略された状態になってしまいます。
myRng.Areas(1).Columns.Item(i)
つまり、
Areas(1)である"A1:B3"
このRangeオブジェクトの範囲が(Columnsなので横に)拡張されてしまっています。

"C2:E5"はAreas(2)になるので、
myRng.Areas(2).Columns.Item(i)
このように取得する必要があります。

したがって、Areasが複数ある場合にColumnsを使うなら、AreasをFor Eachで処理して、そのループの中でColumnsを使うと良いでしょう。


Range("A1:B3,C2:E5").Rows.Item(i)

myRng.Rows.Count
これは3になってしまうので、以下では1~5で実行しました。

For i = 1 To 5
  Debug.Print myRng.Rows.Item(i).Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

Columns同様におかしいですね。
myRng.Rows.Item(i)
これは、Areas(1)が省略された状態になってしまいます。
myRng.Areas(1).Rows.Item(i)
つまり、
Areas(1)である"A1:B3"
このRangeオブジェクトの範囲が(Rowsなので縦に)拡張されてしまっています。


Range("A1:B3,C2:E5").Areas.Item(i)

For i = 1 To myRng.Areas.Count
  Debug.Print myRng.Areas.Item(i).Address(False, False)
Next

マクロ VBA Range Areas For Each

マクロ VBA Range Areas For Each

Areasはインデックスの範囲を超えて指定することは出来ません。
今回の場合であれば、
myRng.Areas.Countの2を超えて3以上を指定した場合はエラーになります。

マクロ VBA Range Areas For Each


RangeオブジェクトのFor EachとAreasの最後に

Areasが複数あるRangeオブジェクトを扱うのは結構面倒な場合がでてきます。
頻繁に扱う事は無いと思いますが、
手動で範囲選択したRangeオブジェクトや、VBAでUnionしたRangeオブジェクトの場合はAreasに注意が必要になります。

そのような場合は、For EachでAreasを処理して、そのループの中に本来の処理を組み込むようにすると良いでしょう。




同じテーマ「マクロVBA技術解説」の記事

VBAでWMIの使い方について
アクティブシート以外のWindowを設定できるWorksheetView
LSetとユーザー定義型のコピー(100桁の足し算)
省略可能なVariant引数の参照不可をラップ関数で利用
ブックのいろいろな開き方(GetObject,参照設定,アドイン)
入力規則への貼り付けを禁止する
Select Caseでの短絡評価(ショートサーキット)の使い方
RangeオブジェクトのFor EachとAreasについて
画像が行列削除についてこない場合の対処
新関数SORTBYをVBAで利用するラップ関数を作成
LAMBDA以降の新関数はVBAで使えるか


新着記事NEW ・・・新着記事一覧を見る

数字(1~50)を丸付き数字に変換するVBA|VBA技術解説(2022-11-15)
TEXTAFTER関数(テキストの指定文字列より後ろの部分を返す)|エクセル入門(2022-11-14)
TEXTBEFORE関数(テキストの指定文字列より前の部分を返す)|エクセル入門(2022-11-14)
TEXTSPLIT関数(列と行の区切り記号で文字列を分割)|エクセル入門(2022-11-12)
LAMBDA以降の新関数はVBAで使えるか|VBA技術解説(2022-11-11)
WRAPCOLS関数(1次元配列を指定数の列で折り返す)|エクセル入門(2022-11-08)
WRAPROWS関数(1次元配列を指定数の行で折り返す)|エクセル入門(2022-11-08)
EXPAND関数(配列を指定された行と列に拡張する)|エクセル入門(2022-11-07)
TAKE関数(配列の先頭/末尾から指定行/列数を取得)|エクセル入門(2022-11-06)
DROP関数(配列の先頭/末尾から指定行/列数を除外)|エクセル入門(2022-11-06)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.繰り返し処理(For Next)|VBA入門
5.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
6.Excelショートカットキー一覧|Excelリファレンス
7.並べ替え(Sort)|VBA入門
8.エクセルVBAでのシート指定方法|VBA技術解説
9.マクロって何?VBAって何?|VBA入門
10.ExcelマクロVBAの基礎を学習する方法|エクセルの神髄




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ