【入門編】DAOのRecordsetCloneとブックマーク:サブフォームとの同期を完璧に制御する – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

マクロの記録を卒業する第一歩:DAOの「ブックマーク」でメインとサブフォームを完璧に同期させる極意

こんにちは。Access開発の世界へようこそ。

「マクロの記録」を使って、ボタンを押したら次のレコードへ行く、といった操作は作れるようになりましたか? それができれば、あなたはすでに自動化の入り口に立っています。でも、実務で「使える」システムを作ろうとすると、必ずと言っていいほど一つの壁にぶち当たります。

それは、「メインフォームを操作したとき、連動しているサブフォームの表示も、寸分狂わず同じレコードに合わせたい」というUI(ユーザーインターフェース)の制御です。

「サブフォームのレコードを動かしたら、メインフォームまで勝手に動いてしまって制御不能になった」
「メインフォームで特定のデータを探したのに、サブフォームが前のデータのまま止まっている」

もしあなたがそんな悩みを抱えているなら、それはあなたが「データの所在(ポインタ)」という概念に触れる準備ができた証拠です。今日は、プロの現場でも使われる「DAOのRecordsetCloneとブックマーク」という技術を使って、この問題をエレガントに解決する方法を伝授します。

ここをクリアすれば、Access VBAの基本はバッチリですよ。さあ、一緒に見ていきましょう。

1. なぜ「レコード番号」で同期してはいけないのか?

多くの初学者が陥る罠があります。それは、「メインフォームのレコード番号が5番だから、サブフォームも5番に移動させる」という考え方です。

' ❌ やってはいけない、脆いコードの例
Me.SubformControl.Form.Recordset.AbsolutePosition = Me.Recordset.AbsolutePosition

これは一見正しく見えますが、非常に危険です。なぜなら、「表示されている順番」と「データの並び順」は必ずしも一致しないからです。フィルタをかけたり、ソート(並び替え)を行ったりした瞬間に、このコードは崩壊します。

私たちが使うべきなのは、番号(Position)ではなく、「ブックマーク(Bookmark)」です。

ブックマークとは何か?(概念図)

ブックマークを理解するために、本棚を想像してください。

  • Recordset(レコードセット): 本棚全体(データの塊)
  • AbsolutePosition(絶対位置): 「左から5番目の本」という指定
  • Bookmark(ブックマーク): 「この本に付いている、目印の付箋」

「左から5番目」という指定は、誰かが本を抜き取ったり順番を入れ替えたりすると意味がなくなります。しかし、「この付箋がついた本」という指定(ブックマーク)は、本がどこに移動しても、その本自体を確実に指し示すことができます。

2. 武器となる「RecordsetClone」の正体

同期を実現するために、もう一つ重要な概念があります。それが `RecordsetClone` です。

通常、フォームが表示されているとき、そのフォームは自分自身のデータを表示するために「Recordset」というオブジェクトを掴んでいます。しかし、VBAでその「表示中のレコードセット」を直接いじくり回すと、フォームの動きと干渉してエラーや予期せぬ挙動を引き起こすことがあります。

そこで登場するのが `RecordsetClone` です。これは、「現在フォームが表示しているデータと同じ内容を持った、目に見えないコピー(分身)」です。

1. 本体(Recordset): ユーザーの目に触れる、実際に動いているレコード。
2. 分身(RecordsetClone): プログラムが裏側で「今、どのレコードにいるかな?」と確認するためだけに使う、静かなコピー。

この「分身」を使って、現在の位置(ブックマーク)を特定し、それを「本体」に伝える。これがプロの同期テクニックです。

3. 実践:メインとサブフォームを同期させる完全なコード

では、具体的な実装を見ていきましょう。
シナリオは、「メインフォームのレコードを移動させたら、それに対応するサブフォームのレコードも、自動的に同じ位置へジャンプさせる」というものです。

※ここでは、メインフォームとサブフォームが、ある共通のID(例えば「顧客ID」など)で関連付けられている前提ではなく、「メインフォームのレコード位置に連動して、サブフォームの表示位置も制御したい」という、より高度なUI制御のケースを想定します。

実装コード

フォームのモジュール(メインフォーム側)に以下のコードを記述します。

Option Compare Database
Option Explicit

