AccessVBAでサブフォームの実体の参照を捕まえる
帳票フォームをLoadする時にサブフォーム部品の中のFormのインスタンスの参照を取りたいと思った事はないだろうか。
私はよくある。
何故なら、サブフォームに定義したイベントを監視したり、プロシージャをCallしたりしたいからだ。
画面部品の『サブフォーム』をそのまま使うのでは、リンク項目で従属テーブルをぶら下げて表示したり、Requeryプロシージャで再表示したりするくらいしかできず、やきもきさせられる。
そんな時は型を明示してサブフォームの実体を取り出せばよい。そのやり方を以下に示す。
レポートでも同じように出来るのかは検証していないが、多分上手く行くだろう。
間違った方法1:デザインしてあるサブフォームから取り出そうとする
普通の手順では、デザインビューを使ってサブフォーム部品を貼り付け、ソースオブジェクトを設定する。従属テーブルであればここでリンク項目も設定しておけば、大体上手くいく。
サブフォームには最初から目当てのフォームが表示出来ているので、後はこれを取り出してやればよさそうだ。
この時のコードは以下のようになる。
Private WithEvents m__SubFoo As Form_SubFoo
Private Sub Form_Load()
Set m__SubFoo = Me.subform.Form '<- Nothing
End Sub
メンバ変数の参照がNothingのままなので、操作しようとするとエラー(ぬるり)になる。
As Newで宣言すればエラーは回避されるが恐らく望んだ動作にはなるまい。
Loadイベントの実行順を知っている人なら、何が起こっているのか分かるかと思う。
サブフォームの中身のインスタンスの生成は親フォームのOpenが終わるまで遅延されるので、このタイミングではFormプロパティを見ても参照を取る事ができない。
間違った方法2:Newしてからサブフォームにはめ込もうとする
方法1ではサブフォームの中身が出来上がってないのが悪いのだった。
そうであるならば、自分でインスタンスを作ってやれば解決しそうにみえる。
別ウィンドウとして開く時みたいに目的のフォームを明示的にNewするだけだし、簡単そうだ。
この時のコードは以下のようになる。
Private WithEvents m__SubFoo As Form_SubFoo
Private Sub Form_Load()
Set m__SubFoo = New Form_SubFoo
Set Me.subform.Form = m__SubFoo '<- RuntimeError. ReadOnly Property!
End Sub
なんてこった。万事休すだ。
サブフォームのFormプロパティは読み取り専用なので、外からSetできない。
Openイベントの完了まで待たないと取れないとなるとイベントを監視するためのインスタンスを取り出す機会がない。
Timerでタイミングをずらして上手くいくように祈るしかないのだろうか。
正しい方法:たった一つの冴えたやり方
もちろん、解決策はある。しかもシンプルなやつだ。
普段使わないであろうTimerなんぞに登場してもらう必要もない。
正解のコードは以下のようになる。
Private WithEvents m__SubFoo As Form_SubFoo
Private Sub Form_Load()
Me.subform.SourceObject = "SubFoo" '<- include:New Form_SubFoo
Set m__SubFoo = Me.subform.Form
End Sub
Private Sub m__SubFoo_Notify(ByVal msg As String)
Call MsgBox(Prompt:=msg, Buttons:=vbOkOnly)
End Sub
Public Event Notify(ByVal msg As String)
Private Sub Command1_Click()
RaiseEvent Notify("SubFoo.Command1_Clicked!")
End Sub
サブフォームのSourceObjectプロパティはFormモジュールの名前を設定する項目だが、
コード内でSetしてやると、その場でFormのインスタンスが生成される。
つまり、すぐ次の行でFormプロパティをGetして参照を得ることができる。
これでめでたくイベントを監視できるし、必要であればプロシージャをCallすることができる。
(ここではプロシージャCallの例は割愛している)