【入門編】DAO.RecordsetのCloneとBookmarkを活用した、フォームの表示位置を維持したデータ更新 – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAの世界へようこそ。業務効率化の現場で数々のデータベースを構築してきた先輩エンジニアです。

Accessでフォームを作成し、ボタンを押してデータを更新したとき、「画面が一瞬チラついて、一番上のレコードまで勝手にスクロールしてしまった…」という経験はありませんか?

ユーザーからすると「あれ?いまどの行を操作していたっけ?」と迷子になってしまい、地味ですが非常にストレスが溜まるポイントです。マクロや単純なVBAコードから一歩抜け出すための登竜門が、まさにこの「フォームの表示位置・選択行を維持したデータ更新」です。

今回は、Access VBAの強力な武器である`DAO.Recordset`の「Clone(クローン)」「Bookmark(ブックマーク)」という2つの機能を使い、プロレベルの滑らかなUI(ユーザーインターフェース)を実現する方法を分かりやすく解説します。

ここをクリアすれば、Access VBAの基本とオブジェクトモデルの理解はバッチリですよ!一緒に学んでいきましょう。

1. なぜ画面が勝手にスクロールしてしまうのか?

まずは仕組み(裏側で起きていること)を簡単に知っておきましょう。

Accessのフォームは、裏側でデータベースのテーブルやクエリと「透明な太いパイプ」でつながっています。このパイプを通ってきたデータの集合体を、VBAでは「レコードセット(Recordset)」と呼びます。

よくある初心者向けコードで使われる`Me.Requery`(再照会)は、例えるなら「パイプを一度切断して、データを最初から読み直す」ような処理です。

【Me.Requery を実行したときイメージ】
[画面] ──(切断!)──> [データ再読み込み] ──> [一番上を表示]
※ユーザーのスクロール位置やカーソル位置はリセットされてしまう!

これでは画面がチラつき、スクロールバーも一番上に戻ってしまいますよね。
そこで登場するのが、画面を再読み込み(Requery)せずに、裏側でそっとデータを書き換える`RecordsetClone``Bookmark`のテクニックです。

2. 2大ヒーロー「Clone」と「Bookmark」を理解しよう

難しそうな横文字が出てきましたが、概念はとってもシンプルです!

① Bookmark(ブックマーク)とは?

レコードセット内の「特定の行」を指し示す「しおり」のようなものです。
Accessが内部で各行に割り当てている一意の識別データで、これを使えば「さっきまで選んでいたあの行!」へ一瞬でジャンプできます。

② RecordsetClone(レコードセット・クローン)とは?

フォームが現在表示しているデータ(レコードセット)の「影武者(分身)」です。
画面上の表示(表側の世界)に影響を与えずに、裏側でデータ検索や書き換えを行うために作成します。

> 💡 ここが最高に面白いポイント!
> フォーム本体の`Recordset`と、分身である`RecordsetClone`は、メモリ上の同じデータ領域を参照しています。
> つまり、影武者がデータを書き換えると、表側のフォーム画面にも即座に(チラつきなしで)変更が反映されるのです!

3. 実践!画面位置を完全に維持して更新するVBAコード

それでは具体的なコードを見てみましょう。
フォーム上の「ステータス更新」ボタンを押したときに、現在の行の位置(スクロール位置)を一切崩さずにデータを更新する実装です。

以下のコードをそのままモジュールに貼り付けて試してみてください。

Private Sub btnUpdateStatus_Click()
‘ エラーハンドリング(予期せぬエラー時に安全に処理を抜けるため)
On Error GoTo ErrorHandler

‘ 1. 変数の宣言
Dim rs As DAO.Recordset
Dim savedBookmark As Variant

‘ 新規レコード入力中(まだ保存されていない行)の場合は処理を中断
If Me.NewRecord Then
MsgBox “新規入力中の行は更新できません。一度保存してください。”, vbInformation
Exit Sub
End If

‘ データが1件もない場合は処理しない
If Me.Recordset.RecordCount = 0 Then Exit Sub

‘ ————————————————————-
‘ 2. 現在の「選択位置(しおり)」を退避する
‘ ————————————————————-
savedBookmark = Me.Bookmark

‘ ————————————————————-
‘ 3. フォームの影武者(RecordsetClone)を取得する
‘ ————————————————————-
Set rs = Me.RecordsetClone

‘ ————————————————————-
‘ 4. 影武者の位置を、退避しておいた「しおり」の場所へ合わせる
‘ ————————————————————-
rs.Bookmark = savedBookmark