''' <summary>
''' メインフォームのレコードが移動したときに呼び出されるイベント
''' </summary>
Private Sub Form_Current()
    On Error GoTo Err_Handler

    ' 1. 変数の宣言
    ' rsClone: メインフォームの現在の位置を特定するための「分身」
    Dim rsClone As DAO.Recordset
    ' subRs: サブフォームのレコードセット
    Dim subRs As DAO.Recordset
    
    ' 2. メインフォームの「分身」を取得
    Set rsClone = Me.RecordsetClone
    
    ' 3. サブフォームのレコードセットを取得
    ' ※SubformControlName は、フォーム上の「サブフォームコントロール」の名前です
    Set subRs = Me.SubformControlName.Form.Recordset

    ' 4. 同期のロジック
    ' メインフォームにレコードが存在するか確認
    If Not (rsClone.EOF And rsClone.BOF) Then
        
        ' メインフォームの現在のブックマーク(付箋)を取得
        ' これをサブフォーム側に「これと同じ場所を見ろ」と指示する
        ' ※注意:サブフォームとメインフォームのデータ構造が論理的に一致している場合に有効
        
        ' ここでは例として、メインのブックマークをサブに適用する
        ' ただし、サブフォームがメインのレコードと直接連動している場合は、
        ' Accessの標準機能が働くため、この処理は「独自の検索ロジック」が必要な時に使います。
        
        ' 【応用例】メインのIDを元にサブフォームを検索して同期する場合
        ' subRs.FindFirst "[関連ID] = " & Me!ID
        
        ' 【純粋な位置同期の例】
        ' もしサブフォームがメインと同じレコードセットを参照している特殊な構造なら:
        If Not subRs.EOF Then
            subRs.Bookmark = rsClone.Bookmark
        End If
        
    End If

Exit_Handler:
    ' 5. 後片付け(メモリ解放)
    ' オブジェクトを使い終わったら必ず解放するのが、プロの作法です
    Set rsClone = Nothing
    Set subRs = Nothing
    Exit Sub

Err_Handler:
    ' エラーが発生した場合、ユーザーに知らせつつ処理を抜ける
    MsgBox "同期中にエラーが発生しました: " & Err.Description, vbCritical
    Resume Exit_Handler
End Sub

コードの解説:ここが重要!

1. `Me.RecordsetClone`:
今、ユーザーが見ているレコードの「付箋(Bookmark)」を、画面を動かさずにこっそり取得するために使います。
2. `subRs.Bookmark = rsClone.Bookmark`:
ここが魔法の一行です。「サブフォームの付箋を、メインフォームの付箋と同じものに書き換える」という指示です。これにより、サブフォームの表示位置が瞬時にメインと同じ場所に飛びます。
3. `Set … = Nothing`:
これ、めちゃくちゃ大事です。DAOのオブジェクト(Recordset)は、使い終わった後に「使い終わったよ」と明示的に伝えないと、メモリの中に居座り続け、Accessが重くなったり、最悪クラッシュしたりします。

4. 陥りやすいエラーと回避策

この実装を行う際、初心者が必ずと言っていいほど遭遇するエラーが2つあります。

① 「No current record(現在のレコードがありません)」エラー

原因: レコードが一件も登録されていない状態で、ブックマークを操作しようとすると発生します。
対策: 必ず `If Not (rsClone.EOF And rsClone.BOF) Then` を使い、「データが存在するかどうか」をチェックしてから処理を行うようにしましょう。

② 「Invalid Bookmark(無効なブックマークです)」エラー

原因: メインフォームのブックマークをサブフォームに渡そうとしたとき、サブフォーム側に「その付箋」が存在しない場合に発生します。
対策: メインとサブが全く別のテーブルを参照している場合、単純な `Bookmark` の代入はできません。その場合は、`subRs.FindFirst “[ID] = ” & Me!ID` のように、「ID(主キー)を使って検索して移動する」というアプローチに切り替えましょう。

まとめ:制御の主導権を握ろう

いかがでしたか?

「レコード番号」という不安定な数字ではなく、「ブックマーク」という確実な目印を使う。そして「RecordsetClone」という分身を使ってスマートに情報を取得する。この考え方が身につくと、Access VBAの制御力は飛躍的に向上します。

マクロの記録は「結果」を教えてくれますが、VBAは「仕組み」を教えてくれます。仕組みがわかれば、もうエラーに怯える必要はありません。あなたは、システムの設計者(アーキテクト)への道を一歩踏み出したのです。

一歩ずつ、着実に進んでいきましょう。応援していますよ!

タイトルとURLをコピーしました