Microsoft Access2003

Page 7 (2005/01)

イベント
Accessでアプリケーションを作るということは、フォームを操作するすることによって発生するさまざまなイベントにそってイベント・プロシージャを作って行くことですが、膨大で複雑なイベントの流れを理解するのは難しいです。
もっとも、たくさんのイベントが次々に起こるといっても、それらのすべてにプロシージャが必要なわけではないので、使用頻度の高いものを理解しておけば必要な処理はできると言えます。

テキストボックスやコマンドボタンなどの様々なコントロールの器となるフォームにはどのような順序でイベントが発生するのかをまとめてみました。

[Open/開く時]           フォームが開き、最初のレコードが表示される前に発生
 ↓
[Load/読み込み時]        レコードが表示されると発生
 ↓
[Resize/サイズ変更時]     名前の通りフォームのサイズを変更したときに発生。フォームをオープンするときも発生する。
 ↓
[Activate/アクティブ時]     開いたフォームがアクティブになると発生
 ↓
[Current/レコード移動時]    フォーカスがレコードに移動し、そのレコードがカレント レコードになったとき、
                        またはフォームの再表示か再クエリが実行されたときに発生します。

Openと Loadイベントの他のイベントは、フォームオープン時以外にも発生するイベントです。むしろ、フォームオープン時にも発生するということです。
フォームにコントロールがひとつもないときはActivateと Currentの間で GotFocusが発生します。もっとも、コントロールがひとつも無いということはあまりないと思いますけど。
フォームを閉じるときのイベントの発生順は・・
[Unload/読み込み解除時] → [Deactivate/非アクティブ時] → [Close/閉じる時]
という順序です。
単純なフォームとプロシージャで確認してみました。

MsgBoxなどのダイアログを使うとフォーカスの移動などが発生するため、起こったイベントをプライベート変数に代入して、最後にフォームのテキストボックスに表示するだけです。

左図のように適当な名前のフォームを作り、テキスト1と名前が付けられるテキストボックスを配置するだけで。

フォームのイベント・プロシージャには下のコードリストを貼りつけます。
Option Compare Database
Option Explicit
Dim strEvent As String
Private Sub Form_Activate()
  strEvent = strEvent & "Form_Activate" & vbCrLf
End Sub
Private Sub Form_Close()
  strEvent = strEvent & "Form_Close" & vbCrLf
  テキスト1 = strEvent
  MsgBox "Form_Close"
End Sub
Private Sub Form_Current()
  strEvent = strEvent & "Form_Current" & vbCrLf
  テキスト1 = strEvent
End Sub
Private Sub Form_Deactivate()
  strEvent = strEvent & "Form_Deactivate" & vbCrLf
  テキスト1 = strEvent
  MsgBox "Form_Deactivate"
End Sub
Private Sub Form_GotFocus()
  strEvent = strEvent & "Form_GotFocus" & vbCrLf
End Sub
Private Sub Form_Load()
  strEvent = strEvent & "Form_Load" & vbCrLf
End Sub
Private Sub Form_Open(Cancel As Integer)
  Dim intYN As Integer
  intYN = MsgBox("フォームを開きますか?(Openイベントからのメッセージ)", vbYesNo)
  Select Case intYN
    Case vbYes
      MsgBox "フォームをOpenします"
      strEvent = "Form_Open" & vbCrLf
    Case vbNo
      MsgBox "CancelされたのでフォームをOpenしません"
      Cancel = True
  End Select
End Sub
Private Sub Form_Resize()
  strEvent = strEvent & "Form_Resize" & vbCrLf
End Sub
Private Sub Form_Unload(Cancel As Integer)
  strEvent = strEvent & "Form_Unload" & vbCrLf '
  Me!テキスト1 = strEvent
  MsgBox "Form_Unload"
End Sub
Private Sub テキスト1_Enter()
  strEvent = strEvent & "テキスト1_Enter" & vbCrLf
End Sub
Private Sub テキスト1_GotFocus()
  strEvent = strEvent & "テキスト1_GotFocus" & vbCrLf
  テキスト1 = strEvent
