李晓亮的博客

导航

【摘】vb6.0自定义事件

 

【本资料摘自vb帮助】

1.声明和引发事件

暂且假设有一个 Widget 类。该 Widget 类有一种方法,该方法可能花费很长时间来执行,同时还希望自己的应用程序能够显示出一些类型的完成标志。

当然,可以让 Widget 对象显示一个用来表示完成百分比的对话框,但是以后在每个使用 Widget 对象的工程中,就会都离不开那个对话框了。对象设计的一个较好的原则是让使用该对象的应用程序来处理用户接口—除非设计该对象的全部意图是管理一个窗体或者一个对话框。

Widget 的作用是执行其它任务,因此合理的做法是:把一个 PercentDone 事件给它,然后让调用 Widget 方法的过程来处理那个事件。PercentDone 事件也可以提供一种机制来取消任务。

现在,可以开始为该主题来建立代码示例了,方法是打开一个“标准 EXE”工程,将两个按钮和一个标签添加到“Form1”中。在“工程”菜单上,选择“添加类模块”,将一个类模块添加到工程中。象下表中所示的那样给对象命名。

对象 属性 设置值
类模块 Name Widget
第一个按钮 Caption 启动任务
第二个按钮 Caption 取消
标签 Name
Caption
lblPercentDone
"0"

Widget 类

在类模块的声明部分,用 Event 关键字来声明事件。事件可以有 ByVal 和 ByRef 参数,就象 Widget 的 PercentDone 事件所演示的那样:

Option Explicit
Public Event PercentDone(ByVal Percent As Single, ByRef Cancel As Boolean)

当调用对象得到 PercentDone 事件时,Percent 参数包含了任务完成的百分比。可以将 ByRef Cancel 参数设置为 True,以取消引发该事件的方法。

注意   可以象声明过程参数那样声明事件参数,所不同的是:事件不能有命名的参数、可选的参数、或 ParamArray 参数。事件没有返回值。

引发 PercentDone 事件

PercentDone 事件是由 Widget 类的 LongTask 方法引发的。LongTask 方法接受两个参数:该方法工作所要求的时间长度,以及 LongTask 在暂停之前产生 PercentDone 事件的最小时间间隔。

Public Sub LongTask(ByVal Duration As Single, ByVal MinimumInterval As Single)
Dim sngThreshold As Single
Dim sngStart As Single
Dim blnCancel As Boolean
'Timer 函数按 Single 返回午夜以来秒数的分数值。
sngStart = Timer
   sngThreshold = MinimumInterval
   
   Do While Timer < (sngStart + Duration)
      '在实际应用程序中,每次循环时将会在这里
'做某些单元的工作。
If Timer > (sngStart + sngThreshold) Then
         RaiseEvent PercentDone( _
         sngThreshold / Duration, blnCancel)
         '检查一下,看看操作是否被取消。
If blnCancel Then Exit Sub
         sngThreshold = sngThreshold + MinimumInterval
      End If
   Loop
End Sub

每隔 MinimumInterval 秒,都会引发 PercentDone 事件。当该事件返回时,LongTask 将检查 Cancel 参数是否设置为 True。

注意   为了简单起见,LongTask 假定事先已经知道任务将持续多长时间。但是,这几乎是不可能的。将任务分解成平均大小的块,可能是比较困难的,而且经常困扰用户的问题是,在他们得到某件事已经发生的指示前,真正经过的时间量。

详细信息   现在已经声明并引发了一个事件,怎样才能使得另一个对象来处理这个事件?下面的“处理对象的事件”,将就 Widget 对象引发继续详细讨论这个问题。

2.处理对象的事件

引发事件的对象叫做事件源。为了处理事件源所引发的事件,可以用 WithEvents 关键字声明对象类的变量。

本主题继续讨论上面的“声明和引发事件”中开始的 Widget 对象示例。为了处理 Widget 的 PercentDone 事件,将下面的代码放置到 Form1 的声明部分:

Option Explicit
Private WithEvents mWidget As Widget
Private mblnCancel As Boolean

WithEvents 关键字指定:变量 mWidget 将用于处理对象的事件。可以通过提供类名来指定对象类型,该类是创建这个对象的类。

