こんにちは!Accessでのフォーム開発、日々の試行錯誤お疲れ様です。
マクロの記録から一歩踏み出し、「自分で自由にVBAを操って、ユーザーが使いやすい爆速のシステムを作りたい!」そう思ってこのページにたどり着いたなら、大正解です。ここをクリアすれば、あなたのAccess VBAスキルは間違いなく一段階上のステージに到達しますよ。
今回は、実務の現場でめちゃくちゃよく使う、「非連結の検索フォームから、連結されたメインフォームの表示レコードを華麗にコントロールする技術」を解説します。
「検索キーワードを入れてボタンを押したら、パッと該当レコードにジャンプする」――このスマートなUI(ユーザーインターフェース)の裏側で何が起きているのか、その本質を一緒に紐解いていきましょう。
—
1. なぜ「RecordsetClone」と「Bookmark」が必要なのか?
まずは、私たちが目指すゴールと、その背景にあるAccessの仕組みを整理します。
Accessのフォームには、大きく分けて2つのタイプがあります。
1. 連結フォーム:テーブルやクエリと直接結びついており、データ編集やスクロールが自動でできて便利。ただし、複雑な検索条件をダイナミックに反映させるのはちょっと面倒。
2. 非連結フォーム:自由にテキストボックスやボタンを置けるが、データの保存やレコード移動の仕組みを自分でゼロから書かないといけないので大変。
「じゃあ、普段は便利な連結フォームをベースにしつつ、上部に『検索用テキストボックス』をポツンと置いて、そこで絞り込んだり該当レコードにジャンプできたら最高じゃない?」
これを実現するのが、`RecordsetClone` と `Bookmark` のコンビネーションです。
脳内イメージ図
[ 検索フォーム(非連結) ] ──(キーワード入力)──> [ 検索ボタン押下 ]
│
┌───────────────────────────────────────────────────────┴────────────────────────┐
│ Accessの裏側の世界 (Form) │
│ │
│ [ フォーム本体 (画面に映っているもの) ] │
│ ▲ │
│ │ (Bookmarkで同期) │
│ ▼ │
│ [ RecordsetClone (メモリ上のコピー / 影武者) ] ──(FindFirstで一致を探す)──> 🎯発見!│
└────────────────────────────────────────────────────────────────────────────────┘
フォームが裏側で持っているデータ(Recordset)の「影武者(Clone)」をメモリ上に呼び出し、影武者側で高速に検索をかけます。見つかったら、その場所(Bookmark)を本物のフォームに教えてあげることで、画面の表示を一瞬でジャンプさせる――これがこのテクニックの全貌です。
—
2. 実装のステップ:検索ボタンのコードを書く
百聞は一見に如かず。実際にコードを見てみましょう。
顧客ID(`CustomerID`)を入力して、該当する顧客のレコードへジャンプする検索ボタンのサンプルコードです。
Private Sub 検索実行ボタン_Click()
‘ エラーハンドラーの設置(プロの嗜みです)
On Error GoTo ErrorHandler
Dim rs As DAO.Recordset
Dim searchID As Long
‘ 1. 検索ボックスが空っぽだったら処理を抜ける
If IsNull(Me.txtSearchID) Then
MsgBox “検索する顧客IDを入力してください。”, vbExclamation, “入力エラー”
Me.txtSearchID.SetFocus
Exit Sub
End If
searchID = Me.txtSearchID.Value
‘ 2. フォームのRecordsetCloneをオブジェクト変数に代入する
‘ ※注意: Setを使わないとダメです!
Set rs = Me.RecordsetClone
‘ 3. 影武者(rs)の中で、該当するデータを探す (FindFirstメソッド)
‘ ※フィールドのデータ型に合わせて条件式の書き方に注意 (数値ならそのまま、文字列なら ‘ ‘ で囲む)
rs.FindFirst “CustomerID = ” & searchID
‘ 4. 見つかったかどうかを判定する (NoMatchプロパティ)
If rs.NoMatch Then
MsgBox “該当するデータは見つかりませんでした。”, vbInformation, “検索結果”
Else
‘ 5. 見つかった! フォームのBookmarkに、影武者のBookmarkを渡して同期させる
Me.Bookmark = rs.Bookmark
‘ 演出として、テキストボックスをクリアしてフォーカスを戻すなど
Me.txtSearchID.Value = Null
End If
CleanUp:
‘ 6. オブジェクト変数の解放(メモリリークを防ぐための鉄則)
If Not rs Is Nothing Then
rs.Close
Set rs = Nothing
End If
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Sub
—
3. コードの急所を徹底解説!ここが分かれば怖くない
初心者のうちは、「呪文のようにコピペして動いたからラッキー」で終わりがちですが、世界最高峰を目指すなら、一行ずつの意味を完全に咀嚼しておきましょう。
① `Set rs = Me.RecordsetClone`
`RecordsetClone` は、今まさに画面に表示されているレコードセットの「コピー(実体ではなく参照)」をメモリ上に作成します。画面の表示位置(カレントレコード)とは独立して動くため、画面をチラつかせずに裏側でこっそり検索を走らせることができます。
※オブジェクトなので、代入には必ず `Set` が必要です。
② `rs.FindFirst “CustomerID = ” & searchID`
DAOの `FindFirst` メソッドです。条件に一致する最初のレコードを探します。
- 数値型の場合:`”CustomerID = ” & searchID`
- 文字列型の場合:`”CustomerName = ‘” & Me.txtSearchName & “‘”` (シングルクォーテーションで囲むのを忘れないで!)
③ `Me.Bookmark = rs.Bookmark`
ここが一番の魔法の瞬間です。
`rs`(影武者)で見つけた場所の「しおり(Bookmark)」を、`Me.Bookmark`(本物のフォーム)にガチャンと挿入します。これによって、「あ、じゃあ本物のフォームも今すぐそこに移動します!」と画面が連動してスクロールします。
④ メモリの解放 (`Set rs = Nothing`)
VBAを書く上で絶対に忘れてはいけないのが「後片付け」です。DAOのレコードセットオブジェクトは、メモリを消費します。使い終わったら `.Close` し、変数に `Nothing` を代入してメモリをキレイに掃除する。これができるプログラマーは、コードの美しさだけでなく、システムの安定性も守ることができます。
—
4. 陥りがちな罠とエラー回避の知恵
現場でこのコードを実装すると、たまに意地悪なエラーに遭遇します。代表的なものを挙げておきますね。
- 罠1:そもそもフォームにレコードがない
フォームが空っぽの状態で検索をかけると、`RecordsetClone` がうまく機能しないことがあります。検索ボタンを押す前に `If Me.Recordset.RecordCount = 0` などのチェックを入れておくと鉄壁です。
- 罠2:フィルター(Filter)がかかっている状態での検索
もしフォームにあらかじめフィルターがかかっている場合、`RecordsetClone` にもそのフィルターが継承されます。「検索したのにヒットしない!」という時は、ベースとなるクエリやフィルターの条件を疑ってみてください。
—
まとめ
いかがでしたか?
`RecordsetClone` と `Bookmark` は、Access VBAの「オブジェクトモデル」を理解する上での最高の教材です。
「画面の裏側で影武者を動かし、見つけたらしおりを共有する」というイメージさえ頭に入っていれば、どんな複雑な検索フォームも怖くありません。
ここをクリアしたあなたなら、もうマクロの制限にイライラさせられることはありません。自由自在なUIで、ユーザーをアッと言わせる素晴らしいシステムを作り上げてくださいね。応援しています!
