Visual FoxPro权威指南

      数据库学习笔记 2009-6-26 19:4
Visual FoxPro权威指南
http://book.csdn.net/ 2007-6-13 19:24:00

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。

标签集:TAGS:
回复Comments() 点击Count()

回复Comments

{commentauthor}
{commentauthor}
{commenttime}
{commentnum}
{commentcontent}
作者:
{commentrecontent}