Visual Basic 初級講座 |
Visual Basic 中学校 > 初級講座 >
第10回 イベントプロシージャ
すでにみなさんご存知だと思いますが、VBではすべて「イベント」が発生したときにプログラムが実行されます。イベントが発生していないのにプログラムを動かすことはできません。このような形態を「イベントドリブン」と呼ぶののでした。
今回はこのことをもう少し掘り下げて、いろいろなイベントに対応したプログラムを書けるようになっていただくことが目的です。
説明の順番は、基本→いくつかのイベントを例としたイベントプロシージャの利用法の説明→高度なイベントプロシージャの利用法となります。
この回の要約 ・イベントプロシージャの第2引数の使い方 ・MouseEnter, MouseLeaveイベントを使ってマウスが来るとボタンが赤くなるようにする ・MouseMoveイベントを使ってマウスで絵を描くプログラムをたった6行でつくる ・KeyPressイベントを使ってテキストボックスにキーボードから数字以外入力できなくする ・AddHandlerを使うとイベントとプロシージャを動的に結びつけることができる。 |
VBではイベントが発生した場合、自動的にそのイベントに対応した「イベントプロシージャ」が呼び出されます。イベントプロシージャはプロシージャの一種ですが、他のプロシージャとは違う2つの特徴があります。
よく見かけるボタンのクリックイベントプロシージャを見ながら、以下の説明を読んでみてください。
Private
Sub Button1_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles Button1.Click End Sub |
■リスト1:Clickイベントプロシージャ
1つ目はイベントとプロシージャを結びつけるHandlesキーワードがあることです。ただし、後で説明しますが、Handlesキーワードは必ず必要というわけではありません。
2つ目は引数です。イベントプロシージャには必ず2つの引数があり、第1引数はObject型、第2引数はEventArgs型またはEventArgsに類似した型です。「類似した型」とはあいまいな説明ですが、ちゃんと説明すると第2引数はEventArgsクラスまたはその派生クラスです。この講座では「派生クラス」というものをまだ説明していないのでここではあいまいなまま「類似した型」と表現しておきます。
この2つの要件を満たしているものはすべてイベントプロシージャです。
第1引数はイベントを発生させたクラスが渡されてきます。リスト1のClickイベントプロシージャの場合第1引数のsenderにはButton1がセットされています。そのため、イベントプロシージャ内で sender.Text = "Hello!" などと書くと、 Button1.Text = "Hello!" と同じ意味になります。
第2引数はイベントの追加情報が渡されてきます。どのような追加情報があるかはイベントによってさまざまですので、後でいくつか紹介するイベントの所で説明します。追加情報がイベントごとに異なるため第2引数の型もイベントごとに決まっています。
なお、Clickイベントには何の追加情報もありませんので、第2引数には意味がありません。
プログラムは全てキーボードから打ち込むことができるので、イベントプロシージャもすべてを自分で書くこともできますが、一般的にはVBの便利なIDEを利用してイベントプロシージャを自動作成します。
イベントプロシージャを作る代表的な方法を表にまとめてみました。
コントロールをダブルクリックする | 最も簡単な方法ですが、既定のイベントプロシージャしか作成できません。たとえば、ボタンならこの方法で作成できるのはClickイベントプロシージャだけです。 |
クラス名ボックスでコントロール名を選択した後で、メソッド名ボックスでイベント名を選択する。 | 最も一般的な方法です。字で書くと説明がわかりにくいのですが、実際にやってみると手軽さがわかります。→この方法の詳しい説明は入門講座第4回 イベントを逃すなを参照してください。また、下の画像を見ればなおわかりよいと思います。 |
プロパティウィンドウのイベント一覧から目的のイベントをダブルクリックする。 | VB2005から登場した新しい方法です。VB2005ではプロパティウィンドウにイベントも表示されるようになります。今後はこの方法が主流になっていくものと思います。 |
■表1:イベントプロシージャの作り方
上記の方法でつくると、引数も正しく、Handlesキーワードもちゃんとなっているしっかりしたイベントプロシージャを作ることができます。
■画像1:メソッド名ボックスでイベント名を選択する
さて、これでどんなイベントプロシージャでも作り出すことができるようになりました。
いよいよ実際にイベントを使ってみましょう。フォームのLoadイベントやボタンのClickイベントを今までに何度もでてきているので、今回はいままで登場しなかったイベントに注目してみます。
以下の説明は読むだけでは単なるイベントの紹介になっていしまいます。是非実際にプログラムして、イベントプロシージャの作り方や引数の使い方を体感してみてください。
MouseEnterイベント (読み方:MouseEnter = マウスエンター) はコントロールの外側にあったマウスカーソルがコントロールの上に入ったときに発生します。
次のコードはマウスがボタンの上に来たときにボタンの色を薄い赤にします。
Private
Sub
Button1_MouseEnter(ByVal
sender As
Object,
ByVal e
As System.EventArgs) Handles
Button1.MouseEnter Button1.BackColor = Color.Pink End Sub |
■リスト2:MouseEnterイベントプロシージャ
Button1.BackColorはsender.BackColorと書いても同じです。
実行して試してみてください。
試してみると分かるのですが、確かにマウスがボタンの上に来るとボタンの色が薄い赤になります。しかし、マウスが離れても色は元に戻らないのでこのままではあまり実用的ではありません。マウスが離れていったときに元の色に戻すには次に説明するMouseLeaveイベントを使います。
MouseLeaveイベント (読み方:MouseLeave = マウスリーブ) は今までそのコントロールの上にあったマウスカーソルがコントロール外に離れていったときに発生します。
MouseEnterイベントの例と同じボタンに、次のようにプログラムすると、ボタンの上にマウスカーソルがあるときだけボタンの色が変わるようにすることができます。
Private
Sub
Button1_MouseLeave(ByVal
sender As
Object,
ByVal e
As System.EventArgs) Handles
Button1.MouseLeave Button1.BackColor = Color.FromKnownColor(KnownColor.Control) End Sub |
■リスト3:MouseLeaveイベントプロシージャ
イベントとは関係ありませんが、このコードでは、色を「コントロールの標準色」にするためにKnownColor構造体(読み方:KnownColor = ノウンカラー)を使う方法で色指定をしています。これは「コントロールの標準色」が環境によって異なるからです。Windows98とWindowsXPではフォームの灰色の濃さが違いますよね。それに同じWindowsXPでもディスプレイのプロパティで設定を変えればコントロールの標準の色をいろいろと変えられます。それで「コントロールの標準色」を表現するには普通の色指定とは異なる方法が必要となるのです。
MouseMoveイベントはコントロールの上をマウスが移動したりクリックしたときに発生します。このイベントは第2引数の型がEventArgsクラスではなく、MouseEventArgsクラス (読み方:MouseEventArgs = マウスイベントオーグス)です。つまりこのイベントには追加情報があるのです。
具体的には、コントロール内の座標でのマウスのX座標、Y座標、およびマウスが移動したときに押されていたマウスボタンを取得することができます。
Button | 押されていたマウスのボタン |
X | コントロール座標系でマウスのX座標 |
Y | コントロール座標系でマウスのY座標 |
■表2:MouseMoveイベントで利用できるMouseEventArgsクラスのプロパティ
たとえば、次のプログラムでは、マウスの移動した座標に赤い円を描きます。マウスが移動するたびに円を描くのであたかもマウスの軌跡に沿って赤い線を書いているようになります。ピクチャーボックスを貼り付けてお絵かきを楽しんでみてください。
Private
Sub
PictureBox1_MouseMove(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles PictureBox1.MouseMove Dim g As Graphics = sender.CreateGraphics() g.FillEllipse(Brushes.Red, e.X, e.Y, 10, 10) End Sub |
■リスト4:MouseMoveイベントプロシージャ
このプログラムでは円を書く座標は、マウスが移動した座標です。この座標を知るには第2引数eのXプロパティとYプロパティを使います。
今度はただ円を描くのではなく、マウスの左ボタンをクリックした状態で移動したときは赤い円、右ボタンをクリックした状態で移動したときは青い円を描くようにしています。押されていたボタンを取得するにはMouseEventArgsクラスのButtonプロパティを使用します。
これだけでシンプルなお絵かきソフトの完成です。
■画像2:リスト5だけでこのような簡易お絵かきソフトが作れる。
Private
Sub
PictureBox1_MouseMove(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.MouseEventArgs)
Handles PictureBox1.MouseMove Dim g As Graphics = sender.CreateGraphics() If e.Button = MouseButtons.Left Then g.FillEllipse(Brushes.Red, e.X, e.Y, 10, 10) ElseIf e.Button = MouseButtons.Right Then g.FillEllipse(Brushes.Blue, e.X, e.Y, 10, 10) End If End Sub |
■リスト5:シンプルなお絵かきプログラム
これだけのプログラムで、きわめてシンプルとはいえお絵かきプログラムが作れてしまうのですからVBはすごいです。自分で書くのはたった6行ですよ。
今度はうってかわってキーボードからの入力を制御するイベントです。
KeyPressイベント (読み方:KeyPress = キープレス)はキーボードから文字が入力されたときに発生します。(バックスペースキーでも発生します)。
このイベントプロシージャの第2引数はKeyPressEventArgs型 (読み方:KeyPressEventArgs = キープレスイベントオーグス)で、入力されたキーに関する情報が取得できます。また、このKeyPressEventArgs型のHandledプロパティ(読み方:Handled = ハンドルド)は読み取りだけではなく、設定することもでき、Trueを設定した場合、キーボードからの入力をキャンセルすることができます。このようにイベントプロシージャの追加情報は単なる情報ではなく、こちらからセットする情報を含んでいる場合もあります。
このことを利用して、テキストボックスに数字以外入力できないようにするには次のようになります。
Private
Sub
TextBox1_KeyPress(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.KeyPressEventArgs)
Handles
TextBox1.KeyPress If IsNumeric(e.KeyChar) = False Then e.Handled = True End If End Sub |
■リスト6:KeyPressイベントプロシージャ
KeyCharプロパティ(読み方:KeyChar = キーキャラ)は入力された文字を表しています。たとえば、キーボードのAが押された場合KeyCharプロパティの値は"A"です。なお、当然"A"と"a"は区別されます。
IsNumeric関数(読み方:IsNumeric = イズニューメリック)は引数が数値として解釈可能な場合True、そうでない場合はFalseを返します。
つまり、このプログラムは「KeyCharが数値でない場合HandledをTrueにしろ」ということになります。もう少し日本語に訳すと「入力された文字が数値でない場合は入力をキャンセルしろ」ということです。
なお、これはあくまでもKeyPressイベントプロシージャなので、マウスからの貼り付けや、[Shift] + [Insert]キーによる貼り付けが行われた場合は、数値以外の文字も入力できてしまいます。
それでは一般論に話を戻します。
今までの説明ではイベントとイベントプロシージャは1対1で結びついていましたが、複数のイベントを1つのイベントプロシージャに結びつけることもできますし、逆に1つのイベントを複数のイベントプロシージャに結びつけることもできます。
たとえば、すぐ前にでてきた、キーボード入力でテキストボックスに数値しかできないようにする例でTextBox1だけでなく、TextBox2でも同じ処理を行いたい場合、同じプログラムをもう一度書く必要はありません。次のようにHandles句 に追加するだけでTextBox2でも同じことができるようになります。
Private Sub
TextBox_KeyPress(ByVal
sender As
Object,
ByVal e
As
System.Windows.Forms.KeyPressEventArgs)
Handles
TextBox1.KeyPress, TextBox2.KeyPress If Not IsNumeric(e.KeyChar) Then e.Handled = True End If End Sub |
■リスト7:2つのイベントに対応するイベントプロシージャ
この場合、プロシージャ名がTextBox1_KeyPressでは、TextBox1しか処理しないような印象があるので、名前も1を取って変えてしまいました。このようにイベントプロシージャの名前は自由に変えることができます。
ところで、こうしてしまってもイベントが発生したのがTextBox1でキー入力があったからなのか、TextBox2でキー入力があったからなのか判断することが可能です。ここで初めて第1引数が意味を持ってくるのです。イベントプロシージャの第1引数にはイベントを発生させたクラスが渡されてくることを思い出してください。
さて、これを逆に考えて2つのイベントプロシージャにHandles TextBox1.KeyPress と書けば、1つのKeyPressイベントに対して2つのイベントプロシージャが結び付けられることになります。このとき2つのプロシージャの名前は自由でよいのですが、引数は必ず2つで1つ目はObject型、2つ目はKeyPressEventArgs型にする必要があることを忘れないで下さい。
ただし、特別な事情がない限り1つのイベントは1つのプロシージャで処理することをお勧めします。そうしないと大変わかりにくいプログラムになってしまいますから。
1つのイベントは1つのプロシージャで処理 1つのプロシージャでは1つのイベントを処理 |
わかりやすい。単純明快。 | 良 |
1つのイベントは1つのプロシージャで処理 1つのプロシージャでは複数のイベントを処理 |
便利。ちょっぴりわかりずらい。 | ↑ |
1つのイベントを複数のプロシ−ジャで処理 1つのプロシージャでは1つのイベントを処理 |
無意味。意外とわかりずらい。 | ↓ |
1つのイベントを複数のプロシージャで処理 1つのプロシージャでは複数のイベントを処理 |
複雑。わかりにくい。…でも、便利。 | 悪 |
■表3:プログラムのわかりやすさとイベントプロシージャの関係
次にイベントとプロシージャを動的に結びつける方法を説明します。「動的」というのは「状況に応じて」ということです。あらかじめHandles句を使ってイベントとプロシージャを結びつける方法では「状況に応じて」というのは無理です。
動的に結び付けるにはAddHandlerステートメントとRemoveHandlerステートメントを使います。(読み方:AddHandler = アドハンドラー、RemoveHandler = リムーブハンドラー)。
AddHandlerステートメントはイベントとプロシージャを結び付けます。
たとえば、MyClickプロシージャとButton2のClickイベントを結び付けるには次のようにします。
フォームにButton1とButton2を貼り付けてから入力してみてください。
Private Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click AddHandler Button2.Click, AddressOf MyClick End Sub |
Private
Sub MyClick(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs)
MsgBox(sender.name) End Sub |
■リスト8:イベントとプロシージャを動的に結びつける
当然、MyClickプロシージャはClickイベントとしての引数が必要です。このプログラムを実行するとはじめはButton2をクリックしても何もおきないのに、Button1をクリックしてからButton2をクリックするとMyClickプロシージャが呼び出されるようになります。
プログラム中AddressOf(読み方:AddressOf = アドレスオブ)という見かけないキーワードが登場しますが、このキーワードはちょっと難しいのでここでは説明しません。今はAddHandlerやRemoveHandlerでプロシージャを指定する場合はAddressOfを使うものと覚えておいてください。
次の例では、はじめButton1をクリックしたときは「はじめ」と表示されますが、それ以降は「2回目以降」と表示されます。
Private Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click MsgBox("はじめ") RemoveHandler Button1.Click, AddressOf Button1_Click AddHandler Button1.Click, AddressOf MyClick End Sub |
Private
Sub MyClick(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs)
MsgBox("2回目以降") End Sub |
■リスト9:イベントとプロシージャの結びつきを解除・再設定する
この例ではButton1_ClickプロシージャにはHandles句があるので、はじめはButton1をクリックするとこのプロシージャが呼び出されます。ところが、このプロシージャの中でRemoveHandlerステートメントが使用されていて、Button1のClickイベントとButton1_Clickプロシージャの結びつきが削除されてしまいます。そして、あらたにAddHandlerステートメントを利用してButton1のClickイベントはMyClickプロシージャに結び付けられるわけです。
これらの機能も濫用するとわかりにくいプログラムができあがってしまいますので、使用する場合は十分に検討してからにしてください。
イベントプロシージャはイベントが発生したときに自動的に呼び出されますが、プロシージャである以上、自分で呼び出すこともできます。次のプログラムはButton1がクリックされたときにButton2のClickイベントプロシージャを呼び出します。
Private
Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click Call Button2_Click(Button1, e) End Sub |
■リスト10:イベントプロシージャを自分で呼び出す
この例では律儀に2つの引数をちゃんと渡していますが、何も渡さないということも可能です。何も渡さない場合は次のようにキーワードNothing(読み方:Nothing = ナッシング)を指定します。
Private
Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click Call Button2_Click(Nothing, Nothing) End Sub |
■リスト11:イベントプロシージャを自分で呼び出す
この場合、Button2_Clickプロシージャ内で引数を利用している場合、エラーになる可能性がありますので注意してください。
イベントの種類は大変多いのですべてのイベントを紹介することは到底不可能です。しかし、どんなイベントであってもイベントプロシージャを作ることは簡単だということが分かっていただけたと思いますし、イベントプロシージャの引数の利用方法も今回説明した内容でカバーできるはずです。
イベントの使い方がわかったろことでMSDNライブラリを紐解いて(ひもといて)、便利なイベントを探してみましょう。
我々はレストランでメニューを選ぶときのような感覚で好きなイベントを選ぶことができるのです。