こんにちは!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ファイルで試してみてくださいね。応援しています!
