アクティブシートではない(開いていない)ワークシートを検索するには、どうしたらいいでしょう。ここでは下図のような表で考えてみます。
Sheet1のセルB3に入力した商品番号を、Sheet2のリストから検索して、見つかった単価をSheet1のセルC3に入力します。検索にはFindメソッドを使います。検索の対象範囲はSheet2のセル範囲A2:A21です。次のようなコードになりますね。
Sub Sample1() Dim FoundCell As Range Set FoundCell = Sheets("Sheet2").Range("A2:A21").Find(Range("B3")) If FoundCell Is Nothing Then Range("C3") = "Not Found" Else Range("C3") = FoundCell.Offset(0, 1) End If End Sub
さて、ここからが問題です。それでは、Sheet2のリストがオートフィルタで絞り込まれていたとしたらどうでしょう。
検索したい"A-008"は、Sheet2のリストに入力されているのですが、Findメソッドは非表示のセルを検索できないんです。
これは、ワークシート上で手動操作したときも同じです。
これは、Excelの検索機能における仕様です。もちろん、VBAもその仕様に従います。
では、非表示になっているかもしれない表から、セルを検索するにはどうしたらいいでしょう。検索の前に、検索の対象範囲(ここではSheet2)がオートフィルタによって絞り込まれているかどうかを確認しますか?それで、もしオートフィルタによる絞り込みがされていたら、絞り込みを解除してから検索をして・・・まぁ、とりあえず、やってみましょうか。
ちなみに、任意のワークシートにオートフィルタが設定されていて、かつ絞り込まれているかどうかを判定するやり方が、悩まずにすぐ書けますか?きっと多くの方がネットを検索するんでしょうね。検索しなくてもいいように、当サイト内のリンクを貼っておきます。
オートフィルタで絞り込まれているか判定する(ワークシート関数)
Sub Sample2() Dim FoundCell As Range With Sheets("Sheet2") If .AutoFilterMode Then ''オートフィルタが設定されていたら If .AutoFilter.FilterMode Then ''絞り込みがされていたら .Range("A1").AutoFilter ''オートフィルタを解除する End If End If Set FoundCell = .Range("A2:A21").Find(Range("B3")) If FoundCell Is Nothing Then Range("C3") = "Not Found" Else Range("C3") = FoundCell.Offset(0, 1) End If End With End Sub
オートフィルタが設定されると、AutoFilterModeプロパティがTrueを返します。しかしそれは、オートフィルタ矢印が表示されているかどうかです。オートフィルタによって絞り込みがされているかどうかは、AutoFilterオブジェクトのFilterModeプロパティで判定します。ただし、注意が必要です。FilterModeプロパティは、Excel 2007で追加されたプロパティですから、当然ですがExcel 2003まででは使えません。
それでも何とか工夫すれば、Excel 2003まででも絞り込みを判定することは可能です。ですが、待ってください。そんな苦労をしなくてもいいんです。セルの検索をFindメソッドだけに頼るから、どんどん難しくなっていくんです。Excelには、いろんな機能が実装されているのを思い出してください。
たとえば、ワークシート関数のVLOOKUP関数を使えば、この問題は解決です。
この関数をVBAから利用します。
Sub Sample3() On Error Resume Next Range("C3") = WorksheetFunction. _ VLookup(Range("B3"), Sheets("Sheet2").Range("A2:B21"), 2, False) If Err > 0 Then Range("C3") = "Not Found" End Sub
INDEX関数とMATCH関数を使う手もありますね。
Sub Sample4() Dim WF As Object On Error Resume Next Set WF = WorksheetFunction With Sheets("Sheet2") Range("C3") = WF.Index(.Range("B2:B21"), WF.Match(Range("B3"), .Range("A2:A21"), 0)) End With If Err > 0 Then Range("C3") = "Not Found" End Sub
WorksheetFunctionをオブジェクト変数WFに格納したのは、特に深い意味はありません。1行のコードが長すぎて、上の枠に入りきらないから短くしたかっただけです。
さらに、次のようなごく簡単なコードで実現することも可能です。
Sub Sample5() Dim i As Long, LastRow As Long With Sheets("Sheet2") LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row Range("C3") = "Not Found" For i = 2 To LastRow If Range("B3") = .Cells(i, 1) Then Range("C3") = .Cells(i, 2) Exit For End If Next i End With End Sub
数千件のデータだったら、一瞬で終わりますので、ぜひお試しください。上記のコードに難しいテクニックはありません。必要なのは、VBAの基礎と柔軟な発想だけです。