理屈を並べるだけの開発者は五万といる。しかし、データベースの「魂」であるメタデータを、フロントエンドの「貌(かお)」へと昇華させ、変更に一切動じない堅牢なシステムを構築できる者は、この業界でも一握りだ。
長年、数多のレガシーシステムを解体し、再構築してきた私から言わせれば、Access VBAという枯れた技術において、UIとデータ構造を分離できていない設計は、負債以外の何物でもない。
今回は、テーブル定義(TableDef)の「説明(Description)」プロパティを抽出し、フォームのラベルへ動的に反映させる「メタデータ駆動型UI」の極意を伝授する。
—
1. なぜ「説明」プロパティをUIに同期させるのか
多くの開発者は、フォーム上のラベルに「氏名」「受注金額」といったテキストを直接ハードコードする。しかし、業務要件の変更により「受注金額」を「受注総額(税込)」に変更せよという指示が飛んだ瞬間、彼らは数十、数百のフォームを修正する苦行を強いられる。
真のアーキテクトは、Single Source of Truth(信頼できる唯一の情報源)をテーブル定義に求める。
テーブルの「説明」プロパティに日本語名を定義しておけば、それをプログラムが読み取り、実行時にUIを生成する。これにより、データ定義と表示の一貫性が保証され、保守コストは劇的に低下する。
—
2. DAOプロパティの「罠」と解決策
Accessの`TableDef`や`Field`オブジェクトにおける「説明」プロパティは、実はデフォルトでは存在しない。ユーザーがデザインビューで値を入力して初めて、`Properties`コレクションに`”Description”`という名前のプロパティが追加される仕様だ。
この「存在しないかもしれないプロパティ」へのアクセスは、実行時エラー3270(プロパティが見つかりません)を引き起こす。これをスマートに回避しつつ、高速にメタデータを取得するロジックが必要だ。
—
3. 極限の制御コード:メタデータ駆動UIの実装
以下のコードは、単なるプロパティ取得に留まらない。DAOオブジェクトのライフサイクル管理、エラーハンドリング、そしてパフォーマンスを意識した設計となっている。
3.1. メタデータ取得コア・エンジン
まずは、指定されたテーブルとフィールドから「説明」を安全に引き出す関数だ。
‘——————————————————————————————
‘ @Function: GetFieldDescription
‘ @Description: 指定したテーブル・フィールドの「説明」プロパティを取得する。
‘ DAOプロパティの遅延バインド特性を考慮し、エラー3270を適切にハンドリングする。
‘——————————————————————————————
Public Function GetFieldDescription(ByVal strTableName As String, ByVal strFieldName As String) As String
On Error GoTo Err_Handler
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim strDesc As String
‘ CurrentDbは呼び出しのたびにインスタンスを生成するため、参照を保持する
Set db = CurrentDb
Set tdf = db.TableDefs(strTableName)
Set fld = tdf.Fields(strFieldName)
‘ Descriptionプロパティはユーザーが設定しない限りコレクションに存在しない
On Error Resume Next
strDesc = fld.Properties(“Description”).Value
If Err.Number = 3270 Then
‘ 説明が未設定の場合はフィールド名を返す
strDesc = strFieldName
End If
On Error GoTo Err_Handler
GetFieldDescription = strDesc
Exit_Handler:
‘ オブジェクトの明示的解放(LIFO順)
Set fld = Nothing
Set tdf = Nothing
Set db = Nothing
Exit Function
Err_Handler:
Debug.Print “Error in GetFieldDescription: ” & Err.Number & ” – ” & Err.Description
GetFieldDescription = strFieldName
Resume Exit_Handler
End Function
3.2. フォームへの動的反映(UIインジェクション)
次に、フォームの読み込み時に各コントロールに紐付くラベルを書き換える処理を実装する。ここでは、コントロールの`ControlSource`からフィールド名を逆引きするテクニックを用いる。
‘——————————————————————————————
‘ @Procedure: SyncFormLabelsWithMetadata
‘ @Description: フォーム上のコントロールに紐付くラベルのCaptionを、
‘ テーブル定義の「説明」プロパティで一括更新する。
‘——————————————————————————————
Public Sub SyncFormLabelsWithMetadata(ByRef frm As Access.Form)
Dim ctl As Access.Control
Dim lbl As Access.Label
Dim strTable As String
Dim strField As String
‘ フォームのレコードソースが空の場合は処理しない
If frm.RecordSource = “” Then Exit Sub
strTable = frm.RecordSource
‘ フォーム内の全コントロールを走査
For Each ctl In frm.Controls
‘ テキストボックスやコンボボックスなど、データに束縛されるコントロールが対象
Select Case ctl.ControlType
Case acTextBox, acComboBox, acCheckBox, acListBox
strField = ctl.ControlSource
‘ コントロールソースが空、または式(”=”で始まる)の場合はスキップ
If strField <> “” And Not (strField Like “=”) Then
‘ 紐付くラベルを探し出す(Accessの標準機能:Attached Label)
‘ ラベルとの親子関係がない場合はエラーを無視して次へ
On Error Resume Next
Set lbl = ctl.Controls(0) ‘ 0番目の子コントロールは通常ラベル
If Err.Number = 0 Then
‘ メタデータを注入
lbl.Caption = GetFieldDescription(strTable, strField)
End If
On Error GoTo 0
End If
End Select
Next ctl
Set lbl = Nothing
Set ctl = Nothing
End Sub
—
4. プロフェッショナルの視点:最適化と拡張
4.1. パフォーマンスの壁
`CurrentDb`をループ内で繰り返し呼び出すのは、初心者が最も陥りやすいパフォーマンスの罠だ。`CurrentDb`は呼び出されるたびにデータベースの最新状態をスキャンし、新しいインスタンスを作成する。本コードのように、一度変数(`db`)にセットして使い回すことは、大規模システムにおける定石である。
4.2. Windows APIによる描画抑制
大量のラベルを動的に書き換える際、画面のチラつきが発生することがある。その場合は、Windows APIを使用して再描画を一時的に停止させる。
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hwndLock As LongPtr) As Long
‘ 処理開始時
LockWindowUpdate frm.hwnd
‘ 処理終了時
LockWindowUpdate 0
4.3. システム間連携への応用
この設計思想は、単なるラベルの書き換えに留まらない。例えば、テーブル定義の「説明」にJSON形式でメタデータを記述しておく(例:`{“label”: “単価”, “format”: “currency”, “required”: true}`)。これをパースして、実行時にバリデーションルールや表示形式まで動的に構築すれば、それはもはやAccessを超えた「メタデータ・ドリブン・フレームワーク」へと進化する。
—
5. 終わりに
「Accessは古い」と切り捨てるのは容易だ。しかし、その内部構造を深く理解し、DAOの特性を限界まで引き出すことで、現代のWebフレームワークにも劣らない柔軟なアーキテクチャを構築できる。
コードを記述する際は、常に「半年後の自分」や「保守を担う後任」の視点に立て。ハードコードされたテキストは、未来のエンジニアへの呪縛だ。メタデータという「意志」をプログラムに吹き込み、変化に強いシステムを構築すること。それこそが、我々チーフアーキテクトに課せられた使命である。
