自从VB6开始,Event这个东西就已经深入了VB系列程序中间。在VB6中,我们是简单的通过诸如TextBox1_TextChanged这样一个函数了解到当文字变化的时候,就能够自动触发这个函数。进入了.NET之后,是我们有幸第一次看到了Event的真面目。现在我们在VB.NET里面直接写入下面的函数
Private SubTextBox1_TextChanged(ByVal sender As System.Object,ByVal e AsSystem.EventArgs) Handles TextBox1.TextChanged
End Sub
看来是不能够达到VB6的效果的。原因是我们少写了HandlesTextBox1.TextChanged。
这个就是真实存在的Event了。VB.NET对于Event终于可以脱离了函数名的限制(但是不是函数的全部限制),自由的使用。只要我们喜欢,我们也可以写成
Private SubThisIsMyTextChangedMethod(ByVal sender As System.Object,ByVal e AsSystem.EventArgs) Handles TextBox1.TextChanged
End Sub
完全可以。
现在我们就来看看我们如何在自己的类里面加入我们需要的Event。
(1)首先我们需要一个类
Public Class Class1
InheritsObject
End Class
(2)然后我们就可以在这个类里面加入我们的Event了。
Public Class Class1
InheritsObject
Public EventMyEvent(ByVal sender As Object,ByVal e As EventArgs)
End Class
Event里面是可以携带参数的。就好像TextChangedEvent一样,他携带了sender和e。sender表示这个Event触发源的实例,也就是这个TextBox1;e是一个事件参数,我们可以将需要传达给使用的信息通过继承自EventArgs的类的实例来传递出去。而这里,EventArgs里面并没有什么有价值的信息。
现在这个类里面已经有了我们要的Event,但是这个Event仅仅是一个声明。也就是说,我们告诉编译器和使用者,Class1里面有一个叫做MyEvent的Event。我们的下一个步骤就是确定什么时候,我们要引发这个Event。
(3)引发Event的语句很简单。
RaiseEvent MyEvent(Me,New EventArgs)
但是值得我们注意的是,Event是不能凭空触发的。我的意思是,RaiseEvent也是一条VB语句,我们只能将这条语句写在Function、Sub或者Property里面。这就意味着我们的Event的触发,要么是通过别人调用了某一个方法,要么通过现在已经存在HandleSub来触发。
现在我们使用第一种方法,我们通过一个Public函数来触发这个Event。
Public SubDoSomething()
RaiseEvent MyEvent(Me,New EventArgs)
End Sub
这就意味着,当用户调用了DoSomething方法的时候,Class1就会自动抛出一个MyEvent事件出来。
到这里,一个完整的Event就已经做完了。很简单吧。
(4)同样的方法,我们可以在一个Handle Sub里面抛出我们Event。
Public Class Class1
InheritsObject
PrivateWithEvents MyButton As New Button
Public EventMyEvent(ByVal sender As Object,ByVal e As EventArgs)
Private SubMyButton_Click(ByVal sender As Object,ByVal e As System.EventArgs)Handles MyButton.Click
RaiseEvent MyEvent(Me,New EventArgs)
End Sub
End Class
这里我们同过一个WithEvents的成员MyButton的Click事件函数,触发我们自己的MyEvent事件。
(5)使用这个Class1,我们就可以看到我们自己做的这个MyEvent事件了。
Public Class Test
InheritsObject
PrivateWithEvents MyClass1 As Class1
Private SubMyClass1_MyEvent(ByVal sender As Object,ByVal e AsSystem.EventArgs) Handles MyClass1.MyEvent
End Sub
End Class
很简单吧,但是使用Event并不止如此。随后我们将会看到这样简单的使用Event会导致的一些问题。
现在我们看看下面的类。
Public Class Class1
Inherits Object
Private WithEvents MyButton As New Button
Public Event MyEvent(ByVal sender As Object,ByVal e As EventArgs,ByRef Cancal As Boolean)
Public Event MyExtendedEvent(ByVal sender As Object,ByVal e AsEventArgs)
Private Sub MyButton_Click(ByVal sender As Object,ByVal e AsSystem.EventArgs) Handles MyButton.Click
Dim blnCancel As Boolean = False
RaiseEvent MyEvent(Me,New EventArgs,blnCancel)
ExtendedCommand()
End Sub
Private Sub ExtendedCommand()
RaiseEvent MyExtendedEvent(Me,New EventArgs)
End Sub
End Class
我们定义了两个Event,一个是MyEvent,另一个是MyExtendedEvent。我们在Button.Click的时候,触发了我们的MyEvent,随后我们通过调用ExtendedCommand函数触发下一个Event。看来没有什么问题,用户在使用的时候,依照往常一样。
Public Class Test
Inherits Object
Private WithEvents MyClass1 As Class1
Private Sub MyClass1_MyEvent(ByVal sender As Object,ByVal e AsSystem.EventArgs) Handles MyClass1.MyEvent
Console.WriteLine("MyEvent ... ")
End Sub
Private Sub MyClass1_MyExtendedEvent(ByVal sender As Object,ByVale As System.EventArgs) Handles MyClass1.MyExtendedEvent
Console.WriteLine("MyExtendedEvent ... ")
End Sub
End Class
看来一切都没有问题。但是我们有一个蹩脚的用户有这样的要求,他希望按照一个判断,确定是否执行MyEvent的后续操作。并且如果不执行后续操作的话,也不要引发MyExtendedEvent这个事件。
现在看看我们怎么做呢?我们可以简单的实现第一个要求,就在MyClass1_MyEvent函数里面加入一些判断就可以了。
Private Sub MyClass1_MyEvent(ByVal sender As Object,ByVal e AsSystem.EventArgs) Handles MyClass1.MyEvent
If m_blnGo = True Then
Console.WriteLine("MyEvent ... ")
End If
End Sub
但是我们的第二个问题就没有办法解决了,因为无论我们在MyClass1_MyEvent下面做了多少工作,程序都回执着的回到RaiseEvent那条语句,然后执行ExtendedCommand,进而触发了MyExtendedEvent事件。所以这时候,我们应该回头看看我们的类是不是有些问题了。
我们的问题确实存在,就是我们在Button.Click后无条件的RaiseEvent了。使用者没有办法阻止这一切的发生。他们能做的就是在RaiseEvent的时候加入一些操作,但是无论如何不能修改我们原先的操作。这看来着实让人沮丧,就好像订阅短信之后无法退订一样。现在我们看看怎么来解决这个问题,就是说我们提供给使用者功能的同时,我们还应该提供给他们如何取消这些功能的办法(虽然这个功能可能很好)。
我们首先把着眼点放在了Event的参数里面,我们可以通过参数将一些信息传递给用户,自然我们也能供通过参数将用户的要求反馈回来。
我们这样修改我们的程序。
Public Event MyEvent(ByVal sender AsObject,ByRef Cancel As Boolean)
现在Event通过一个ByRef Cancel AsBoolean参数将用户的要求返回回来。这样我们就可以在MyClass1里面知道了用户有什么要求。如果Cancel=True的时候,表明了用户希望终止这个事件之后的所有操作。这样我们的调用函数就变成。
Private Sub MyButton_Click(ByVal sender As Object,blnCancel)
If blnCancel = False Then
ExtendedCommand()
End If
End Sub
这样使用者使用这个事件的时候,就可以控制这个事件之后的操作了。
Private Sub MyClass1_MyEvent1(ByVal sender As Object,ByVal e AsSystem.EventArgs,ByRef Cancel As Boolean) HandlesMyClass1.MyEvent
If m_blnGo = True Then
Console.WriteLine("MyEvent ... ")
Else
Cancel = True
End If
End Sub
如果用户不希望操作,那么他通过Cancel =True就可以把此后的操作都撤销掉。
这样的操作在我们制作工具控件的时候特别有用,比如我们在Button.Click的时候显示一个默认的字符串,但是用户可以修改是否显示、内容,这时候用这种方法最为合适。
我们定义一个Event,通过参数传递Cancel和Message。如果Cancel为False的时候,我们就把Msg显示出来。
Public ClassShowMessage
Inherits Object
Public Event ShowMessage(ByVal sender As Object,ByVal e AsEventArgs,ByRef Cancel As Boolean,ByRef Message AsString)
Private Const CST_MSG As String = "This is defaultmessage."
Private WithEvents Button1 As New Button
Private Sub Button1_Click(ByVal sender As Object,ByVal e AsSystem.EventArgs) Handles Button1.Click
Dim Cancel As Boolean = False
Dim Msg As String = CST_MSG
RaiseEvent ShowMessage(Me,Cancel,Msg)
If Cancel = False Then
MsgBox(Msg)
End If
End Sub
End Class
而Cancel和Msg的内容,我们实现给定了默认值,由用户来决定。如果用户什么都不做,那么就是显示默认的内容了。
Public ClassTestShowMessage
Inherits Object
Friend WithEvents ShowMessage1 As New ShowMessage
Private m_blnNeedShow,m_blnCustomShow As Boolean
Private Sub ShowMessage1_ShowMessage(ByVal sender As Object,ByVale As System.EventArgs,ByRef Message AsString) Handles ShowMessage1.ShowMessage
If m_blnNeedShow = True Then
If m_blnCustomShow = True Then
Message = "This is custom message."
End If
Cancel = False
Else
Cancel = True
End If
End Sub
End Class
但是我们发现了一个新的问题,如果我们的参数很多很多,难道我们的Event就要无限的延长了么?我们这时候还是可以从参数来入手,看看.NET自己是怎么做的。
现在我们看看这样一个问题。我们想要做这样一个控件,他继承自Window.Forms.TextBox下面,也就是说它支持TextBox的所有功能,但是我们需要一个新的功能,就是当用户按下回车之后,我们判断一下这个TextBox里面的文字,如果是空字符串的话,就显示一个MessageBox,默认的内容是“Emptyconnect is notvalidated.”。同时我们希望用户可以选择是否显示这个MessageBox。我们用此前的办法来做。
我们的类很快就可以完成。
Public ClassMyTextBox
Inherits TextBox
Public Event EnterKeyPress(ByVal sender As Object,ByRef Cancel AsBoolean,ByRef Message As String)
Private Sub MyTextBox_KeyPress(ByVal sender As Object,ByVal e AsSystem.Windows.Forms.KeyPressEventArgs) HandlesMyBase.KeyPress
'Do something then user press ENTER KEY
If Asc(e.KeyChar) = 13 Then
Dim Cancel As Boolean = False
Dim Message As String = "Empty connect is notvalidated."
'Send the event with parameters
RaiseEvent EnterKeyPress(Me,Message)
'Show message Box depend on the return parameters
If Cancel = False Then
MsgBox(Message)
End If
End If
End Sub
End Class
而且我们很容易就可以使用这个控件了。
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows FormDesigner generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent()call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing AsBoolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows FormDesigner
'It can be modified using the Windows FormDesigner.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()>Private Sub InitializeComponent()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5,12)
Me.ClientSize = New System.Drawing.Size(292,273)
Me.Name ="Form1"
Me.Text = "Form1"
End Sub
#End Region
Friend WithEvents MyTextBox1 As New MyTextBox
Private Sub Form1_Load(ByVal sender As System.Object,ByVal e AsSystem.EventArgs) Handles MyBase.Load
Me.Controls.Add(Me.MyTextBox1)
Me.MyTextBox1.Location = New Point(100,100)
End Sub
Private Sub MyTextBox1_EnterKeyPress(ByVal sender As Object,ByRefCancel As Boolean,ByRef Message As String) HandlesMyTextBox1.EnterKeyPress
End Sub
End Class
现在看来一切正常,但是无聊的客户突然来了一个Mail,他们希望我们能够在显示MessageBox的时候,将TextBox的背景色变成红色,并且允许有可能在其它的时候会变成绿色。也就是说变色也是要使用者可以自由设定的。这样我们就要在Event参数列表里面加入一个新的背景色参数,并且修改所有使用了这个Event的Handle函数。
Public ClassMyTextBox
Inherits TextBox
Public Event EnterKeyPress(ByVal sender As Object,ByRef Message As String,ByVal BackColor AsColor)
Private Sub MyTextBox_KeyPress(ByVal sender As Object,ByVal e AsSystem.Windows.Forms.KeyPressEventArgs) HandlesMyBase.KeyPress
'Do something then user press ENTER KEY
If Asc(e.KeyChar) = 13 Then
Dim Cancel As Boolean = False
Dim Message As String = "Empty connect is notvalidated."
Dim Color As Color = Color.Red
'Send the event with parameters
RaiseEventEnterKeyPress(Me,Message,Color)
'Show message Box depend on the return parameters
If Cancel = False Then
MsgBox(Message)
End If
End If
End Sub
End Class
然后修改使用的地方,也许实际上我们只是让它显示成默认的红色,我们也需要修改使用Event的地方,因为Event的定义已经不同了。
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows FormDesigner generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent()call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing AsBoolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows FormDesigner
'It can be modified using the Windows FormDesigner.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()>Private Sub InitializeComponent()
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5,273)
Me.Name = "Form1"
Me.Text = "Form1"
End Sub
#End Region