变量 mWidget 是在“Form1”的声明部分所声明的,因为 WithEvents 变量必须是模块级的变量。这是正确的,而不管将它们放置其中的模块类型如何。

变量 mblnCancel 将用于取消 LongTask 方法。

对 WithEvents 变量的一些限制

使用 WithEvents 变量时,应该注意下面这些限制条件:

  • WithEvents 变量不能是派生对象变量。也就是说,不能把它声明为 As Object—当声明该变量时必须指定类名。

  • 不能把 WithEvents 变量声明为 As New。必须明确地创建事件源对象,并将它赋给 WithEvents 变量。

  • 不能在标准模块中声明 WithEvents 变量。只能在类模块、窗体模块以及其它定义类的模块中声明。

  • 不能创建 WithEvents 变量数组。

编写处理事件的代码

一旦声明了 WithEvents 变量,变量名就出现在模块“代码”窗口左边的下拉菜单上。当选择了 mWidget 时,Widget 类的事件将出现在右边下拉菜单上,如下图 9.9 中所示。

图 9.9   与 WithEvents 变量相关联的事件

选定一个事件,将显示相应的事件过程,以 mWidget_ 为前缀。所有跟 WithEvents 变量相关联的事件过程,都将以该变量名为前缀。将下面的代码添加到 mWidget_PercentDone 事件过程中。

Private Sub mWidget_PercentDone(ByVal Percent As _
Single, Cancel As Boolean)
lblPercentDone.Caption = CInt(100 * Percent) & "%"
DoEvents
If mblnCancel Then Cancel = True
End Sub

不论何时,引发 PercentDone 事件时,事件过程就在 Label 控件中显示完成的百分比。DoEvents 语句允许重新画出该标签,同时也给用户单击“取消”按钮的机会。将下面的代码添加到标题为“取消”按钮的 Click 事件中。

Private Sub Command2_Click()
mblnCancel = True
End Sub

当 LongTask 正在运行时,如果单击了“取消”按钮,一旦 DoEvents 语句允许出现对事件的处理,那么将会执行 Command2_Click 事件。模块级的变量 mblnCancel 设置为 True,mWidget_PercentDone 事件将对其进行测试,并将 ByRef Cancel 参数设置为 True。

连接 WithEvents 变量与对象

Form1 已经设置了对 Widget 对象事件的处理。剩余工作就是寻找某个地方的 Widget。

在设计时,声明 WithEvents 变量时,并没有任何对象与之相关联。WithEvents 变量与任何其它对象变量相同。必须创建对象,并将对该对象的引用赋给这个 WithEvents 变量。将下面的代码添加到 Form_Load 事件过程中,以创建 Widget。

Private Sub Form_Load()
Set mWidget = New Widget
End Sub

当执行上面的代码时,Visual Basic 将创建一个 Widget,并将它的事件跟 mWidget 相关联的事件过程连接起来。从那时起,一旦 Widget 产生了 PercentDone 事件,都将执行 mWidget_PercentDone 事件过程。

为了调用 LongTask 方法,将下面的代码添加到标题为“启动任务”按钮的 Click 事件中。

'“启动任务”按钮。
Private Sub Command1_Click()
   mblnCancel = False
   lblPercentDone.Caption = "0%"
   lblPercentDone.Refresh
   Call mWidget.LongTask(14.4, 0.66)
   If Not mblnCancel Then lblPercentDone.Caption = 100
End Sub

在调用 LongTask 方法之前,完成百分比的显示标签必须被初始化,而且必须将模块级的 Boolean 标记(其作用是取消方法)设置为 False。

用 14.4 秒任务延迟调用 LongTask。每隔三分之二秒引发一次 PercentDone 事件。每次引发该事件时,都将执行 mWidget_PercentDone 事件过程。

LongTask 结束以后,都要测试 mblnCancel,以了解 LongTask 是否正常结束,或者是否因为 mblnCancel 设置为 True 而停止。只有在前一种情况下,完成的百分比才会被更新。

运行程序

