For Each xCell In Selectionの落とし穴

エクセルでマクロの書き方が分からない場合は、マクロ記録でコードを生成させて参考にするのが一番簡単です。ところが、そのコードはそのままでは使えない代物だということが分かってますか? 今回は、Selectionの落とし穴について書いてみたいと思います。

 

Selectionオブジェクトは個人の責任で

選択されているセルに対してVBAで何かしら処理を記述したいというケースはたまにあります。エクセルに不慣れなユーザーが対象という場合はお勧めしませんが、個人で使うツール程度ならば別に問題ないでしょう。

 

Selectionオブジェクトの落とし穴1: アクティブシートじゃないと実行時エラー 

新規ワークブックを作って、Selectionの内容を新規ワークブックに転記するようなVBAを書くとしましょう。以下のコードでは、Selectionは新規ワークブック上の選択されたセルになってしまいます。これでは困りますよね。

 

Dim xBook As Excel.Workbook
Dim xSheet As Excel.Worksheet
Dim xCell As Excel.Range

Set xBook = Application.Workbooks.Add
For Each xCell In Selection
    ........
Next

 

じゃあ、この問題を回避するにはどうしたらよいでしょうか。それは、前もってSelectionの内容をコピーしておくだけです。簡単ですね。

Dim xRange As Excel.Range
Set xRange = Selection
Set xBook = Application.Workbooks.Add
For Each xCell In xRange
    ........
Next
 

Selectionオブジェクトの落とし穴2: 非表示のセルも処理対象

Selectionの中に非表示の行または列があったとします。下の例では102-104行が非表示になっています。非表示部分は除外して処理したいのですが、For Each文でSelectionを対象としたループでは、非表示セルも含まれてしまいます。

 

f:id:gungnir46:20191106221201p:plain

For Each xCell in Selection
    If xCell.Font.Color = vbBlue Then
        iCount = iCount + 1
    End if
Next

では、どうしたらこの問題を回避できるでしょうか。答えは簡単、RowHeightを見ればいいんです。

For Each xCell in Selection
    If xCell.RowHeight > 0 And xCell.Font.Color = vbBlue Then
        iCount = iCount + 1
    End if
Next

 

Selectionオブジェクトの落とし穴3: ループするセルの順番は左→右

上から下にループしてくれると思っていたら、それは間違いです。左から右にループします。セルの順番に依存するようなコードは書けないと思っておいた方がよいでしょう。

 

結論

「現在のワークシート上の選択されたセル」のように前提条件がいくつもあるようなオブジェクトに依存するようなコードは書かないようにすべきです。ただ、残念ながらActiveSheetやActiveWorkbookといったオブジェクトを使わざるを得ない場合もあります。その場合は、ローカル変数にバックアップコピーして、ActiveSheetやActiveWorkbookの参照は1回だけにすべきです。