End Sub
上のシンプルなフォームはコントロールとしてテキストボックスひとつだけですが、コントロールの場合、フォーカスが当たるときと、抜け出すときに発生するイベントは次の通りです。
[Enter/フォーカス取得時] →  [GotFocus/フォーカス取得後]
[Exit/フォーカス喪失時]  → [LostFocus/フォーカス喪失後]

Enterは初めてフォーカスを取得したときに発生し、他のフォームから戻ってきたときには、Enterイベントは発生せず、GotFocusが発生します。
このように、イベントが発生する条件と順序を理解してイベント・プロシージャを割り当てないと、希望する処理ができなかったりします。

上記の例ではフォームがひとつの場合ですが、これが二つのフォームの間をフォーカスが移動すると、イベントの発生はかなり煩雑なものになります。
それから、ややこしいことですが、フォーム編集中に表示されるプロパティ・ウィンドウのなかのイベント・タブのなかに表示されるのはプロパティです。
例えばフォームを開くときは、[OnOpen/開く時]というプロパティにイベント・プロシージャを割り当てるわけです。OnOpenプロパティとOpenイベントを混同しないようにしましょう。
キーボード・イベント
フォームやコントロールの中でキーボードのキーを押すのもイベントですが、コントロールとフォームのどちらでイベントを感知かるのか?というところも問題です。フォームのプロパティの中に[KeyPreview/キーボードイベント取得]というプロパティがあります。
True/Falseの値を持ち、デフォルトでは False/いいえ で、この場合、アクティブなコントロールだけがキー入力を受け取ります。
ただし、フォーム上にフォーカスを受け取れるコントロールが無い場合はフォームが受け取ります。
値を True にすると、先ずフォームがキー入力を受け取り、次にアクティブなコントロールが受け取ります。

キー入力によって発生するイベントは、KeyPress[OnKeyPress/キー入力時] と KeyDown[OnKeyDown/キークリック時] と KeyUp[OnKeyUp/キー解放時] です。
KeyPressはANSI文字セットに対応するキー、キーの組合せに反応し、引数KeyAsciiはANSIキーコードを返します。
このKeyAsciiに0を設定すると、オブジェクトはキーが押されたことを認識しません。フォーム上のどのコントロールにフォーカスがあっても、共通のキー操作に対応するのには便利です。

ANSI文字セット
「米国規格協会が定めた256文字の文字セット。最初の128文字は標準的なキーボードのアルファベットや記号に対応しています。
ASCII文字はANSI文字セットの一部です」
「英数字記号文字を含む、7bitの基本的な文字コード。ほかの文字コードでも、英数字部分の文字コードはこのANSI文字コードをベースにしているものがほとんどである。ASCIIコードとも呼ばれる」


KeyDownはANSI文字セットだけではなく、ほとんどのキー入力に対して発生します。
キーを押し続けると、KeyDownと KeyPressのイベントが交互に発生し、キーを離したときにKeyUpイベントが発生します。
ファンクション・キー、方向キー、TABキー、Shiftや Ctrlや Altキーとの組合せなどを検出できます。

基本的には、KeyPress を使い、これで対応ではないキーは KeyDown イベントで対応する、ということのようです。
この二つの違いを視覚的に理解できるような簡単なフォームを作ってみました。
下の二つのテキストボックスの名前はtxtKeyDownとtxtKeyPressです。
入力したキーの KeyCodeと KeyAscii の値を調べるのに使えます。
Option Compare Database
Option Explicit
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
  txtKeyPress = ""
  txtKeyDown = KeyCode
End Sub
Private Sub Form_KeyPress(KeyAscii As Integer)
  txtKeyPress = KeyAscii
End Sub
Private Sub Form_Load()
  Me.KeyPreview = True       フォームがキーストロークを受け取れるように設定する
  txtKeyDown.TabStop = False   一番上のテキストボックスからフォーカスが移動しないようにする
  txtKeyPress.TabStop = False
End Sub
例えば、数字の1はキーボードとテンキーにありますが、KeyDownでは、これは別のキーということで、違うKeyCodeを返しますが、KeyPressでは、同じ「1」ということで同じ文字コードを返します。

