Microsoft .NET へのアップグレード
Mike Gunderloy
Lark Group, Inc.
February 2002
日本語版最終更新日 2002 年 9 月 19 日
要約: Visual Basic .NET でのイベントの発生と応答について、また Visual Basic .NET におけるイベント ハンドラのイベントへの柔軟な関連付けを利用する方法について説明します。これには、イベント ハンドラの実行時でのイベントへの動的な関連付けが含まれます。
目標
- Microsoft® Visual Basic® .NET におけるイベント処理を理解する。
- Visual Basic .NET コードを使ってイベントを発生させる。
- Visual Basic .NET コードを使ってイベントに応答する。
前提条件
このドキュメントを有効に活用するためには、以下の条件が必要です。
- Visual Basic プログラミングについての知識があること。
- イベント ドリブン プログラミングの基本的な概念を理解していること。
- Visual Basic .NET にアクセスできること。
目次
イベント ドリブン プログラミング
イベントの実装と応答の練習
Visual Basic 6.0 との違い
要約
イベント ドリブン プログラミング
Microsoft® Windows® が登場する以前は、多くのプログラムが厳密な手続き型のデザインを持っていました。ユーザーがプログラムを起動すると、プログラムは何らかの仕事を行い、その仕事が終わるとコマンド プロンプトが表示され、ユーザーは別のプログラムを実行できるようになります。
Windows では、イベント ドリブン プログラミングという新しいプログラミング パラダイムが優勢になりました。ユーザーがアプリケーションに対する操作を行うと、イベントが発生します。たとえば、コマンド ボタンをクリックすると、ボタンの Click イベントが発生します。これらのイベントには、イベント ハンドラと呼ばれるコード セクションが反応します。イベントは、アプリケーションの 1 つの部分から別の部分へのシグナルのようなものです。イベントに応答するコードをどう書くかは、開発者に任されています。ハンドラがないイベントは、単に Windows によって破棄されます。
このドキュメントでは、Visual Basic .NET を使って、イベント コントラクトの両方の側の処理を行う方法について説明します。Visual Basic .NET では、イベントの発生とイベントへの応答の両方を行うことができます。また、Visual Basic .NET は Visual Basic 6.0 よりもはるかに高い柔軟性を備えており、イベントとイベント ハンドラの関連付けを実行時に変更することもできます。
イベント ソース
イベント ソース (イベント送信者と呼ばれることもあります) は、イベントが生じたことをシグナルとして知らせることができる任意のオブジェクトのことを指します。このシグナルをブロードキャストすることは、イベントの発生と呼ばれます。.NET Framework クラス ライブラリの多くのクラスと、Windows フォームおよび Web フォーム パッケージのユーザー インターフェイス コンポーネントがイベントを発生させることができます。図 1 は、一般に Windows フォーム上のすべてのコントロールに継承されている System.Windows.Forms.Control クラスのイベントの例を示しています。
図 1. System.Windows.Forms.Control クラスのイベント
Visual Basic .NET では、プログラマが独自のイベント ソースを作成することができます。クラスまたはインターフェイスに独自のイベントを実装するためには、次の 2 つのステップを実行します。
-
Event キーワードを使ってイベント シグニチャを宣言します。
-
RaiseEvent ステートメントを使ってイベントを発生させます。
イベントを宣言するには、クラス モジュール (またはインターフェイス) の宣言セクションで Event キーワードを使用します。イベントはつねにパブリックとして宣言されます。イベントは引数リストを持つことができますが、オプションの引数、ParamArray の引数、または戻り値を持つことはできません。たとえば、ConnectFailed という名前のイベントは次のように宣言します。
Public Event ConnectFailed(ByVal Reason As Integer)
ConnectFailed イベントを発生させるには、クラスの中で RaiseEvent キーワードを使用します。
RaiseEvent ConnectFailed(1)
イベントの引数リストは、イベントを発生させるコードとイベントに応答するコードの間での双方向の通信に使用できます。特に、通常は Cancel という名前が付けられる Boolean 引数を実装し、イベント ハンドラからイベントを発生させているコードに対し、現在の処理を停止するように指示することができます。このようなイベントは次のように宣言することができます。
Public Event ShutDown (ByRef Cancel As Boolean)
Cancel 引数は、イベントを発生させているコードがイベント ハンドラによって設定された値に応答できるように、参照によって渡さなくてはならないことに注意してください。
イベント受信者
イベント ハンドラと呼ばれることもあるイベント受信者は、イベントに応答するサブルーチンです (イベントは戻り値を持つことができないので、イベント ハンドラは関数であってはなりません)。一般に、イベント ハンドラの名前としては、オブジェクトの名前にイベントの名前を付けたものが使用されますが、これは必須ではありません。
たとえば、Customer という名前のクラスで ConnectFailed イベントを宣言し、CurrentCustomer という名前の Customer クラスのインスタンスを宣言したとします。ConnectFailed イベントのイベント ハンドラは、次のように宣言できます。
Protected Sub CurrentCustomer_ConnectFailed( _
ByVal Reason As Integer)
イベント ハンドラへのイベントのフック
Visual Basic .NET に対し、特定のイベント ハンドラが特定のオブジェクトからの特定のイベントを処理するように指示するためには、イベント ハンドラをイベントに関連付ける必要があります。このための方法は 2 つあります。第 1 に、WithEvents ステートメントと Handles 句を使って、コンパイル時に関連付けをセットアップすることができます。第 2 に、AddHandler ステートメントを使って、実行時に関連付けをセットアップできます。
オブジェクトからのイベントを処理するには、次の例のように、オブジェクトを WithEvents キーワードを指定して宣言します。
Private WithEvents CurrentCustomer As Customer
このクラスのイベントに特定のイベント ハンドラを関連付けるには、イベント ハンドラを Handles 句を指定して宣言します。
Protected Sub CurrentCustomer_ConnectFailed(_
ByVal Reason As Integer) _
Handles CurrentCustomer.ConnectFailed
このようにイベント ハンドラを宣言することで、イベントとハンドラの間の関連付けをコンパイル時に指定することができます。また、これよりも柔軟な AddHandler ステートメントを使って、イベントとイベント ハンドラの間の関連付けを実行時に指定することもできます。
Protected Sub CurrentCustomer_ConnectFailed(_
ByVal Reason As Integer)
・
AddHandler CurrentCustomer.ConnectFailed, _
AddressOf CurrentCustomer_ConnectFailed
AddHandler を使ってイベントをフックする場合、イベント プロシージャは Handles 句を持っていてはなりません。また、イベントとイベント ハンドラの間の関連付けを切断する RemoveHandler ステートメントもあります。
RemoveHandler CurrentCustomer.ConnectFailed, _
AddressOf CurrentCustomer_ConnectFailed
COM ソースからのイベントの処理
Visual Basic .NET コードがレガシー COM コンポーネントと相互運用を行う場合には、.NET コードの中で COM コンポーネントからのイベントを自由に処理することができます。このためには、以下のいずれかの方法で、ランタイム呼び出し可能ラッパー (RCW) を入手または生成する必要があります。
- COM コンポーネントのベンダから、プライマリ相互運用アセンブリを入手する。
- TLBIMP コマンド ライン ツールを使って、COM コンポーネントの RCW を作成する。
- Visual Basic .NET プロジェクトで、.NET プロジェクトから COM コンポーネントへの参照を直接に設定する。
どのように作成した場合でも、RCW によって COM クラスのすべてのイベントが.NET コードから利用可能になります。これらのイベントは、マネージ コードで発生させたイベントと同じように、Handles または AddHandler を使用してハンドラに関連付けることができます。
イベントとデリゲート
イベントとイベント ハンドラの間の接続は、実際にはデリゲートと呼ばれる特殊なオブジェクトによって実装されています。デリゲートは、他のオブジェクトのメソッドを呼び出すために使用できるオブジェクトです。デリゲートは C++ などの言語の関数ポインタに似ています。ただし、デリゲートは自分が扱えるオブジェクトの型を指定することで、任意の関数をポイントできる場合の管理上の問題を回避しています。このため、デリゲートはタイプセーフな関数ポインタと呼ばれることもあります。
イベントを Event キーワードで宣言すると、Visual Basic .NET は EventNameEventHandler という名前のデリゲート クラスを暗黙のうちに作成します。たとえば、ConnectFailed イベントは、ConnectFailedEventHandler という名前のデリゲートを自動的に作成します。
デリゲート オブジェクトは、AddHandler ステートメントで明示的に使用することもできます。通常、このためにはデリゲートの名前を完全修飾する必要があります。たとえば、ConnectFailed イベントが Sales という名前の Customer クラスのメンバである場合、次の 2 つのステートメントは等価です。
AddHandler CurrentCustomer.ConnectFailed, _
AddressOf CurrentCustomer_ConnectFailed
AddHandler CurrentCustomer.ConnectFailed, _
New Sales.Customer.ConnectFailedEventHandler( _
AddressOf CurrentCustomer_ConnectFailed)
生成されたコードを ILDASM を使って確認すればわかるように、この 2 つの形式の AddHandler ステートメントの間には、実装上もパフォーマンス上も違いはありません。ほとんどの場合は、短い方の形式を使用し、Visual Basic .NET にデリゲート オブジェクトを暗黙のうちに処理させるべきです。唯一の例外は、複数のイベントで 1 つのデリゲート型を共有したい場合です。この場合には、イベントの宣言で、明示的なデリゲート型を使用するように指定します。
Public Event EventName As DelegateType
この場合には、Visual Basic .NET が正しいデリゲート型を使用するように、長い形式の AddHandler ステートメントを使用します。
イベントの実装と応答の練習
次の例では、2 つのイベントを発生させるクラスを含んだクラス ライブラリを作成します。その後、これらのイベントに応答できるユーザー インターフェイス アプリケーションを作成します。Handles を使ったコンパイル時のイベント関連付けと、AddHandler を使った実行時のイベント関連付けの両方を使用します。
クラス ライブラリの作成
次の操作を行って、イベントを発生させるクラス ライブラリを作成します。
- Visual Studio .NET を開き、スタート ページの [新しいプロジェクト] をクリックします。
- 画面の左側のツリー ビューで、[Visual Basic プロジェクト] をクリックします。
- [クラス ライブラリ] をクリックして、これをプロジェクト テンプレートとして選択します。
- アプリケーションの名前を Water に設定し、[OK] をクリックしてプロジェクトを作成します。
- [ソリューション エクスプローラ] ウィンドウで Class1.vb という名前のクラスをクリックし、名前を Bucket.vb に変更します。
- Bucket.vb の Class1 のコードを選択し (空のクラス定義になっているはずです)、次のコードで置き換えます。
Public Class Bucket
Private ContentsValue As Integer
Private CapacityValue As Integer
Private NameValue As String
Public Event Full()
Public Event Overflowing(_
ByVal sender As System.Object)
Public Sub New(ByVal Capacity As Integer)
mintCapacity = Capacity
mintContents = 0
End Sub
Public Sub Empty()
mintContents = 0
End Sub
Public ReadOnly Property Contents()
Get
Contents = mintContents
End Get
End Property
Public Property Name() As String
Get
Name = mstrName
End Get
Set(ByVal Value As String)
mstrName = Value
End Set
End Property
Public Sub Add(ByVal Amount As Integer)
mintContents = mintContents + Amount
If mintContents > mintCapacity Then
RaiseEvent Overflowing(Me)
mintContents = mintCapacity
ElseIf mintContents = mintCapacity Then
RaiseEvent Full()
End If
End Sub
End Class
このコードは、水を入れることができるバケツを表す Bucket という名前のクラスを作成します。New メソッドは、バケツを初期化し、指定された容量を持たせます。Name プロパティはバケツに名前を割り当てます。Add メソッドはバケツに水を追加し、Empty メソッドはバケツを空にします。
Bucket クラスには 2 つのイベント宣言が含まれています。
Public Event Full()
Public Event Overflowing(_
ByVal sender As System.Object)
この 2 つのイベントは、どちらも Add メソッドの中で発生します。バケツの中の水の量がバケツの容量を超えると、コードは Overflowing イベントを発生させます。一方、バケツの中の水の量がバケツの容量にちょうど等しい場合、コードは Full イベントを発生させます。
2 つのイベントの引数リストが異なることに注意してください。個々のイベントには、自由に情報を渡すことができます。
- [ビルド] メニューの [ビルド] をクリックするか、[Ctrl+Shift+B] キーを押して、クラス ライブラリをビルドします。
ユーザー インターフェイス プロジェクトの作成
次の操作を行って、Bucket クラスのイベントをテストするための Windows アプリケーションを作成します。
- Microsoft® Visual Studio® .NET を開き、スタート ページの [新しいプロジェクト] をクリックします。
- 画面の左側のツリー ビューで、[Visual Basic プロジェクト] をクリックします。
- [Windows アプリケーション] をクリックして、これをプロジェクト テンプレートとして選択します。
- アプリケーションの名前を BucketTest に設定し、[OK] をクリックしてプロジェクトを作成します。
- [ソリューション エクスプローラ] ウィンドウで Form1.vb という名前のフォームをクリックし、名前を frmBuckets.vb に変更します。
- 表 1 に従って適切なコントロールを追加し、これらのコントロールのプロパティを設定して、図 2 に示したフォームを作成します。
表 1. frmDragDrop.vb のコントロール
| コントロール タイプ | プロパティ | 値 |
| Form | Text | Buckets |
| Label | Text | Bucket 1 |
| Button | Name | btnAdd1 |
| | Text | Add |
| ProgressBar | Name | pb1 |
| | Maximum | 10 |
| Label | Name | lblFull1 |
| | Text | FULL |
| | Visible | False |
| Label | Text | Bucket 2 |
| Button | Name | btnAdd2 |
| | Text | Add |
| ProgressBar | Name | pb2 |
| | Maximum | 10 |
| Label | Name | lblFull2 |
| | Text | FULL |
| | Visible | False |
| Label | Text | Bucket 3 |
| Button | Name | btnAdd3 |
| | Text | Add |
| ProgressBar | Name | pb3 |
| | Maximum | 10 |
| Label | Name | lblFull3 |
| | Text | FULL |
| | Visible | False |
| ListBox | Name | lboOverflow |
図 2. Bucket クラス イベントをテストするためのフォーム
イベントを処理するためのコードの追加
これで、Bucket クラスのイベントを処理するコードを作成する準備ができました。このコードは Bucket クラスのオブジェクトを 3 つ作成します。Full イベントは、Handles を使って、コンパイル時に個別にイベント ハンドラに接続されます。Overflow イベントは、実行時に AddHandler を使ってイベントに動的に関連付けられる 1 つのプロシージャによって処理されます。
[表示] メニューの [コード] をクリックし、Windows Form Designer が生成したコードの前に次のコードを入力します。
Dim WithEvents Bucket1 As New Water.Bucket(10)
Dim WithEvents Bucket2 As New Water.Bucket(10)
Dim WithEvents Bucket3 As New Water.Bucket(10)
また、Windows Form Designer が生成したコードの後に次のコードを入力します。
Private Sub Form1_Load(_
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Bucket1.Name = "Bucket 1"
Bucket2.Name = "Bucket 2"
Bucket3.Name = "Bucket 3"
AddHandler Bucket1.Overflowing, _
AddressOf HandleOverflow
AddHandler Bucket2.Overflowing, _
AddressOf HandleOverflow
AddHandler Bucket3.Overflowing, _
AddressOf HandleOverflow
End Sub
Private Sub btnAdd1_Click(_
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnAdd1.Click
Bucket1.Add(1)
pb1.Value = Bucket1.Contents
End Sub
Private Sub btnAdd2_Click(_
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnAdd2.Click
Bucket2.Add(1)
pb2.Value = Bucket2.Contents
End Sub
Private Sub btnAdd3_Click(_
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnAdd3.Click
Bucket3.Add(1)
pb3.Value = Bucket3.Contents
End Sub
Private Sub Bucket1_Full() Handles Bucket1.Full
lblFull1.Visible = True
End Sub
Private Sub Bucket2_Full() Handles Bucket2.Full
lblFull2.Visible = True
End Sub
Private Sub Bucket3_Full() Handles Bucket3.Full
lblFull3.Visible = True
End Sub
Private Sub HandleOverflow(_
ByVal sender As System.Object)
lboOverflow.Items.Add(sender.Name & " Overflow!")
End Sub
このコードでは次のような処理を行っています。
- Form Load の中のコードは、この 3 つの Bucket オブジェクトの Name プロパティに値を代入しています。また、AddHandler を呼び出して、すべての Overflowing イベントを HandleOverflow プロシージャに渡しています。この例からわかるように、1 つのイベント ハンドラは、すべてのイベントが互換性のある引数リストを持っている限り、複数のイベントを処理することができます。
- Add プロシージャは、対応する Bucket オブジェクトの Add メソッドを呼び出し、バケツに 1 単位の水を追加します。また、ProgressBar コントロールを更新して、バケツの状態を画面上に表示します。
- Full プロシージャは、Full イベントのイベント ハンドラです。これらのプロシージャは、対応する "Full" のラベルをユーザー インターフェイス上に表示します。
- HandleOverflow イベント プロシージャは、すべての Overflowing イベントを処理します。これは、自分に渡された、イベントを発生させたオブジェクトへの参照を使用して、フォーム上のリスト ボックスのためのメッセージを作成します。
試してみてください
イベント処理の動作を確認するために、次の操作を行います。
- [F5] キーを押して、プロジェクトを起動します。
- 1 つ目の [Add] ボタンを 2 回クリックします。プログレス バー コントロールが自動的に更新されるのがわかります。
- 2 つ目の [Add] ボタンを 10 回クリックします。10 回目のクリックで、プログレス バーの横に "Full" のラベルが表示されます。
- 3 つ目の [Add] ボタンを 11 回クリックします。10 回目のクリックで、プログレス バーの横に "Full" のラベルが表示されます。11 回目のクリックで、リスト ボックスにメッセージが表示されます。
- 2 つ目の [Add] ボタンをもう 1 回クリックします。リスト ボックスに別のメッセージが表示されます。
図 3 に、この時点でのユーザー インターフェイスの様子を示します。
図 3. Buckets テスト フォームの動作
Visual Basic 6.0 との違い
Visual Basic .NET では、イベント ドリブン プログラミングの基本的な概念は変わっていませんが、一部の実装上の細部は異なります。最も大きな変更点は、実行時にイベント ハンドラをイベントに動的に関連付けることができる AddHandler および RemoveHandler ステートメントが追加されたことです。
要約
Visual Basic .NET では、新しいイベントの実装とイベントへの応答の両方が簡単に行えるようになっています。少数のキーワード (Event、RaiseEvent、Handles、AddHandler、および RemoveHandler) を使って、アプリケーションに必要なすべてのイベントの作成、処理、および関連付けを行うことができます。また、Visual Basic .NET は他の.NET 言語と同様に、共通言語ランタイムが提供するサービスの上に作られているので、イベント ドリブンのコードを作成するときには複数の言語を自由に混在させることができます。Visual Basic .NET で作成されたイベント ハンドラで、C# のライブラリで宣言されたイベントを処理することもできますし、その逆も可能です。
執筆者について
Mike Gunderloy は Washington 州東部でソフトウェアに関する執筆とニワトリの飼育を行っています。Sybex から出版されている『Access 2002 Developer's Handbook』の共著者であり、『SQL Server Developer's Guide to OLAP with Analysis Services』の著者でもあります。彼は Windows 以前の時代から Microsoft プロダクト関連のコードを書いており、今後もこれを続けていく予定です。執筆者へのメールのあて先は、MikeG1@larkfarm.com です。
Informant Communications Group について
Informant Communications Group, Inc. (www.informant.com) は、情報テクノロジ セクタに焦点を当てた多角的なメディア会社です。1990 年に創設された ICG は、ソフトウェア開発関連の出版事業、カンファレンス、カタログの発行、および Web サイトを専門としています。米国と英国にオフィスを持つ ICG は、有名なメディアおよびマーケティング コンテンツ インテグレータとして、IT プロフェッショナルに対して高品質の技術情報を提供してきました。http://www.informant.com