多くの場合Accessフォームにはレコードソースが設定されていて、
単票フォームの時は単一レコードが、帳票フォームとデータシートビューの時はレコード一覧が
表示されます。後者の場合、フォームの大きさにすべてのレコードが表示しきれない時は右側に
スクロールバーが現れてドラッグしたり、マウスホイールを回せば(フォームがアクティブ時)画面が
スクロールしてくれます。つまり、対象のフォームに対して直接アクションをしなければなりません。
もちろんDoCmd.GoToRecordを実行したりすればその限りではないのですが、この場合は
カレントレコードが移動してしまいます。
今回は対象フォームが非アクティブであっても外部からスクロールさせる例を書いてみます。
最終的には以下の図で右側のFrmBの内のボタンをクリックすると、 左側のデータシートフォームFrmAがスクロールするようにします。
以下の記述はAccess 2000での例です。
レコードソース : 1.で作成したテーブル名 既定のビュー : データシート ビュー設定 : データシート スクロールバー : 水平/垂直(デフォルト)FrmAをデザインビューで開いてツールバーの表示(V)→フィールドリスト(L)から、フォームの 詳細セクションにドラッグ&ドロップしてコントロールを配置して表示すると 図1のようになると思います。
スクロールバー : なし レコードセレクタ : なし 移動ボタン : なし 区切り線 : なし (別に設定しなくても問題ないです。見栄えだけのことです)
テキストボックス - 名前 : txt0 コンボボックス - 名前 : cmb0 値集合タイプ : 値リスト 値集合ソース : 1;2;3;4;・・・;10 (スクロール間隔) 入力チェック : はい コマンドボタン1 - 名前 : cmd_up クリック時 : [イベントプロシージャ] コマンドボタン2 - 名前 : cmd_down クリック時 : [イベントプロシージャ]
'//クラスモジュール CScrollbar/////////////////////////////////////// '// Option Compare Database Option Explicit 'スクロールバーハンドル取得 Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _ (ByVal hWnd1 As Long, _ ByVal hWnd2 As Long, _ ByVal lpsz1 As String, _ ByVal lpsz2 As String) As Long 'スクロールバーウィンドウスタイル取得 Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _ (ByVal hwnd As Long, _ ByVal nIndex As Long) As Long Private Const GWL_STYLE = (-16) Private Const SBS_VERT = &H1& '垂直スクロールバー 'Private Const SBS_HORZ = &H0& '水平スクロールバー Private Const WS_DISABLED = &H8000000 'スクロールバーパラメータ取得 Private Declare Function GetScrollInfo Lib "user32" _ (ByVal hwnd As Long, _ ByVal n As Long, _ lpScrollInfo As SCROLLINFO) As Long Private Const SB_CTL = 2 '第二引数に指定 'WM_VSCROLLメッセージ送信用 Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hwnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long '2011-6-22修正 Private Const WM_VSCROLL = &H115 'メッセージ 'Private Const WM_HSCROLL = &H114 Private Const SB_THUMBPOSITION = 4 'wParam設定用 'Private Const SB_THUMBTRACK = 5 '同上 'GetScrollInfo関数で使用する構造体 Private Type SCROLLINFO cbSize As Long fMask As Long nMin As Long nMax As Long nPage As Long nPos As Long nTrackPos As Long End Type Private Const SIF_RANGE = &H1 'SCROLLINFO構造体 fMaskメンバ設定用 Private Const SIF_PAGE = &H2 ' 同上 Private Const SIF_POS = &H4 ' 同上 Private vmoFrm As Access.Form '親フォームへの参照 '------------------------------------------------------------------------ 'スクロールさせるフォームへの参照取得 Public Sub Init(argoFrm As Access.Form) Set vmoFrm = argoFrm End Sub 'スクロールさせるフォームの現在のビュー確認(フォームがデザインビューでないか) Private Function IsParentView() As Boolean On Error GoTo ErrRoutine If vmoFrm.CurrentView = 0 Then IsParentView = False Else IsParentView = True End If Exit Function ErrRoutine: IsParentView = False End Function '垂直スクロールバーへのハンドル取得 Private Function GetVScrollbarHandle() As Long Dim vlHScbar As Long If Not IsParentView() Then Exit Function 'スクロールバーのハンドル取得 vlHScbar = FindWindowEx(vmoFrm.hwnd, 0&, "ScrollBar", vbNullString) '(A) Do Until vlHScbar = 0 If (GetWindowLong(vlHScbar, GWL_STYLE) And _ (SBS_VERT Or WS_DISABLED)) = SBS_VERT Then '(B) Exit Do End If vlHScbar = FindWindowEx(vmoFrm.hwnd, vlHScbar, "ScrollBar", vbNullString) Loop GetVScrollbarHandle = vlHScbar End Function '垂直スクロール実行 '引数 : arglDist スクロールさせる距離 Public Sub VerticalMove(arglDist As Long) Dim vtScInfo As SCROLLINFO Dim vlHScbar As Long Dim vlNewPos As Long Dim vlWParam As Long 'スクロールさせるフォームがなかったりデザインビューの場合は終了 If Not IsParentView() Then Exit Sub '対象フォームのスクロールバープロパティが水平のみまたはなしの場合は終了 If vmoFrm.ScrollBars = 0 Or vmoFrm.ScrollBars = 1 Then Exit Sub '垂直スクロールバーハンドル取得 vlHScbar = GetVScrollbarHandle() '(C) If vlHScbar = 0 Then Exit Sub 'GetScrollInfoで取得するスクロールのパラメータ種類の設定 vtScInfo.cbSize = LenB(vtScInfo) 'Page,つまみの位置,範囲を取得 vtScInfo.fMask = SIF_PAGE Or SIF_POS Or SIF_RANGE 'スクロールバーのパラメータ取得 If GetScrollInfo(vlHScbar, SB_CTL, vtScInfo) = 0 Then '(D) Exit Sub End If '新しいツマミの位置設定 If (vtScInfo.nPos + arglDist) < vtScInfo.nMin Then '(E) vlNewPos = vtScInfo.nMin ElseIf (vtScInfo.nPos + arglDist) > _ vtScInfo.nMax - IIf(vtScInfo.nPage - 1 > 0, vtScInfo.nPage - 1, 0) Then vlNewPos = vtScInfo.nMax _ - IIf(vtScInfo.nPage - 1 > 0, vtScInfo.nPage - 1, 0) Else vlNewPos = vtScInfo.nPos + arglDist End If 'WM_VSCROLLメッセージ送信時に加えるwParamの値作成 vlWParam = vlNewPos * &H10000 Or SB_THUMBPOSITION '(F) 'メッセージ送信 SendMessage vmoFrm.hwnd, WM_VSCROLL, vlWParam, vlHScbar vmoFrm.Repaint End Sub |
・宣言部
このクラスの各プロシージャで使用するWin32APIおよび 定数・構造体を宣言、およびスクロールさせたいフォームへの参照を保持するメンバ(vmoFrm)の 宣言です。Win32APIの宣言については ここに テキストファイルがあるので興味がある方はダウンロードしてご覧になってください。
・Init Subプロシージャ
引数で指定されたスクロールさせたいフォームへの参照を メンバ変数vmoFrmに代入します。
・IsParentView Functionプロシージャ
Initプロシージャで設定されたフォームのビューが デザインビューでないか、またはオブジェクト自体ないかをチェックするルーチン。
・GetVScrollbarHandle Functionプロシージャ
このプロシージャについて説明するにはSpy++という
ツールを使うと分かりやすいのですが、これはVisual Studioに付属しているものなので
(MSのサイトにあるかどうかはわかりません)結論だけ書きます。
Accessフォームには様々な子ウィンドウがあり、その中でスクロールバーのウィンドウ
(コントロールもウィンドウです)はそのクラス名が「Scrollbar」で、これをFindWindowExの第三引数に
指定して(A)でスクロールバーのハンドルをvlHScbarに取得しています。ところがスクロールバーには
垂直と水平の2つがありクラス名だけではどちらが垂直か判断できません。そこでGetWinodowLongで
垂直なスクロールバーにはあるSBS_VERTが設定されているかを確認して(B)、設定されていれば
Doループを抜け、設定されていなければZオーダーでクラス名が「Scrollbar」である次のウィンドウの
ハンドルを取得します。
※ここでいう「クラス名」とはいわゆるオブジェクト指向や「クラスモジュール」のクラスではなく、
ウィンドウクラスの意味です。
・VerticalMove Subプロシージャ
このプロシージャで実際にスクロールさせます。
GetVScrollbarHandleで取得したハンドルを使って(C)、GetScrollInfoで現在のスクロールの位置と
移動範囲の最大値・最小値、ページ数を取得します(D)。詳しい解説は MSDNでGetScrollInfo関数
およびSCROLLINFO構造体を参照してください。
次に取得したスクロールバーのツマミの現在の位置(SCROLLINFO構造体のnPosメンバ)に、
このプロシージャの第一引数で指定したarglDistの値を加えて(上にスクロールする場合はマイナス値)、
新しい位置をvlNewPosに設定します。この際GetScrollInfoで取得したツマミの範囲内に収まるように
位置を調整します(E)。
新しいツマミの位置が決まったのでWM_VSCRLLメッセージを対象フォーム(vmoFrm)に送信するのですが、
wParam、lParamにスクロールするためのパラメータを設定しなければなりません。
新しい位置にスクロールするにはwParamの下位16ビットにSB_THUBPOSION、上位16ビットに新しい位置を
設定し(F)、lParamにスクロールバーコントロールのハンドル(vlHScbar)を設定してSendMessegeを
実行します。
詳しい説明はMSDNのWM_VSCROLLを参照してください。
なお、Initプロシージャ、VerticalMoveプロシージャ以外は外部に公開する必要がないので
Privateにしておきます。
以上でクラス実装ができたので、CScrollbarオブジェクトを使ってFrmBのモジュールに
実装していきます。
'//FrmB フォームモジュール/////////////////////////////// '// Option Compare Database Option Explicit 'ボタンクリックイベント Private Sub cmd_up_Click() FormScroll True End Sub 'ボタンクリックイベント Private Sub cmd_down_Click() FormScroll False End Sub Private Sub FormScroll(argyUp As Boolean) Dim voScBar As CScrollbar On Error Resume Next Set voScBar = New CScrollbar voScBar.Init Forms(Me!txt0.Value) voScBar.VerticalMove CLng(IIf(argyUp, -1, 1) * Me!cmb0.Value) End Sub |
今回はコマンドボタンのクリックでスクロールさせましたが、MouseWheelイベントがある AccessのバージョンでしたらCScrollbarオブジェクトを使えば同じ感覚で別フォームのスクロールが できると思います。
※はっきりいってこんなんできて何がおもろい?と思われる方も多いと思います。 書いた私も実は使い道なんて思いつかないですし、たいしたネタもないのでとりあえず余興として 書いてみました。