分享

陈纯BOE《VFP 6.0中的_Assign方法》

 一尘819 2015-10-27

前言

译者言 这篇文章来自著名的VFUG网站1999年10月的新闻邮件。VFUG是Virtual FoxPro User Group的缩写,主页地址是:http://www.,他们每个月发行一份新闻邮件,内容包括VFP专题文章、技巧、网络资源以及新闻等等,当然,是全英文的。你可以自由加入VFUG,以订阅新闻邮件。

自从99年11月收到这份新闻邮件开始,我就对这篇文章产生了兴趣。我的E语还凑合,再靠着金山词霸帮忙,慢慢的读完了它。在阅读过程中,我不止一次的感叹:这篇文章真是太好了,一定要把她介绍给中国的VFP爱好者们。

这 篇文章里提供了一种完全异类的思路和方法,相信会对读者大有启发的。国内的VFP研究学习总是离不开程序员指南和帮助文件那点内容,(最可恨的是,国内 VFP书籍很多都是完全或大部分抄袭程序员指南的!!!)而欧美的文章里却有很多关于编程原理性的东西,很值得我们学习借鉴。 由于个人水平所限,翻译起来比较慢,也未必准确,请大家原谅。对原文感兴趣可以去VFUG网站加盟,或给我发封Email,我会给你寄来的。

 

第一部分:对象和记录(原文如此)

为纪录使用对象

过去(面对对象编成出现以前的时候)许多人都知道SCATTER 和 GATHER MEMVAR命令的使用。VFP现在给了我们通过对象来做同样的事情的方法,而且更加简单。

现在假设你有一张客户表(Customer)只包括“名字”和“姓”两个字符型字段,你不妨建立一张表试一下。

从命令窗口执行

随便建立一张自由表,包括两个字段“名字”字符型(30)、“姓”字符型(30)。

浏览这张表并随便输入几个纪录。在浏览的时候按下ctrl+Y键来增加一个新纪录并输入随便什么人的姓名。

现在新建一个表单,给表单添加一个自定义属性\'thi\0srec\'。

向数据环境中添加入你的客户表(Customer)。

在表单的load方法中,选择客户表(Customer),并用scatter命令分散空白纪录到对象。

***************************************************************************
Select customer && 也许表已经被选中了,但为了安全起见还是再选择一下
scatter name thisform.thisrec blank

***************************************************************************

【译者注:scatter命令的用法:

scatter命令:用来从一个表中的当前纪录从拷贝数据到一系列的变量或一个数组中。
scatter MemVar:会自动建立与字段名相同的一系列变量。
scatter to arrayName:建立一个数组,将每个字段的数据放到数组的元素中。
scatter Name objectName:会建立一个对象,对象的每个属性名与字段名相同,值为字段的数据。
scatter:命令后加上blank参数则建立的变量、数组或对象的属性值为空。

一般来说,scatter命令与Gather命令合用,scatter命令收集数据,对数据进行修改后用Gather命令保存到表中的当前纪录。

同 时,在这里要注意的是:thisrec本是表单的一个属性,但使用了scatter语句后,就把这个属性当成一个对象用了!!!我不知道这算是个什么对 象,从调试器的局部窗口里也看不到它的父类是什么,只有“对象”两个字而已,这个对象也只有以字段名为名称的两个属性。】

这有点像SCATTER TO MEMVAR命令只不过使用了表单的自定义属性。作为面对对象方法的先进之处在于在内存中可以同时有多个纪录。另外,这个方法建立的对象可以被传递到另外的方法,比如表单,另外,象我们将会看到的那样,还有com 对象。

下 一步我们需要得到纪录并显示它们。添加一个列表框list1到表单上并使用生成器来选择客户表(Customer)作为数据源。如果碰到超过一个人同姓的 情况,你可以把两个字段都选中。同样,你也可以查看和修改纪录,添加两个文本框Textbox1、Textbox2并把它们绑定到我们的纪录对象。

把Textbox1文本框的controlsource属性设置为:thisform.thisrec.firstname
把Textbox2文本框的controlsource属性设置为:thisform.thisrec.surname

