KEN's .NET

[特集2] VB.NETのイベントの扱い方

ホーム > KEN's .NET > [特集2] VB.NETのイベントの扱い方

ここではVB.NETで変わったイベントの扱い方について紹介します。

1.Handles句

従来のVBでは例えば、コマンドボタンCommand1のClickイベントは、

Private Sub Command1_Click()
End Sub
図1 従来のVBのイベント処理

に記述すると決まっていました。これに対してVB.NETではどのプロシージャがどのイベントの処理を行うかは、 Handlesキーワードを使って指定するようになっています。 上記の例をVB.NETでの記述になおすと、

Private Sub Button1_Click _ 
    (ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
    Handles Button1.Click
End Sub
図2 VB.NETのイベント処理

のようになります。(Command1→Button1に変えているのは、従来のVBでCommandButtonだったものは、.NETではButtonクラスに変わっているからです) このコードの意味は、Button1_Clickというプロシージャが、Button1.Click(Button1のClickイベント)を取り扱う(Handles)ということです。 このコードを以下のように書き換えると、

Private Sub Button_Click _ 
    (ByVal sender As System.Object, ByVal e As System.EventArgs) _ 
    Handles Button1.Click, Button2.Click, Button3.Click
End Sub
図3 複数のイベントを1つのプロシージャで処理する方法(図2からの変更点を赤表示)

このButton_Clickプロシージャが、Button1〜3のすべてのClickイベントを取り扱うということになります。 このように複数のイベントを一つのプロシージャで処理することができます。

2.senderとe

イベントの引数に注目すると従来のVBではClickイベントには引数がなかったのが、 Object型のsenderとEventArgs型のeとなっています。 これらの役割は、senderがイベント発生源のオブジェクト、eがイベントに関連する補足情報です。 先ほども書いたように、従来のVBからVB.NETへの変更で、1つのプロシージャで複数のイベントを処理することができますので、 どのオブジェクトによって発生したイベントなのかを区別する必要がありますが、そのためのsenderです。図2で言うとsenderはいつもButton1です。 図3では、senderはButton1〜3のどれかです。

eはイベントに関連した補足情報ですが、先のClickイベントでは特別な補足情報が不要なので、 形式的に渡されるものであまり意味はありません。eの具体例は、次のFormのMouseDownイベントの例で見てみましょう。

Private Sub Form_MouseDown _ 
    (ByVal sender As Object, ByVal e As MouseEventArgs) _
    Handles MyBase.MouseDown
    If e.Button = MouseButtons.Right Then
        MessageBox.Show("MouseDown 右")
    End If
End Sub
図4 MouseDownイベントと引数e(フォームを右クリックしたときにメッセージ表示)

従来のVBで言えばButtonやShiftやXやYにあたるものをパックしたのがEventArgsを継承したMouseEventArgsクラスのeです。 VB.NETではホイール関連もサポートされています。とにかく、eはそういうった情報をひとまとめにしたものです。 これによりどのイベントでも基本的に2つの引数 - senderとe - を持つという普遍的なパターンができ上がり実にわかりやすくなりました。 (ま、標準の.NETクラスライブラリ以外でいろいろなユーザによって作られるイベントがこれに則っている保証はありませんが。)

さて、図4でお気づきになったでしょうか。Handles句に続いている文字列は、MyBase.MouseDownとなっています。 図4ではフォームのイベントを処理していますが、VB.NETでのWindowsアプリの基本パターンは、 連載の第2〜4回の「NET Framework SDKを使ったVB.NETプログラム作成の流れ」にあるように Formクラスを継承してアプリ用のフォームを作ります。このとき作成中のクラス自身は変数として宣言していないので、 自分自身のイベントではなく、親クラス(MyBaseキーワード)のイベントを処理の対象とします。 図2などの場合は、変数宣言部分は省略していますが、実際には、 以下のように変数Button1はWithEvents付きで宣言しておく必要があります。

Friend WithEvents Button1 As Button 

Handles句を使ったイベント処理は以上です。実はこのHandles句、WithEventsを使わない別のイベント処理方法もあります。 それを次に紹介します。

2.AddHandler

Handles句を使ったイベント処理は、一度イベントを処理すると決めたら、アプリ実行中に処理をしないようにすることはできません。 つまり、静的であり、コーディング時にイベント処理をする/しないが決まってしまいます。 また、多数オブジェクトのイベントを1つのプロシージャで処理する場合、Handlesの後にぞろぞろとButton1.Click, Button2.Click, ・・・ と続くことになり、数が多くなってくると不便です。 そんなときには、AddHandlerキーワードです。使い方を見てみましょう。 以下はFormを継承したクラス中に書いた内の今回の説明に関連する部分の抜粋です。(行番号は便宜上付けているものでコードの一部ではありません)

   1:Friend textBox1  As TextBox() = New TextBox(29){}
   2:
   3:Public Sub New()
   4:    Dim i As Integer
   5:    For i = 0 To 29
   6:        textBox1(i) = New TextBox()
   7:        textBox1(i).Size = New Size(20,20)
   8:        textBox1(i).Location = New Point((i mod 5)*20, (i \ 5) * 20)
   9:        textBox1(i).Text = CStr(i+1)
  10:        Me.Controls.Add(textBox1(i))
  11:        AddHandler textBox1(i).KeyPress, AddressOf textBox1_KeyPress
  12:    Next
  13:
  14:End Sub
  15:
  16:Private Sub textBox1_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
  17:    e.Handled = True
  18:End Sub
図5 AddHandlerの使用方法

ここでやっている処理の概要をまず説明します。横に5つ、縦に6つTextBoxを並べ、 最初のTextBoxを1として順に30までの数字を表示します。その際、それぞれのTextBoxには英数記号等を入力不可なように KeyPressイベントで制御します。(あくまでAddHandlerの使用方法の1例を示しているだけなので、イベント内の処理は適当です)

1行目をご覧下さい。Handles句を使った場合と違って、WithEventsキーワードは不要になっています。 この行の意味は、TextBox30個分(0〜29)の配列を確保しています。 続いて、3〜14行がこのフォームのコンストラクタ(クラスからインスタンスを生成する際に最初に実行されるプロシージャ)です。 この中で、それぞれのTextBoxに位置やサイズや表示するTextの設定をしています。 その辺は今回の本題ではないので説明しません。Size、Location、TextプロパティやPoint、Size構造体をヘルプで調べてください。 11行目、本題のAddHandlerを使用している行です。使用方法は以下の通りです。

AddHandler  オブジェクト.イベント名, AddressOf イベントを処理するプロシージャ名 

AddressOf演算子はVB5から導入された演算子でプロシージャのアドレスを取得できます。 AddHandlerキーワードで、イベントとプロシージャのアドレスを関連付けています。 こうするとイベント発生時には、ここで指定したプロシージャが呼び出されます。 関連付けたイベントはRemoveHandlerキーワードで解除することができるため、 動的にイベントを受け付ける/受け付けないを切り替えることができます。

以下に、HandlesとWithEventsではなく、AddHandlerを使う1例を示します。

Imports Microsoft.Win32
〜略〜
AddHandler SystemEvents.SessionEnding, AddressOf system_SessionEnding
〜略〜
Private Sub system_SessionEnding(ByVal sender As Object, ByVal e As SessionEndingEventArgs)
    If e.Reason = SessionEndReasons.Logoff Then
        MessageBox.Show("ログオフしようとしています。キャンセルします。")
        e.Cancel = True
    End If
End Sub

Windowsが終了しようとしている、または、 ユーザがログオフしようとしているときに発生するシステムのイベントを持っているSystemEventsクラスを利用する場合です。 このクラスのイベントはすべてSharedなイベントなので、特にインスタンスを生成する必要はありません。 これでWindowsの終了、ログオフを検知することができます。

3.まとめ

  1. Handlesキーワードの使用
    • イベント用プロシージャ名が固定でない
    • イベント発生元のオブジェクト変数はWithEvents付きで宣言する必要がある
  2. イベント用プロシージャの引数の変更
    • 基本はsenderとeの2つ
    • senderはイベント発生源のオブジェクト。型はObject
    • eはイベント関連データをパックしたもの。型はEventArgsクラス、または、EventArgsを継承したクラス
  3. AddHandlerとRemoveHandlerで動的にイベント処理を受け付ける/受け付けないを切り替えられる
VB.NETのイベントの取り扱い方は以上です。

ホーム > KEN's .NET > [特集2] VB.NETのイベントの扱い方

[e-mail] yone_ken00@hotmail.com