按 F5 键,就使工程处于运行模式。单击“启动任务”按钮。每次引发 PercentDone 事件,带有完成百分比的标签将会被更新。单击“取消”按钮,可以停止任务。注意:单击“取消”按钮时,该按钮的外观并不会立即改变。直到 DoEvents 语句允许事件处理时,才引发 Click 事件。

可能会发现用 F8 键来运行程序是有益的—这样做的话,每次将执行一行代码。可以清楚地看到是怎样执行到 LongTask 的,然后在每次引发 PercentDone 事件时,能直接重新进入 Form1。

当执行回到 Form1 中的代码时,如果再次调用 LongTask 方法,将会发生什么样的情况?混乱、混沌,并最终(如果每次产生事件时都发生这种情况)出现堆栈溢出。

处理不同的 Widget 事件

通过将对新 Widget 的引用赋给 mWidget 变量,可以使得 mWidget 变量为不同 Widget 对象处理事件。事实上,每次单击按钮时,通过添加下面两行代码,可以让 Command1 中的代码来做这些:

Set mWidget = New Widget      '<- 新行。
Call mWidget.LongTask(14.4, 0.66)
Set mWidget = Nothing         '<- 新行。

每次按下按钮时,上面的代码都将创建一个新的 Widget。一旦 LongTask 方法结束,对 Widget 的引用就会通过将 mWidget 设置为 Nothing 而释放,而且这个 Widget 也被撤消。

WithEvents 变量每次只能包含一个对象引用,因此如果将不同的 Widget 对象赋给 mWidget,先前 Widget 对象的事件将不会再得到处理。如果 mWidget 是唯一包含对旧 Widget 引用的对象变量时,那么该对象将被撤消。

注意   需要多少 WithEvents 变量,就可以声明多少,但是不支持 WithEvents 变量数组。

终止 WithEvents 变量事件的处理

只要有一个赋给变量 mWidget 的 Widget 对象,不论何时,当该 Widget 产生事件时,都将调用跟 mWidget 相关联的事件过程。为了终止事件处理,可以将 mWidget 设置为 Nothing,就象在下面代码块中所示。

'终止 mWidget 事件处理。
Set mWidget = Nothing

当把 WithEvents 变量设置为 Nothing 时,Visual Basic 将拆除对象的事件和与该变量相关联的事件过程的连接。

重点   WithEvents 变量与任何其它对象变量一样,包含一个对象引用。这个对象引用所起的作用,是保持该对象是活动的。当把对象的所有引用都设置为 Nothing 以撤消它时,不要忘记声明为 WithEvents 的那个变量。

详细信息   与 WithEvents 变量相关联的事件过程看起来很象窗体上控件的事件过程。“WithEvents 与窗体上的控件事件的比较”讨论了它们之间的相似和不同之处

WithEvents 与窗体上的控件事件的比较

可能已经注意到,在使用 WithEvents 变量的方式和处理某个窗体上控件所产生的事件的方式这二者之间,存在某些相似之处。在两种情况下,当在代码窗口右边下拉菜单上选择事件时,都将得到包含事件正确参数的事件过程。

事实上,确切地说二者机制是相同的。控件是作为窗体类的属性来处理的,并且这个属性的名字,就是在“属性”窗口中赋给控件 Name 属性的那个值。

看起来似乎有一个与控件同名的 Public 模块级的变量,而且所有控件的事件过程名,都以这个变量的名字为起始,就象 WithEvents 变量情况下,它们都以 WithEvents 变量名为起始一样。

通过将 mWidget 变量声明为 Public 而不是 Private,可以很容易地看到这一点。这样做时,mWidget 将作为 Form1 的一种属性出现在“对象浏览器”中,象在另一种情况下,控件出现在窗体上一样。

两种情况的不同之处在于:当创建窗体时,Visual Basic 自动创建该窗体上所有控件的实例,而在另一种情况下,必须创建自己的类(这些类的事件是想要处理的)的实例,并且应把那些对象的引用赋给 WithEvents 变量。

详细信息   可以将自己的事件添加到窗体中,如下面“向窗体添加事件”所述

posted on 2008-06-22 06:58  LeeXiaoLiang  阅读(1926)  评论(0编辑  收藏  举报