记住thisrec是一个储存我们的纪录的对象,在程序开始时我们把它清空了。

【译者注:原来属性还可以这么用!属性变成了对象,竟然还会有自己的属性!】

在列表框list1的Click方法中加入以下代码以向对象添入数据:

List1 Click 方法:

*****************************************************
Select customer
Scatter name thisform.thisrec
thisform.refresh()

*****************************************************

That is it!现在你可以用建立的对象来对纪录进行更改、比较等无论什么操作,但等你干完了以后,必须象下面一样把修改送回表中:

添加一个命令按钮Command1,设置它的Caption属性为:\"Save\"、Enabled属性为False。

回到list1的Click方法,添加下面一行代码:

Thisform.Command1.enabled = .T.

现在list1的Click方法代码应该是这样的:

******************************************************
Select customer
Scatter name thisform.thisrec
thisform.refresh()
thisform.Command1.enabled = .T.

******************************************************

在Command1的Click方法中加入以下代码:

Command1.Click方法:

*******************************************************
Gather name thisform.thisrec
this.enabled = .F.

*******************************************************

我想你已经理解了,可以添加你自己的\"添加新纪录\"按钮或\"删除纪录\"的按钮。

下一步,我们将看一下怎样传递这些对象到别的方法。

 

第二部分:对象和记录

传递对象到其他的方法和表单

如 果你已经照着第一步那么做了,那么你已经有了一个收集和更改纪录的表单,它通过使用一个放置纪录的对象来做到这一点。现在我们来看看怎样传递对象到另一个 方法。尽管当对象对任何表单方法是可视的时,这种办法看起来没什么用。不过我们先来试着做一下,我们将给表单添加一个交换名和姓的自定义方法。

给表单添加一个名叫Swapfields的自定义方法,并输入以下的代码:

表单的 Swapfields 方法

*******************************************************
LParameters RecObject
Local TempStore
TempStore = RecObject.Firstname
RecObject.Firstname = RecObject.Surname
RecObject.Surname = TempStore
*******************************************************

添\0加一个命令按钮command2,设置它的Caption属性为Swap names、Enabled属性为False。

Comand2.Click方法

*******************************************************
thisform.swapfields(thisform.thisrec)
this.enabled = .F.
*******************************************************

[译者注:注意到了吗?向swapfields方法传递的是从属性变成对象的thisrec!玩了那么久的VFP,只听说过传递变量、数组、字符串的,没想到竟然还能传递对象!真是开了眼界了。]

在list1控件的Click方法代码中加入下面一行:

thisform.command2.enabled = .T.

List1的Click方法代码现在看起来应该是这样的:

******************************************************
Select customer
Scatter name thisform.thisrec
thisform.refresh()
thisform.Command1.enabled = .T.
thisform.Command2.enabled = .T.
******************************************************

现 在让我们看看发生了什么:姓和名被交换了一圈,并且结果可以被储存。象我说过的那样,对这个简单的示例程序来说,这看起来没什么用,因为我们能够直接对 Swapfields方法中使用的对象进行操作。但是如果你要同时操作多个纪录,也许你在比较纪录并且你的表单上有两个或多个纪录对象。

那么用这种方法,这个表单自定义方法对任何一个对象而言都是相同的,并且你的命令按钮也可以对任何一个相关联的对象进行操作。只要字段相同,Swapfields方法始终不会在乎。你可以更进一步建立一个自定义方法同时对两个纪录对象操作并同时发送结果。

下面我们来看看怎样发送对象到另一个表单。

 

第三部分:对象和其他表单

传递对象到其他的表单

这 里有更多的技巧。如果你看看我们的文本框对象,你将看到它们的ControlSource属性都被从我们的\'thisrec\'对象赋值。因为我们在文 本框初始化之前已经让我们的对象分散(Scattered)了数据,所以这样做是正确的。这里我们在表单的Load方法中用Scatter name thisform.thisrec收集了数据,当我们调用另一个表单时,在新表单的文本框有控制源(ControlSource)以前我们不能对这些文本 框做任何事。另外会发生一个错误并且新表单不能初始化。有许多办法可以解决这种情况,但你选择怎么做必须依情况而定。一种办法是让文本框的 ControlSource为空。另外也可以把它们的值指向一个虚假的属性或者把它们当成一个直到表单的纪录对象已经被生成时才添加的一个容器对象的一部 分。下面使用了第一种方法。

