9.5 使用对象
每个对象都是一个类的实例,通过表单设计器或编程创建对象后,就可以修改表单的属性、调用表单的方法,以及进行一些其他的特殊操作,如对象的事件绑定等。
9.5.1 创建对象引用
在9.4.2节中介绍了可以使用CREATEOBJECT()函数建立一个对象并返回对象引用的方法。在由用户自定义类使用CREATEOBJECT()创建对象之前,必须有使用DEFINE CLASS命令创建的类,或者已有使用SET CLASSLIB打开的.vcx可视类库中的类。
下面介绍一个NEWOBJECT()函数,该函数功能与CREATEOBJECT()函数类似。但是更加强大,在建立对象前,不必事先打开对象所基于的可视类库。该函数的语法格式如下:
NEWOBJECT(cClassName [, cModule [, cInApplication | 0 [, eParameter1, eParameter2, ...]]])
其中,cClassName指定类或对象的名称,表示要从该类或对象创建新的类或对象。
cModule指定类所在的.vcx可视类库或Visual FoxPro应用程序(如.prg、.mpr、.app、.exe)名称,默认为一个.vcx可视类库,如果想指定一个应用程序,则必须指定文件的扩展名。使用SET CLASSLIB命令可以为类库指定一个别名,如果要使用类库别名指定类库中的类或对象,可在类库别名后面带一个点号以及对象名,如下所示。
SET CLASSLIB TO MyClass ALIAS MyCntrls
mMyButton=NEWOBJECT("MyCntrls.MyButton")
cInApplication指定包含由cModule指定的.vcx类库的Visual FoxPro应用程序(.exe或.app名称),该应用程序必须具有扩展名。如果省略cModule,或者cModule为空字符串或null值,则忽略cInApplication。
0指定类或对象在实例化时不执行任何事件或方法,该功能在运行中的应用程序中不支持。
eParameter1, eParameter2, ...指定可选参数,该参数传递到类或对象的Init事件。
例如,下面的代码同样可以与CREATEOBJECT()函数一样创建一个表单对象。
PUBLIC oMyform
oMyform=NEWOBJECT("form1")
oMyform.Show
DEFINE CLASS form1 AS Form
ENDDEFINE
9.5.2 检查对象是否存在
因为对象名称保存在变量中,所以,可以使用TYPE()或VARTYPE()函数来检测变量的类型并判断对象是否已经存在。
如果变量保存的是一个对象引用,则TYPE()或VARTYPE()函数将返回字母“O”。参考下列代码。
PUBLIC oMyform
oMyform=NEWOBJECT("form1")
oMyform.Show &&执行oMyform的Show方法
?VARTYPE(oMyform) &&返回oMyform的数据类型,也可以使用?TYPE("oMyform")
DEFINE CLASS form1 AS Form
ENDDEFINE
9.5.3 调用对象的属性、方法和事件
对象建立以后,就可以通过对象层次引用来调用对象的属性、方法和事件。例如,在下列代码中,单击其中的Command1对象,将执行Command2对象的Click事件。
PUBLIC oMyform
oMyform=NEWOBJECT("form1")
oMyform.Show &&通过对象引用执行oMyform的Show方法
DEFINE CLASS form1 AS Form
Caption="调用对象事件示例"
ADD OBJECT Command1 AS CommandButton WITH ;
Top=30,;
Left=10,;
Height=30,;
Width=90,;
Caption="Command1"
ADD OBJECT Command2 AS CommandButton WITH ;
Top=30,;
Left=200,;
Height=30,;
Width=90,;
Caption="Command2"
PROCEDURE Command1.Click
Thisform.Command2.Click &&通过对象引用执行Command2的Click事件
ENDPROC
PROCEDURE Command2.Click
MESSAGEBOX("Hi,你好!")
ENDPROC
ENDDEFINE
在某些容器对象嵌套层次过多的情况下,这种对象的引用就会显得十分臃肿,例如,下面的代码用于将光标移动到表单中表格的第1列的Text1文本框对象中。
Thisform.Grid1.Columns(1).Text1.Setfocus
对于这种情况可以使用WITH...ENDWITH命令来解决这个问题。下面的代码使用对象的AddObject()方法向容器中添加一个按钮对象,并在WITH...ENDWITH命令中进行按钮的属性设置。
PUBLIC oMyform
oMyform=CREATEOBJECT("form1")
oMyform.Visible=.T.
oMyform.AddObject("Command1","CommandButton") &&加入Command1对象
WITH oMyform.Command1
.Top=190 &&指定对象的属性
.Left=220
.Height=30
.Width=100
.Caption="关闭"
.Visible=.T.
ENDWITH
DEFINE CLASS form1 AS Form
ENDDEFINE
此外,还可以使用Thisform等相对引用的方法减少对象调用的层次问题,详细内容请参考9.2.2节的介绍。
9.5.4 对象事件的触发顺序
Visual FoxPro的事件在与用户进行交互操作前,是按照一定的顺序触发的。在进行交互操作后,事件根据用户的操作进行触发。表9-9列出了事件的一般触发顺序,其中的标注1表示对象按从最内层对象向对外层容器的顺序执行;标注2表示在表单激活后按Tab顺序第1个获得焦点对象后触发的事件顺序;标注3表示第1个对象失去焦点向第2个对象移动焦点时触发的事件顺序;标注4表示对象失去焦点时触发的事件;标注5表示关闭表单时触发的事件顺序。
表9-9 Visual FoxPro事件的一般触发顺序
对象
|
事件
|
事件说明
|
Data environment
|
BeforeOpenTables
|
在打开表之前发生
|
Form set
|
Load
|
在对象加载时发生
|
Form
|
Load
|
|
Data environment cursor(s)
|
Init
|
在对象创建时发生
|
Data environment
|
Init
|
|
Objects 1
|
Init
|
|
续表
对象
|
事件
|
事件说明
|
Form
|
Init
|
|
Form set
|
Init
|
|
Form set
|
Activate
|
在对象激活时发生
|
Form
|
Activate
|
|
Object1 2
|
When
|
在对象接收到焦点前发生
|
Form
|
GotFocus
|
在对象接收到焦点时发生
|
Object1
|
GotFocus
|
|
Object1
|
Message
|
在对象接收到焦点时发生,用于在状态栏中显示信息
|
Object1
|
Valid 3
|
在对象失去焦点前发生
|
Object1
|
LostFocus
|
在对象失去焦点时发生
|
Object2 3
|
When
|
|
Object2
|
GotFocus
|
|
Object2
|
Message
|
|
Object2
|
Valid 4
|
|
Object2
|
LostFocus
|
|
Form
|
QueryUnload
|
在卸载表单前发生
|
Form
|
Destroy
|
在释放对象时发生
|
Object 5
|
Destroy
|
|
Form
|
Unload
|
在释放对象时发生,发生在Destroy之后
|
Form set
|
Unload
|
|
Data environment
|
AfterCloseTables
|
在关闭表后发生
|
Data environment
|
Destroy
|
|
Data environment cursor(s)
|
Destroy
|
|
9.5.5 对象事件的绑定
对于已经实例化的对象,无法再修改对象的事件。这种现象尤其表现在对Visual FoxPro主窗口对象的调用中,Visual FoxPro主窗口的对象名称保存在_SCREEN系统变量中。可以说,用户无法为Visual FoxPro主窗口直接指定自定义事件过程。并且使用在9.5.3节介绍的AddObject()添加到对象中的对象也无法为其指定事件的处理方法。
1.绑定Visual FoxPro事件
Visual FoxPro提供了一种事件绑定的方法来为已实例化的对象指定事件处理过程,事件绑定可以使用BINDEVENT()函数,该函数的语法格式如下:
BINDEVENT(oEventSource, cEvent, oEventHandler, cDelegate [, nFlags])
其中,oEventSource指定要绑定事件的对象名称;cEvent指定要绑定的事件名称;oEventHandler指定要将事件绑定到的对象名称;cDelegate指定事件绑定到的代理方法名称;nFlags用于指定事件绑定操作的附加位标记,nFlags的可用值如表9-10所示。
表9-10 nFlags的可用值
nFlags
|
位
|
说明
|
0
|
000
|
执行事件代码前执行代理代码(默认值)
|
1
|
001
|
执行事件代码后执行代理代码
|
2
|
010
|
执行一个简单调用时不触发事件,而仅仅是当作一个方法来调用,例如Thisform.Click仅仅是执行Click中的代码,而不会再引发与Thisform.Click绑定方法的代码
|
3
|
011
|
在代理代码前执行事件代码,当执行一个简单调用时不触发事件(即不执行代理代码)
|
4
|
100
|
绑定到Windows消息
|
BINDEVENT()函数返回绑定的事件数目。
需要注意的是,代理方法必须与cEvent中指定的事件具有相同的参数,并且代理方法必须具有PUBLIC属性,而不能是PROTECTED或HIDDEN;特定的控件事件诸如GotFocus、LostFocus、InteractiveChange和ProgrammaticChange等等, 要求设置nFlags=2,因为这些事件在内部被Visual FoxPro作为方法来调用, 虽然它们被认为是事件。
下面的代码将Visual FoxPro主窗口的MouseMove事件绑定到自定义类MyHandler的MyMouseMove方法,绑定后,当在主窗口中移动鼠标时,将显示鼠标的坐标位置。
PUBLIC oHandler &&注意,请将保存对象的变量设置为全局变量
oHandler=NEWOBJECT("MyHandler")
*!* 将_SCREEN.MouseMove绑定到oHandler.MyMouseMove
BINDEVENT(_SCREEN,"MouseMove",oHandler,"MyMouseMove")
DEFINE CLASS MyHandler AS Custom
PROCEDURE MyMouseMove
*!* 要保证该方法与_SCREEN.MouseMove有同样的参数设置
LPARAMETERS nButton, nShift, nXCoord, nYCoord
WAIT WINDOW "鼠标坐标:"+STR(nXCoord)+"|"+STR(nYCoord) NOWAIT
ENDPROC
ENDDEFINE
2.绑定Windows消息事件
从Visual FoxPro 9.0开始,BINDEVENT()函数可以绑定Windows消息事件到一个Visual FoxPro类的方法。要使用BINDEVENT()函数的这个新功能,需要为其指定4个参数。语法格式如下:
BINDEVENT(hWnd, nMessage, oEventHandler, cDelegate)
hWnd是当前Visual FoxPro应用程序的句柄,对于表单对象,可以从表单的HWnd属性获得这个句柄。
nMessage是要跟踪的Windows消息,这是一个整数值。如果你的计算机中安装了Visual Studio,可以在其安装目录的Platform SDK\include目录下面的WinUser.H文件中找到许多Windows应用程序可以接收的消息。
oEventHandler是与消息进行绑定的Visual FoxPro类实例对象。
cDelegate是接受到消息后要执行的方法,该方法位于oEventHandler所指定Visual FoxPro类实例中。
下面的代码建立了一个IDEHandler类,并绑定Windows的WM_THEMECHANGED消息到该类的HandleEvent方法。在Windows XP桌面主题被改变时,将执行HandleEvent方法中的代码。
#DEFINE WM_THEMECHANGED 0x031A
PUBLIC oHandler
*!* 建立处理Windows消息的类实例
oHandler=CREATEOBJECT("IDEHandler")
*!* 绑定Windows消息到oHandler实例的HandleEvent方法
BINDEVENT(_SCREEN.HWnd, WM_THEMECHANGED, oHandler, "HandleEvent")
*!* 定义类
DEFINE CLASS IDEHandler AS Custom
*!* WM_THEMECHANGED消息用于通知每个窗口发生了一个改变Windows桌面主题的事件,
*!* 例如在将当前Windows XP主题改变为Windows经典格式时。窗口在接收这个消息时,
*!* 是通过窗口的WindowProc API函数来完成的。因此,如果我们要将WM_THEMECHANGED
*!* 消息绑定到一个VFP类的方法,该方法也必须符合WindowProc函数的语法格式。
*!* WindowProc函数的语法格式如下:
*!* WindowProc(HWND hwnd,
*!* UINT uMsg,
*!* WPARAM wParam,
*!* LPARAM lParam
*!* )
*!* hwnd是接收消息的窗口句柄,uMsg是Windows消息,wParam和lParam在发送
*!* WM_THEMECHANGED消息时是保留参数,这里不作处理。
PROCEDURE HandleEvent &&该方法必须可以接收与WindowProc同样的参数
LPARAMETERS hWnd as Integer, Msg as Integer, wParam as Integer, lParam as Integer
IF Msg = WM_THEMECHANGED
MESSAGEBOX("你改变了Windows主题")
ENDIF
ENDPROC
PROCEDURE Destroy
UNBINDEVENT(_SCREEN.hWnd, WM_THEMECHANGED) &&解除绑定
ENDPROC
ENDDEFINE
3.检测事件绑定数目
可以用AEVENTS()函数来获取已存在的事件绑定数,函数的语法格式如下:
AEVENTS( ArrayName [, 0 | 1 | oEventObject ] )
其中,ArrayName指定保存事件绑定信息的数组名称;0指定AEVENTS()返回一个包含当前事件源的对象引用的三列数组,如表9-11所示;1指定返回一个包含Windows消息的四列数组,如表9-12所示;oEventObject用于指定一个对象引用,AEVENTS()为oEventObject返回一个包含引发事件和代理方法的五列数组,如表9-13所示。
表9-11 使用0参数时生成的三列数组
列
|
说明
|
类型
|
1
|
事件源
|
对象引用
|
2
|
事件
|
String
|
3
|
事件类型,或者是事件如何触发
|
0-由系统触发
1-由RAISEEVENT()函数触发
2-由方法调用触发
|
表9-12 使用1参数时生成的四列数组
列
|
说明
|
类型
|
1
|
hWnd
|
Integer
|
2
|
Window消息
|
Integer
|
3
|
对处理器的引用
|
对象引用
|
4
|
代理方法
|
String
|
表9-13 使用oEventObject指定的对象生成的五列数组
列
|
描述
|
类型
|
1
|
如果第2个元素是事件源,为.T.;如果第2个元素是事件处理程序,为.F.
|
Logical
|
2
|
事件源或事件处理程序
|
对象
|
3
|
事件
|
String
|
4
|
代理方法
|
String
|
5
|
BINDEVENT()标记
|
Integer
|
如果AEVENTS()返回0,并且数组没有事先存在,Visual FoxPro不创建数组,Visual FoxPro只在有返回结果的时候改变或转换已存在的数组。
下面的代码将返回当前的事件绑定数,并显示数组的内容。
PUBLIC oHandler &&注意,请将保存对象的变量设置为全局变量
oHandler=NEWOBJECT("MyHandler")
*!* 将_SCREEN.MouseMove绑定到oHandler.MyMouseMove
BINDEVENT(_SCREEN,"MouseMove",oHandler,"MyMouseMove")
*!* 返回与oHandler绑定的事件数目
nCount=AEVENTS(aMyArray,oHandler)
FOR i=1 TO nCount &&显示数组内容
??aMyArray(i,1),aMyArray(i,2),aMyArray(i,3),aMyArray(i,4),aMyArray(i,5)
?
ENDFOR
DEFINE CLASS MyHandler AS Custom
PROCEDURE MyMouseMove
LPARAMETERS nButton, nShift, nXCoord, nYCoord
WAIT WINDOW "鼠标坐标:"+STR(nXCoord)+"|"+STR(nYCoord) NOWAIT
ENDPROC
ENDDEFINE
4.解除事件绑定
解除事件绑定可以使用UNBINDEVENTS()函数,该函数有两种语法格式。如果希望从一个事件处理程序解除一个绑定事件,可以使用下面的语法格式,其参数说明请参考前面对BINDEVENT()函数的介绍。
UNBINDEVENTS(oEventSource, cEvent, oEventHandler, cDelegate)
如果希望解除所有与对象相关的事件,可以使用下面的语法,其中的oEventObject参数是事件源或事件处理程序的对象名称。
UNBINDEVENTS(oEventObject)
如果解除绑定成功,UNBINDEVENTS()返回被解除绑定的事件数;如果没有要解除绑定的事件,UNBINDEVENTS()返回0;如果UNBINDEVENTS()解除绑定失败而又有可以解除绑定的事件,Visual FoxPro将产生错误。
例如,要解除先前_SCREEN.MouseMove与oHandler.MyMouseMove的事件绑定,可以使用下面的命令。
UNBINDEVENTS(_SCREEN,"MouseMove",oHandler,"MyMouseMove")
也可以使用下面的命令解除所有与_SCREEN事件源或oHandler事件处理程序的绑定。
?UNBINDEVENTS(_SCREEN)
*!* 或使用下面的命令
?UNBINDEVENTS(oHandler)
5.使用代码引发一个事件
通常情况下,事件是由表单行为或用户操作引发的。但是,在Visual FoxPro中也完全可以把一个事件当作方法来调用(称之为简单调用),如Thisform.Init。这种情况下,将不能真正地引发事件。例如,在把BINDEVENT()函数的nFlags参数设置为2或3并进行简单调用时,并不会执行与事件绑定的方法,参考下列代码。
PUBLIC oHandler,oHandler1 &&注意,请将保存对象的变量设置为全局变量
oHandler=NEWOBJECT("MyHandler")
oHandler1=NEWOBJECT("MyHandler1")
*!* 将oHandler1.MyMethod绑定到oHandler.MyMethod,参数2指定在执行简单调用时,不会执行oHandler的MyMethod
BINDEVENT(oHandler1,"MyMethod",oHandler,"MyMethod",2)
oHandler1.MyMethod &&这种简单调用只是执行oHandler1的MyMethod,而不会执行与之绑定的oHandler的MyMethod
DEFINE CLASS MyHandler AS Custom
PROCEDURE MyMethod
MESSAGEBOX("这是oHandler的MyMethod方法")
ENDPROC
ENDDEFINE
DEFINE CLASS MyHandler1 AS Custom
PROCEDURE MyMethod
MESSAGEBOX("这是oHandler1的MyMethod方法")
ENDPROC
ENDDEFINE
要想真正引发一个事件,可以使用RAISEEVENT()函数,语法格式如下:
RAISEEVENT( oEventSource, cEvent [, eParm1...] )
oEventSource指定一个事件源对象,cEvent指定要引发的事件、方法或属性的名称,eParm1用于指定一个或多个参数来传递给方法。
该函数一直返回.T.。
如果将上面代码中的oHandler1.MyMethod语句替换为RAISEEVENT(oHandler1,"MyMethod"),则在执行oHandler1.MyMethod的同时会执行与之绑定的oHandler1.MyMethod。