‘ ————————————————————-
‘ 5. データを安全に更新する(Edit ~ Update)
‘ ————————————————————-
rs.Edit
rs.Fields(“ステータス”).Value = “対応完了”
rs.Fields(“更新日時”).Value = Now()
rs.Update

‘ ————————————————————-
‘ 6. 後片付け(メモリの解放)
‘ ————————————————————-
rs.Close
Set rs = Nothing

‘ ユーザーへ完了メッセージ(画面のスクロール位置は維持されています!)
‘ ※メッセージ不要な場合はカットしてOKです
‘ MsgBox “ステータスを更新しました。”, vbInformation

Exit Sub

ErrorHandler:
‘ エラーが発生した場合は安全にオブジェクトを破棄する
If Not rs Is Nothing Then Set rs = Nothing
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub

4. プログラミング解説:コードの裏側で何が起きているか?

先輩エンジニアとして、特に意識してほしい重要ポイントを解説しますね。

ポイント1:`Me.Bookmark` を変数に保存する

`savedBookmark = Me.Bookmark`
これで、クリックした瞬間にユーザーが操作していた「行」の住所を`Variant`型の変数に記憶させています。

ポイント2:`Edit` と `Update` で囲む

DAO(Data Access Objects)でデータを更新する際の絶対ルールです。

  • `rs.Edit` :「今からこの行を書き換えるよ!」という準備(ロック)
  • `rs.Fields(…).Value = …` :値を書き換える
  • `rs.Update` :「書き換え確定!データベースに保存して!」

この`rs.Update`が実行された瞬間、`Me.Requery`をしなくても、画面上のフォームの値がスッと書き換わります。 画面が再描画されないため、スクロール位置は1ピクセルも動きません。

ポイント3:オブジェクトの解放(ライフサイクルの管理)

rs.Close
Set rs = Nothing

VBAにおいて、`Set`コマンドで割り当てたオブジェクト(`DAO.Recordset`など)は、使い終わったら明示的に`Nothing`を代入してメモリを開放するのが鉄則です。これを怠ると、Accessの動作が徐々に重くなる「メモリリーク」の原因になります。

5. 【発展】SQL更新や Requery が必要な場合はどうする?

「どうしても `Me.Requery` を実行しなければいけない時」(例:別の場所で追加された新データを画面に反映したい時など)もありますよね。

実は、`Me.Requery` を行うと以前の `Bookmark` は無効(ゴミデータ)になってしまいます
その場合は、「主キー(IDなど)」を使って位置を復元するアプローチをとります。こちらもセットで覚えておくと無敵です!

Private Sub SafeRequeryExample()
Dim currentID As Long

‘ 1. 現在選択されている行の「ID(主キー)」を覚えておく
If Not Me.NewRecord And Me.Recordset.RecordCount > 0 Then
currentID = Me!ID.Value
End If

‘ 2. 画面を最新化(ここで一番上にスクロールしてしまう)
Me.Requery

‘ 3. 覚えておいたIDの場所を探してスクロール位置を戻す!
If currentID <> 0 Then
With Me.RecordsetClone
‘ 主キーで検索
.FindFirst “ID = ” & currentID

‘ 見つかったら、フォームの Bookmark に適用してジャンプ!
If Not .NoMatch Then
Me.Bookmark = .Bookmark
End If
End With
End If
End Sub

  • 更新のみ(画面チラつきなし) $\rightarrow$ 最初にご紹介した `RecordsetClone`直接更新
  • 最新状態の全読み込み(Requery必須) $\rightarrow$ 後者の `FindFirst` + `Bookmark` 復元

この2つを使い分けられるようになれば、あなたはもうAccess VBAの初学者ではありません。立派な中級エンジニアの仲間入りです!

6. まとめ

今回のポイントを振り返ってみましょう。

1. `Me.Requery` は画面がリセットされるため、単純なデータ更新には使わない
2. `RecordsetClone`(影武者)を使えば、画面に影響を与えず裏で操作できる
3. `Bookmark`(しおり)を使えば、選択行の位置を完璧にコントロールできる
4. 使い終わったオブジェクトは `Set rs = Nothing` で綺麗に片付ける

このテクニックを一度覚えると、作成するAccessツールの操作性が劇的に向上し、ユーザー(社内の同僚やクライアント)から「このツール、すごく使いやすいです!」と感謝されるようになりますよ。

焦らず1行ずつコードの意味を噛み締めながら、ぜひご自身のAccessファイルで試してみてくださいね。応援しています!

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