ところで、ヘルプファイルに書かれている細かなことなのですけど、キー入力によってフォーカスが移動する場合、KeyDownは移動前のコントロールで発生し、KeyPressとKeyUpは移動後のコントロールで発生するということですから、注意が必要です。
データ、レコードの変更、更新イベント
テキストボックスやコンボボックスを変更するときに発生するイベントがChange[OnChang/変更時]です。これはキー入力で変更する度に発生します。
変更したあと、他のコントロールにフォーカスが移動すると、データが更新され、BeforeUpdate/更新処理前と AfterUpdate/更新処理後 が順次発生します。キーイベントやフォーカス移動イベントとのからみを示すと以下のようになります。
                                                               (次のコントロール)
KeyDown→ KedyPress→ Change→ KeyUp→ BeforeUpdata→ AfterUpdate→ Exit→ LostFocus→ Enter→ GotFocus

BeforeUpdateは引数としてCancelを持っため、変更入力をキャンセルできます。入力値のチェックに使われます。
KeyPress、KeyDownでのShft Alt Ctrl キーの検出とBeforeUpdateやAfterUpdateを確認するフォームを作ってみました。
フォームにあるのは「テキスト0」「テキスト2」そして「txtKeyCode」「txtKeyPress」の4個だけです。
大文字変換は英字だけです。
Option Compare Database
Option Explicit
Private Sub txtKeyPress_KeyPress(KeyAscii As Integer)
  Dim strCharacter As String
  strCharacter = Chr(KeyAscii)
  KeyAscii = Asc(UCase(strCharacter))
End Sub
Private Sub テキスト0_AfterUpdate()
  MsgBox "テキスト0_AfterUpdate" & vbCrLf & "次のコントロールに移動します"
End Sub
Private Sub テキスト0_BeforeUpdate(Cancel As Integer)
  Dim Ans As Integer
  Ans = MsgBox("テキスト0_BeforeUpdateイベントです", vbYesNo)
  If Ans = vbNo Then
    Cancel = True
    テキスト2 = " ↑ BeforeUpdateでキャンセル"
  End If
End Sub
Private Sub テキスト2_Enter()
  テキスト2 = ""
End Sub
Private Sub テキスト0_KeyDown(KeyCode As Integer, Shift As Integer)
  Dim intShiftDown As Integer, intAltDown As Integer
  Dim intCtrlDown As Integer         'これは某マニュアルのサンプルですが不可解です
  intShiftDown = (Shift And acShiftMask) > 0 'このようなAnd演算子の使い方は謎です
  intAltDown = (Shift And acAltMask) > 0
  intCtrlDown = (Shift And acCtrlMask) > 0
  If intShiftDown Then MsgBox "テキスト0で Shiftキー が押されました"
  If intAltDown Then MsgBox "テキスト0で Alt キーが押されました"
  If intCtrlDown Then MsgBox "テキスト0で Ctrl キーが押されました"
End Sub
Private Sub テキスト2_KeyDown(KeyCode As Integer, Shift As Integer)
  txtKeyCode = KeyCode  'こちらは↑のコードと同じ処理をやってみました。
  Select Case Shift
    Case acShiftMask '組み込み定数の値は1
      MsgBox "テキスト2で Shiftキー が押されました"
    Case acAltMask  '組み込み定数の値は4
      MsgBox "テキスト2で Altキー が押されました"
    Case acCtrlMask '組み込み定数の値は2
      MsgBox "テキスト2で Ctrlキー が押されました"
    Case Else
  End Select
End Sub
フォームなどで起こる様々なイベントの流れと機能については、驚くほど煩雑ですが、良く理解してコードを割り当てないと、矛盾した処理をしてしまうおそれがあります。
このあたりのイベントの流れはとても大切なことなのに、きちんと解説した解説書はほとんど無いみたいに感じます。

ひとつの分かりやすい例として、テキストボックスを入力必須として、フォーカスを失うときにチェックを入れて、無記入の場合はフォーカス喪失などをキャンセルするような処理をすると、フォームを閉じるときにもエラーチェックが機能してしまいます。これを避けるようなイベントで処理しなくてはならない訳ですね。
複数のフォームを往復しながら処理する場合は、とても煩雑になってくるようです。
ある意味、イベント駆動型プログラミングの大きなネックになるのが、このイベントの理解ではないかと思います。


Page Topへ戻る