要开始下一步,建立一个新的表单,表单上有两个文本框和一个命令按钮,设置命令按钮的caption属性为\'Swap Fields\'。建立一个表单属性\'recobject\',它将被用来保存从初始表单传递过来的对象。最后,把表单设置为模式表单。

表单的Init事件中:

*********************************************************
LParameters oRecObject
thisform.recobject = oRecObject
thisform.text1.controlsource = \"thisform.recobject.firstname\"
thisform.text2.controlsource = \"thisform.recobject.surname\"
*********************************************************

【译 者注:有没有搞错!?这个方法摆明了是要从上一个表单接受thisrec对象了,可thisrec是对象呀!它被自己所在的表单的自定义方法使用还可以理 解,可要说把一个对象传递到另一个表单的属性中,然后那个属性也变成了对象了,这真是闻所未闻。这是面对对象编程,还是艾博拉病毒感染?:-))】

在Init事件中,我们传递了纪录对象,现在它是这个表单的一个表单属性并且设置了文本框的数据源ControlSource。因为现在表单尚未激活,在这里不需要刷新表单。

下面是命令按钮的代码:

Command1.Click

*********************************************************
Local Tempstore
Tempstore = thisform.recobject.firstname
thisform.recobject.firstname = thisform.recobject.surname
thisform.recobject.surname = Tempstore
thisform.release()
*********************************************************

看起来这个表单(被调用表单)有自己的纪录对象。而实际上这是和调用它的表单使用的是同一个对象。我们现在来证明一下。编辑调用它的表单的Swap fields命令按钮的Click事件代码:

Command2.Click

********************************************************
&& thisform.swapfields(thisform.thisrec)
do form swapper with thisform.thisrec
this.enabled = .F.
thisform.refresh()
********************************************************

现在当表单运行时,如果按下了Swap fields命令按钮,将弹出新的表单,在这
个新表单上单击Swap fields命令按钮,这个表单将\0被释放。这个初始表单的字段已经被
交换了一周。

下面我们快速的看一下assign方法。

 

第四部分:对象和 _Assign

对象和assign方法

VFP6里新增了Access 和 Assign方法。当一个属性被查询时access方法被触发。在这里我只做一个简单的注释,不会谈很多的关于一个属性被查询或更改时存在的\"this_access\"方法的内容。

查看帮助以获得更多的信息。目前我感兴趣的是Assign方法。原因是这将有益于帮助我们掌握这一系列描述一个VFP COM dll触发的事件的文章。现在,让我们看一个例子:一个Assign方法得到一个从Swap fields表单中返回的错误。

因 为在这个例子中需要一个对象,第一件事情是建立一个用户自定义类,从菜单上选择“新建”——“类”,建立一个名叫\"callclass\"的自定义类并 保存在cclass.vcx类库中。第二步,给这个自定义类添加一个名叫callerror的自定义属性,并选中Assign方法复选框。在 callerror_assign方法中输入以下代码。

Callerror_assign方法:

****************************************************
LPARAMETERS vNewVal
if type(\"vNewVal\") == \"C\"
=messagebox(\"vNewVal\")
endif
THIS.callerror = m.vNewVal
****************************************************

关闭类设计器。

现在给主调用表单添加一个新的属性SwapError。在表单的load方法中设置类库。

Form Load

****************************************************
select customer
scatter name thisform.thisrec blank
if !\"CCALL\"$set(\"classlib\")
set classlib to CCALL
endif
**************************************************

【译者注:作者在这里有一个疏忽,类库的文件名是cclass.vcx,因此应该把上面的代码中的CCALL都改为CCLASS】

在表单的init事件中添加错误对象(error object).

表单 Init事件:

*****************************************************
thisform.swaperror = createobject(\"callclass\")
*****************************************************

现在给command2.click方法中的do form命令添加一个参数

Command2.Click

****************************************************
&& thisform.swapfields(thisform.thisrec)
do form swapper with thisform.thisrec, thisform.swaperror
this.enabled = .F.
thisform.refresh()
****************************************************

现在编辑被调用表单。添加一个用于接受新错误对象的属性HasError。在init事件中,添加Lparameters语句给表单的一个属性赋值一个错误对象参数。

Form Init

*********************************************************
LParameters oRecObject, oError
thisform.recobject = oRecObject
thisform.HasError = oError
thisform.text1.controlsource = \"thisform.recobject.firstname\"
thisform.text2.controlsource = \"thisform.recobject.surname\"
*********************************************************

下面我们所有在这里做的只是如果姓为空或长度小于2的情况则给出一个返回路径,以避免给姓赋值一个单字母数据。象下面一样编辑Command1.click方法:

Command1 Click方法:

*********************************************************
Local Tempstore
Tempstore = alltrim(thisform.recobject.firstname)
if len(Tempstore) > 2
thisform.recobject.firstname = thisform.recobject.surname
thisform.recobject.surname = Tempstore
else
thisform.HasError.callerror = \"Incorrect surname\"
endif
thisform.release()
*********************************************************

就 是这样。现在修改一个纪录的数据,使它的姓为单字母数据,然后试着交换(swap)它。被调用的表单将改变HasError对象的callerror属性 (它有一个可被触发的Assign方法)。虽然对话框(messagebox)看起来出自被调用表单,事实上是主调用表单的对象触发它的。以后当我们模拟 一个来自VFP dll的事件时,记住这一点是重要的。

第五部分:VFP6 COM dll

建立一个COM dll

继续同样的交换字段的主题,这个部分是在VFP6中建立一 个COM dll。第一步是建立一个新的项目swapcom,并且给它添加一个自定义类swapcom保存在cswapcom.vcx类库中。编辑这个类,选择类/ 类信息菜单,从弹出的窗口中选中“OLE 公共”复选框。下一步给类添加一个自定义方法swapfields,代码如下:

swapfields 方法:

****************************************************
LParameters oRecobject
Local TempStore
TempStore = alltrim(oRecobject.Firstname)
if len(TempStore) > 1
oRecobject.Firstname = oRecobject.Surname
oRecobject.Surname = Tempstore
endif
****************************************************

那 就是OLE COM准备做的。关闭类,然后项目窗口中选择“连编”。连编为一个单线程或多线程COM服务器。【译者注:在“连编选项”的“操作”单选按钮组中选择“连 编 COM dll”】产生的dll现在准备注册并可以被任何能调用COM 服务器的应用程序使用了。当然,你最好需要一个在COM 服务器中的错误检查,不过,那不是这篇文章的内容。

我认为在这里错误检查比普通的程序更重要。如果你正在与ASP的COM dll一起使用你自己的COM dll,最多只是停止反应或锁定服务器。就是那样——没有更多的损失。

要 看看发生了什么,在连编了动态连接库后,在命令窗口中输入MyCom = createobject(\"swapcom.swapcom\"),接着输入MyCom.swapfields(),回车,将会发生一个错误。下一步 是打开我们的主调用窗体,然后编辑代码,通过调用我们的新的COM dll来代替调用第二个窗体。

这很简单。注销(REM)在command2.click方法中的do form语句,使代码看起来是下面这样:

******************************************************
&& thisform.swapfields(thisform.swapfields)
&& do form swapper with thisform.thisrec, thisform.swaperror
MyCom = createobject(\"swapcom.swapcom\")
MyCom.swapfields(thisform.thisrec)
this.enabled = .F.
Release MyCom
MyCom = .NULL.
thisform.refresh()
*****************************************************

运行表单,它将字段交换一圈,除非我们的例外发生或名字少于两个字符。下一步,我们将结束给COM dll添加错误事件。

第六部分: 总 结

使用assign从VFP COM dll中得到返回

这 一系列文章的目的,是显示一个VFP COM dll能够给调用它的表单一个返回信息。作为一个COM部件,VFP看来没有事件。这意味着Microsoft站点上的VFPCOM.dll能够被大多数 COM应用程序使用,除了VFP COM!!!当COM已经做完了自己的工作而整个程序需要一个返回来作为终点时,我认为这简直是个耻辱。在我的方案里,我希望模块化我的程序并且需要得到 返回。在文章的最后部分(第七部分)我将表露希望得到返回的真实原因。在这里,先讲错误返回。我们已经有了错误的代码,所以我们所需要做的是把输出错误的 do form版本改编成我们新的COM版本。

打开调用表单,并且编辑Command2.click事件代码来包含错误对象。

Command2 click

******************************************************
&& thisform.swapfields(thisform.swapfields)
&& do form swapper with thisform.thisrec, thisform.swaperror
MyCom = createobject(\"swapcom.swapcom\")
MyCom.swapfields(thisform.thisrec, thisform.swaperror)
this.enabled = .F.
Release MyCom
MyCom = .NULL.
thisform.refresh()
*****************************************************

打开COM dll项目并且打开自定义类swapcom,添加LParameters参数和if/endif语句的其它部分,使它看起来象下面这样:

****************************************************
LParameters oRecobject, oError
Local TempStore
TempStore = alltrim(oRecobject.Firstname)
if len(TempStore) > 1
oRecobject.Firstname = oRecobject.Surname
oRecobject.Surname = Tempstore
else
oError.callerror = \"Incorrect Surname\"
endif
****************************************************

重新连编COM dll并且运行表单。就是那样。我还添加了一个最后部分以说明我想做的一件事。

第七部分 结 尾

这是关于Assign方法和COM dll事件传奇的最后部分。在这里,我将对实时工作的事件以及程序并不是象看起来的那样已经执行完毕的加个证明。这些仍然很简单但有助于思考。

首 先建立一个新的COM dll。建立一个新的项目Countercom,添加一个自定义类“counter\"。打开类菜单下的“类信息”菜单项,选中“OLE 公共”复选框。给自定义类添加一个自定义属性“countclass”,它将被用来保存从调用表单传递过来的对象\0;给自定义类添加一个自定义方法 “countdown”,在方法里,输入下面的代码:

countdown method

*****************************************************************
LParameters oCountobject
Local currentcount, stepseconds
this.countclass = oCountobject
currentcount = 0
stepseconds = seconds() + 1
do while currentcount < 10
if seconds() > stepseconds
currentcount = currentcount + 1
stepseconds = seconds() + 1
this.countclass.countdown = alltrim(str(currentcount*10)) + \"%\"
endif
enddo
*****************************************************************

随便连编这个COM dll为单线程或多线程dll,关闭这个项目。

下 一步,是用来掌握已赋值的属性(assigned property)的类,在“countclass”类库中建立一个自定义类“Countreq”。在那个类里建立一个属性“countdown”同时选 中Assign复选框。现在你会有一个名叫countdown_assign的自定义方法,向该方法中输入以下代码:

countdown_assign

******************************************************************
LParameters vNewVal
if type(\"vNewVal\") == \"C\"
_screen.activeform.currentx = 10
_screen.activeform.currenty = 10
_screen.activeform.print(padr(vNewVal,20,\" \"))
endif
THIS.countdown = m_vNewVal
******************************************************************

最后是主调用表单。建立一个名叫\"Countcomfrm\"的新表单,表单上有一个命令按钮。给表单添加下面的代码段:

Form Load

******************************************************************
set classlib to countclass
public counterobject
******************************************************************

Form Init

******************************************************************
thisform.countcom = createobject(\"countreq\")
******************************************************************

Form Unload

******************************************************************
if type(\"countobject\") == \"O\"
release countobject
countobject = .NULL.
endif
thisform.release(\"thisform.countcom\")
thisform.countcom = .NULL.
******************************************************************

还有就是命令按钮的代码

Command1 Click

******************************************************************
thisform.currentx = 10
thisform.currenty = 10
thisform.print(\"Starting\")
if !type(\"countobject\") == \"O\"
countobject = createobject(\"countercom.counter\")
endif
countobject.countdown(thisform.countcom)
thisform.currentx = 10
thisform.currenty = 10
thisform.print(\"Complete\")

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多