デリゲート変数をイベントに紐づけるには
先日のこと
私はあるプログラムのメソッド集をひたすら書き写して実行するということをしていました。
画面上にボタンを貼りつけて、ボタンを押したときにそのプログラムが実行される、という形式のものなのですが、量が多く、いちいちボタンを画面に貼りつけていては面倒だし、かといってプロジェクトをある程度の個数ごとにわけるのもおっくうだと感じていました。
そこで、ボタンの元となる連続するデータより動的にボタンを生成する、という方式にしてはどうかと思いつきました。
Public Class Form1
Private Delegate Sub btnClickEv(ByVal s As Object, ByVal e As EventArgs)
Private Class ButtonSeedDatum
Public Property caption As String
Public btnEv As btnClickEv
End Class
Private Iterator Function GetBtnData() As IEnumerable(Of ButtonSeedDatum)
Yield New ButtonSeedDatum() With {
.caption = "イ",
.btnEv = Sub(s, e)
MsgBox("イロハのイ!")
End Sub
}
Yield New ButtonSeedDatum() With {
.caption = "ロ",
.btnEv = Sub(s, e)
MsgBox("イロハのロ!")
End Sub
}
Yield New ButtonSeedDatum() With {
.caption = "ハ",
.btnEv = Sub(s, e)
MsgBox("イロハのハ!")
End Sub
}
End Function
Public Sub New()
InitializeComponent()
Const ROW_HEIGHT As Integer = 30
Const COL_WIDTH As Integer = 150
Dim row As Integer = 0
For Each btnDatum In GetBtnData()
Dim btn As New Button()
btn.Top = row * ROW_HEIGHT
btn.Height = ROW_HEIGHT
btn.Width = COL_WIDTH
btn.Text = btnDatum.caption
AddHandler btn.Click, AddressOf btnDatum.btnEv
Me.Panel1.Controls.Add(btn)
row += 1
Next
End Sub
End Class
■リスト1:当初の目論見上記ソースコードの★のコメント中にエラーが出ました。
いわく、「'AddressOf'オペランドはメソッドの名前でなければなりません。かっこは不要です」とのことです。
つまり、btnDatum.btnEv の箇所がメソッドだと認識されていないようなのです。
しかし、一般的にデリゲートのメソッドは以下のように呼ばれます。
Private Delegate Sub Dlg()
Private Sub CallDelegate()
Dim d As Dlg
d = Sub()
MsgBox("ア!")
End Sub
d()
End Sub
■リスト2:一般的なデリゲートメソッドの呼び出したしかにこれでも呼べるのですが、より堅苦しい(というかこちらが正式で、上記はシンタックスシュガー?)呼びかたとしては、
Private Delegate Sub Dlg()
Private Sub CallDelegate()
Dim d As Dlg
d = Sub()
MsgBox("ア!")
End Sub
d.Invoke()
End Sub
■リスト3:堅苦しいデリゲートメソッドの呼び出しと、Invoke をつけます。
Invoke はメソッドですから、そうなると、これを使えばメソッドとして認識してもらえそうです。
Public Sub New()
InitializeComponent()
Const ROW_HEIGHT As Integer = 30
Const COL_WIDTH As Integer = 150
Dim row As Integer = 0
For Each btnDatum In GetBtnData()
Dim btn As New Button()
btn.Top = row * ROW_HEIGHT
btn.Height = ROW_HEIGHT
btn.Width = COL_WIDTH
btn.Text = btnDatum.caption
AddHandler btn.Click, AddressOf btnDatum.btnEv.Invoke
Me.Panel1.Controls.Add(btn)
row += 1
Next
End Sub
■リスト4:修正したコンストラクタ上記のように修正して実行すると、パネルにボタンが縦に並び、押すとそれぞれのメッセージボックスが表示されるようになります。
私はたまたま Invoke メソッドの存在を知っていましたが、知らなければ「デリゲート変数をイベントに紐づけることはできないんだ」と結論づけるところでした。
もっとも、このようなコーディングをするのはレアケースと思われます。
もしこの情報がお役に立てたなら嬉しいです。