vfp编程实例及提高 vfp教程 VFP编程入门到精通教程 vfp教程

本页已使用福昕阅读器进行编辑。

福昕软件(C)2005-2010,版权所有,

仅供试用。

第三课 对象、属性、事件、方法

对象(object):就是我们软件中所看到表单、文本框、按钮、标签等等东西,一个软件的外观主要也就是由这些东西组成,那么我们要编软件就应该掌握各种对象的使用方法。

可放在表单内的对象,如按钮、文本框等也常常被叫作控件,我们在后面的课程中也经常会用到这个词。

所谓面向对象的编程,就是我们在编程的过程中是看着这些东西来编程,而不是用一大堆的语言代码来编出这些东西,因此面向对象的编程非常直观,在编的过程中就能看见程序运行起来的样子。

另外由于不需用语言来构造这些对象,只是象画图一样将它们画出来,其大小及位置也不须用精确的数字来表示,你喜欢画多大就画多大,您无须知道诸如长是100、宽是80(当然您想知道的话也可以在属性窗口中查到),这样使得编程变得非常简单。

属性(property):就是对象的性质,如长、宽、放的位置、颜色、标题、字体大小等等。为了达到我们所编软件的目的,也为了使软件运行的时候各种界面看起来舒服,我们必须在设计软件时对每个对象的有关属性做适当的设置。所谓"有关",就是对于一个对象来说,在一个软件中只有部分与这个软件有关的属性需要设置,而大部分可能不需要设置,只需使用它们隐含的设置就行了,而同一种对象在另一个地方,可能需要设置的属性又不同了。

对于属性的设置,有些只需用鼠标做适当的拖动即可,如长、宽、放的位置等,当然它们也可以在属性窗口中设置,另一些则必须在属性窗口中进行设置,如字体、颜色、标题等。

事件(event):就是可能会发生在对象上的事情,也可以说我们对对象所做的操作(或者系统对某个对象的操作),如按钮被按动(单击)、对象被拖动、被改变大小、被鼠标左键双击等等。在一个软件中,总会有一些对象在运行时会被我们操作,当然我们不是平白无故去操作一个对象,总是希望这个对象在被我们操作后能做出我们所期望的反应,如"退出"按钮在被按动后,我们就希望它使软件结束运行。

而这一反映即使是同一种对象在不同的软件中,或在同一个软件中的不同地方,我们所需要的反映都是不同的,比如同样是按钮,“开始”和“退出”在按下后所产生的效果不同。

为了使得对象在某一事件发生时能够做出所需要的反应,我们就必须针对这一事件编出相应的程序代码来完成我们的目标。如一个对象的某个事件被编入了相应的代码,那么软件运行时,当这一事件发生(如按钮被按动),相应的程序段就被激活,并开始执行,如这一事件不发生,则这段程序就不会运行。

而没有编有代码的事件,即使发生也不会有任何反应。

方法(method):也叫“方法程序”。前面三个概念在上一课已接触过了,“方法”则是个新概念,它是指对象所固有完成某种任务的功能,可由我们在需要的时候调用。

“方法”与“事件”有相似之处,都是为了完成某个任务,但同一个事件可完成不同任务,取决于您所编的代码是怎样的,而方法则是固定的,任何时候调用都是完成同一个任务,所以其中的代码也不需要我们编了,FOXPRO系统已为我们编好(我们也看不见),只需在必要的时候调用即可。

比如:文本框可以用以显示文字,也可以输入文字,假如一个表单上有三个文本框,那么我们打字的时候,字进入哪个框呢?这就要看当前的焦点在哪个框上,一般我们可以用鼠标点一下所要的框,即将焦点放到了这个框上,有时我们会让软件自动

1

地将焦点放在某个框上,这时就要调用“设置焦点”方法(setfocus),例如我们要把焦点放到第二个文本框上,调用的方法如下:

text2.setfocus

至于它是怎么将焦点放上去的,也就是说具体放的程序是怎么编的,我们不用知道,只要能达到的目的就行。

当然不但是文本框,其它的对象也都有此方法(有些方法只有某些对象才有),调用的一般语法是:

对象名称.setfocus

举一个实例来说明:当我们的幸运7游戏软件启动后,我们会看到在第一个文本框text1中有一个光标在闪动,也就是此时焦点在text1上,但我们并不需要在其中输入文字,因此有个光标在闪动看起来很不舒服,此时需要的是按“开始”按钮,所以焦点应在“开始”按钮上(这样按回车键也可以按下“开始”按钮),因此我们应该在整个表单启动的时候将焦点设到此按钮上。

当我们启动一个表单的时候,该表单的“激活”事件(activate)就会发生,但由于一般情况下这一事件中没有编入代码,所以即使发生我们也感觉不出来。那么现在我们就要借用这个事件再去调用setfocus方法,便可达到我们目的。具体做法是这样:

打开xy7项目管理器,找到xy7表单,按“编辑”进入表单设计器,调出属性窗口,看一下窗口上面的对象选择窗中是否是“form1”,如图1,如不是则选择到“form1”。

选择“方法程序”(“全部”也行),在第一行找到“activate event”事件,如图

2,

双击此事件进入程序编辑窗口,输入下面代码:

thisform.command1.setfocus

意思是“本表单.command1对象.设置焦点”,command1即是“开始”按钮的名称。

输入完程序后,存盘、退出。这样一旦软件开始运行,表单被启动-该表单的“激活”事件发生-其中的程序开始执行-将焦点设到command1即开始按钮,焦点设在上面后会看到有一个虚线框住按钮标题,这时text1中就没有光标在闪动了。 还不赶快试一下!

VFP 初级教程——第四课 常用的对象、属性、事件、方法 —方法(methord)

设置焦点方法(setfocus)

2

将焦点放到控件上。 语法:

控件.setfocus 备注:

一旦一个控件获得了焦点,操作所做的输入都是针对此控件。

常用到的对象

按钮(commandbutton) 文本框(text) 表格(grid)

刷新方法(refresh)

重新显示对象,并把它们的各个值设为最新。 语法:

表单.对象.Refresh 备注:

一般说来,系统会自动在需要的时候为对象刷新,但并不总是有效,有时我们会看到在程序执行过程中改变了一个对象的某个值,但这个对象却没有任何反应,这时我们就调用一下刷新方法程序,来将这个对象刷新一下。

常用到的对象

表单(form) 按钮(commandbutton) 文本框(text) 表格(grid)

释放方法(release)

释放一个表单,也就是关闭一个表闭。 语法:

表单.Release

常用到的对象

表单(form)

VFP 初级教程——第四课 常用的对象、属性、事件、方法 —事件(event)

单击事件(click)

3

当一个对象被鼠标左键点击时发生此事件。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label) 文本框(text) 表格(grid)

初始化事件(init)

当一个对象被创建时此事件发生。 举例:

在幸运7程序中,我们也可以不属性窗口中把“输”、“赢”两标签的可见属性设为假,而在这两个标签的init事件中加上如下语句:

this.visible=.f.

这样当两标签形成时,它们的init事件被激活,执行上面语句,同样可达到使两标签看不见的效果。 备注:

容器中对象的init事件比容器的init事件早发生,这一点在程序编制过程中也是很有用的,籍此我们可以在容器的init事件对容器中的对象进行访问,比如还是上面那个例子,可以不在每个标签的init事件中写入程序,而只在表单的init事件中写入如下语句即可:

thisform.label1.visible=.f.

thisform.label2.visible=.f.

这样可以达到同样效果。

而容器中各对象的init事件发生的先后顺序,取决于编程时它们被添加到表单中的先后顺序。

常用到的对象

几乎所有对象都有此事件。

VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)

标题属性(caption)

指定对象标题所显示的文本,主要用于指示该对象的用途。

在程序设计和运行时都可更改。 语法:

对象.Caption=cText

4

设置

其中的cText即为对象标题设置的文本。

备注:

标题属性对于不同的对象其显示是不同的:

表单:标题显示在表单的标题栏中,如图1。当表单缩到最小,标题文本显示在该表单图标的旁边,如图2。

其它控件:有的标题显示在控件上面,有的显示在其旁边。

当您创建一个新的表单或控件,它的隐含标题与它的隐含名称属性是一样,但实际上是两回事,当您要指定一个对象时您必须用它的名称,而不能用它的标题。比如您要在一个表单上创建一个退出按钮,如果它是在表单上创建的第二个按钮,那么它的隐含标题很可能是command2,名称也是同样的,当然您很可能马上会将其标题改为“退出”,但其名称仍是command2,如您要在程序中调用这个按钮,必须用它的名称来指定调用的是那个对象,例如:要用程序把焦点放在这按钮上,程序应这样写:

command2.setfocus

而不能写成:

退出.setfocus

表单(form) 按钮(commandbutton) 标签(label)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

左起始位属性(left)

设定对象的左边起始位置。

也就是该对象的左边界与容纳该对象的容器的左边界的距离,比如一个表单中的按钮,表单就是按钮的容器,按钮左边界与表单左边界的距离就是按钮的左起始位置。

在程序设计和运行时都可更改。

一个表单的隐含容器为VFP主窗口。 语法:

对象.Left=nDist

其中的nDist为代表左边界的数值,使用时用一具体数值代入。 备注:

一般不需要在程序中用以上语句来设置对象的左边界,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其左边界就设好了。当我们用鼠标改变其位置时,其左边界也会相应变化。

当然如果我们要在程序运行的过程中,改变某对象的左边界,以达到改变对象位置的目的,就可用到上面的语句了。 5

例如:

我们要在程序中把按钮command1的左边界设为10,实现的语句为:

command1.left=10

此语句一旦运行,command1按钮就会移动到其左边界为10的地方。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

上起始位属性(top)

设定对象的上边起始位置。

也就是该对象的上边界与容纳该对象的容器的上边界的距离,比如一个表单中的按钮,表单就是按钮的容器,按钮左边界与表单上边界的距离就是按钮的上起始位置。

在程序设计和运行时都可更改。

一个表单的隐含容器为VFP主窗口。 语法:

对象.Top=nValue

其中的nValue为代表左边界的数值,使用时用一具体数值代入。 备注:

一般不需要在程序中用以上语句来设置对象的上边界,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其上边界就设好了。当我们用鼠标改变其位置时,其上边界也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的上边界,以达到改变对象位置的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的上边界设为10,实现的语句为:

command1.top=10

此语句一旦运行,command1按钮就会移动到其上边界为10的地方。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label) 文本框(text) 表格(grid)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

宽度属性(width)

6

设定对象的宽度。

在程序设计和运行时都可更改。 语法:

对象.Width=nWidth

nWidth为指定宽度的数值。 备注:

一般不需要在程序中用以上语句来设置对象的宽度,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其宽度就设好了。当我们用鼠标改变其大小时,其宽度也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的宽度,以达到改变对象大小的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的宽度设为10,实现的语句为:

command1.width=10

此语句一旦运行,command1按钮的宽度就会为10了。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label) 文本框(text) 表格(grid)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

高度属性(height)

设定对象的高度。

在程序设计和运行时都可更改。 语法:

对象.Height=nHeight

nHeight为指定宽度的数值。 备注:

一般不需要在程序中用以上语句来设置对象的高度,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其高度就设好了。当我们用鼠标改变其大小时,其高度也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的高度,以达到改变对象大小的目的,就可用到上面的语句了。 例如:

7

我们要在程序中把按钮command1的高度设为10,实现的语句为:

command1.height=10

此语句一旦运行,command1按钮的高度就会为10了。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label) 文本框(text) 表格(grid)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

控制源属性(controlsource)

指定与对象邦定的数据源。

一般是指一个变量或数据库字段,比如对于一个文本框来说,指定一个变量为其控制源,那么在文本框中输入的数据就会存贮到这个变量中。

在程序设计和运行时都可更改。 语法:

对象.ControlSource=cName

设置:

cName 即为控制变量名,可以是一个变量,也可以是一个数据库字段。 备注:

一旦某对象控制源属性设置到了一字段或变量,该对象的“值”属性与控制源相同。

一般情况下,我们不需要用上面的语句为对象设置控制源,而只需在面向对象编程时,当设好一对象后,在属性窗口中找到contorlsource属性,输入相应的变量或字段名即可。 例如:

在程序中要将文本框text1中所输入的数据放在变量sj中,则将text1的控制源设为sj,实现的语句为:

text1.controlsource='sj'

常用到的对象

文本框(text)

VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)

8

值属性(value)

指定控件当前的状态。

比如一个文本框当前的内容是什么。

在程序设计和运行时都可更改。 语法:

控件.Value=nSetting

设置:

nSetting对于文本框来说即是在文本框中输入的字符串或数值、日期、逻辑值等等。隐含的数据类型为字符型。 备注:

对于表格控件,其值只有在表格获得焦点的时候才可更改。

当控件的控制源做了设置,那么它的值与控制源的值相同。

一般在我们的程序中都是用控制源来控制控件的值。

常用到的对象

文本框(text)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)] 文本对齐属性(alignment)

指定控件文本的对齐方式。即左、中、右。

在程序设计和运行时都可更改。 语法:

控件.Alignment=nAlign

设置:

nAlign对于文本框来说,可作的设置如下:

Setting Description

0 左

1 右

2 中

3 自动(隐含),即文本靠左,数值靠右。

常用到的对象

文本框(text)

9

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

名称属性(name)

指定对象被调用的名称。

在程序设计和运行时都可更改。 语法:

对象.Name=cName

设置:

cName即为给对象指定的名称文本。 备注:

对象隐含的名称一般是对象的类型再加一个整数,比如:创建的第一个表单就叫form1,在一个表单中创建的第三个文本框就被叫做text3。当然您可以修改为任何您所喜欢的名称,但为了简便起见我们一般都不改。

名称和标题不同,虽然隐含时它们是一样的,当您要调用一个对象时必须用它的名称,而不能用它的标题(caption),参见标题属性。所以当您改了一个对象的标题时,并不表示它的名称也改了,它的名称还是原来的,您可以在属性窗口的name属性中看到。

如果一个对象是容器型对象(其中包含有别的对象),而它又是最外一层的容器(因为容器中还可有容器),那么在程序中第一次调用它时,应将它附给一个变量,以后调用它时,用这个变量名,而不用它自己的名称。比如一般我们在程序中第一次调用一个表单往往是用类似如下的语句:

do form xy7 name lucky

这里的lucky就是xy7表单所附于的变量名,您可以用任何变量名,只要不与程序中的其它变量重名,当然也可以用与表单名一样的变量名(我们常常是这样),以后在调用这个表单时就用这个变量名,如把焦点设到这个表单中的command1按钮,程序为:

lucky.command1.setfocus

而不是:xy7.command1.setfocus

当在一个对象内部的事件程序中调用本对象时,可用“this”代替对象名称,比如在幸运7表单中,在按过开始按钮后,我们想把这个按钮的标题设为“再开始”,那么可在这个按钮的click事件中加上如下语句:

this.caption='再开始'

它等同于command1.caption='再开始',但当这个按钮的名称改为“command3”时,这一句就不对了,必须做修改才能正确运行,但上一句不用改,仍然可以用。

如果这个对象是表单,那么调用它自己时不能用“this”,而用“thisform”代替名称或名称变量。

常用到的对象

10

所有对象都要用。

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

只读属性(readonly)

确定使用者是否可以编辑控件。

在程序设计和运行时都可更改。 语法:

表单.控件.ReadOnly=lExpr

设置:

lExpr 可设置的值有:

.t. 真,即控件不可编辑

.f. 假,即控件可编辑 备注:

如果要让一个件一开始就是指读(不可编辑)的,可在程序设计时将其设为指读,在属性窗口中找到readonly属性,将其设T(真)即可。

如要在程序运行时,使一个控件变为指读,就可采用上面的语句,比如将xy7表单中的text1文本框设为指读,程序为: xy7.text1.readonly=.t.

注意,这里的xy7是启动表单时为该表单所指定的变量名。 举例:

以我们在第二课中的幸运7程序为例,在程序启动后,可以在上面三个文本框中输入字符,但这是我们所不需要的,为了避免这个问题可以在设计程序时,将三个文本框设成指读,这样就不能输入字符了。不信您试试。

常用到的对象

文本框(text) 表格(grid)

[dvnews_page=VFP 初级教程——第四课 常用的对象、属性、事件、方法 —属性(property)]

可见属性(visible)

设定一个控件是否可以看见。

在程序设计和运行时都可更改。 语法:

11

对象.Visible=lExpr

设置:

lExpr 可设置的值有:

.t. 真,即可见

.f. 假,即不可见 备注:

就如同我们在幸运7程序中对“输”、“赢”两字的处理。

常用到的对象

几乎所有对象都用。

VFP 初级教程——第四课 常用的对象、属性、事件、方法 —表单对象(form) 本课介绍一些常用的对象及其属性、事件、方法,掌握了这些知识以后,便可编一些简单实用的软件了。 对象(object):

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

表单对象(form)

用于放置其它对象(其它的对象也常常叫做“控件”)的界面,或叫窗口。

常用属性:

标题(caption) 左起始位(left) 上起始位(top)

宽度(width) 高度(hight) 控制源(controlsource) 名称(name) 可见(visible)

常用事件:

单击(click) 初始化(init)

常用方法:

刷新(refresh) 释放(release)

标题属性(caption)

指定对象标题所显示的文本,主要用于指示该对象的用途。

12

在程序设计和运行时都可更改。 语法:

对象.Caption=cText

设置

其中的cText即为对象标题设置的文本。

备注:

标题属性对于不同的对象其显示是不同的:

表单:标题显示在表单的标题栏中,如图

1。

当表单缩到最小,标题文本显示在该表单图标的旁边,如图

2。

其它控件:有的标题显示在控件上面,有的显示在其旁边。

当您创建一个新的表单或控件,它的隐含标题与它的隐含名称属性是一样,但实际上是两回事,当您要指定一个对象时您必须用它的名称,而不能用它的标题。比如您要在一个表单上创建一个退出按钮,如果它是在表单上创建的第二个按钮,那么它的隐含标题很可能是command2,名称也是同样的,当然您很可能马上会将其标题改为“退出”,但其名称仍是command2,如您要在程序中调用这个按钮,必须用它的名称来指定调用的是那个对象,例如:要用程序把焦点放在这按钮上,程序应这样写:

command2.setfocus

而不能写成:

13

退出.setfocus

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

左起始位属性(left)

设定对象的左边起始位置。

也就是该对象的左边界与容纳该对象的容器的左边界的距离,比如一个表单中的按钮,表单就是按钮的容器,按钮左边界与表单左边界的距离就是按钮的左起始位置。

在程序设计和运行时都可更改。

一个表单的隐含容器为VFP主窗口。 语法:

对象.Left=nDist

其中的nDist为代表左边界的数值,使用时用一具体数值代入。 备注:

一般不需要在程序中用以上语句来设置对象的左边界,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其左边界就设好了。当我们用鼠标改变其位置时,其左边界也会相应变化。

当然如果我们要在程序运行的过程中,改变某对象的左边界,以达到改变对象位置的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的左边界设为10,实现的语句为:

command1.left=10

此语句一旦运行,command1按钮就会移动到其左边界为10的地方。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

上起始位属性(top)

设定对象的上边起始位置。

也就是该对象的上边界与容纳该对象的容器的上边界的距离,比如一个表单中的按钮,表单就是按钮的容器,按钮左边界与表单上边界的距离就是按钮的上起始位置。

14

在程序设计和运行时都可更改。

一个表单的隐含容器为VFP主窗口。 语法:

对象.Top=nValue

其中的nValue为代表左边界的数值,使用时用一具体数值代入。 备注:

一般不需要在程序中用以上语句来设置对象的上边界,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其上边界就设好了。当我们用鼠标改变其位置时,其上边界也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的上边界,以达到改变对象位置的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的上边界设为10,实现的语句为:

command1.top=10

此语句一旦运行,command1按钮就会移动到其上边界为10的地方。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

宽度属性(width)

设定对象的宽度。

在程序设计和运行时都可更改。 语法:

对象.Width=nWidth

nWidth为指定宽度的数值。 备注:

一般不需要在程序中用以上语句来设置对象的宽度,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其宽度就设好了。当我们用鼠标改变其大小时,其宽度也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的宽度,以达到改变对象大小的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的宽度设为10,实现的语句为:

15

command1.width=10

此语句一旦运行,command1按钮的宽度就会为10了。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

高度属性(height)

设定对象的高度。

在程序设计和运行时都可更改。 语法:

对象.Height=nHeight

nHeight为指定宽度的数值。 备注:

一般不需要在程序中用以上语句来设置对象的高度,在面向对象编程时,一旦对象设置好,或者说用鼠标拉好,其高度就设好了。当我们用鼠标改变其大小时,其高度也会相应变化。

当然如果我们要在程序运行的过程中,想改变某对象的高度,以达到改变对象大小的目的,就可用到上面的语句了。 例如:

我们要在程序中把按钮command1的高度设为10,实现的语句为:

command1.height=10

此语句一旦运行,command1按钮的高度就会为10了。

常用到的对象

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

控制源属性(controlsource)

指定与对象邦定的数据源。

一般是指一个变量或数据库字段,比如对于一个文本框来说,指定一个变量为其控制源,那么在文本框中输入的数据就会存贮到这个变量中。

在程序设计和运行时都可更改。 语法:

16

对象.ControlSource=cName

设置:

cName 即为控制变量名,可以是一个变量,也可以是一个数据库字段。 备注:

一旦某对象控制源属性设置到了一字段或变量,该对象的“值”属性与控制源相同。

一般情况下,我们不需要用上面的语句为对象设置控制源,而只需在面向对象编程时,当设好一对象后,在属性窗口中找到contorlsource属性,输入相应的变量或字段名即可。 例如:

在程序中要将文本框text1中所输入的数据放在变量sj中,则将text1的控制源设为sj,实现的语句为:

text1.controlsource='sj'

常用到的对象

文本框(text)

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

名称属性(name)

指定对象被调用的名称。

在程序设计和运行时都可更改。 语法:

对象.Name=cName

设置:

cName即为给对象指定的名称文本。 备注:

对象隐含的名称一般是对象的类型再加一个整数,比如:创建的第一个表单就叫form1,在一个表单中创建的第三个文本框就被叫做text3。当然您可以修改为任何您所喜欢的名称,但为了简便起见我们一般都不改。

名称和标题不同,虽然隐含时它们是一样的,当您要调用一个对象时必须用它的名称,而不能用它的标题(caption),参见标题属性。所以当您改了一个对象的标题时,并不表示它的名称也改了,它的名称还是原来的,您可以在属性窗口的name属性中看到。

如果一个对象是容器型对象(其中包含有别的对象),而它又是最外一层的容器(因为容器中还可有容器),那么在程序中第一次调用它时,应将它附给一个变量,以后调用它时,用这个变量名,而不用它自己的名称。比如一般我们在程序中第一次调用一个表单往往是用类似如下的语句:

17

do form xy7 name lucky

这里的lucky就是xy7表单所附于的变量名,您可以用任何变量名,只要不与程序中的其它变量重名,当然也可以用与表单名一样的变量名(我们常常是这样),以后在调用这个表单时就用这个变量名,如把焦点设到这个表单中的command1按钮,程序为:

lucky.command1.setfocus

而不是:xy7.command1.setfocus

当在一个对象内部的事件程序中调用本对象时,可用“this”代替对象名称,比如在幸运7表单中,在按过开始按钮后,我们想把这个按钮的标题设为“再开始”,那么可在这个按钮的click事件中加上如下语句:

this.caption='再开始'

它等同于command1.caption='再开始',但当这个按钮的名称改为“command3”时,这一句就不对了,必须做修改才能正确运行,但上一句不用改,仍然可以用。

如果这个对象是表单,那么调用它自己时不能用“this”,而用“thisform”代替名称或名称变量。

常用到的对象

所有对象都要用。

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

可见属性(visible)

设定一个控件是否可以看见。

在程序设计和运行时都可更改。 语法:

对象.Visible=lExpr

设置:

lExpr 可设置的值有:

.t. 真,即可见

.f. 假,即不可见 备注:

就如同我们在幸运7程序中对“输”、“赢”两字的处理。

常用到的对象

18

几乎所有对象都用。

[dvnews_page=第四课 常用的对象、属性、事件、方法(1)]

常用事件:

单击事件(click) 初始化(init)

当一个对象被鼠标左键点击时发生此事件。

表单(form) 按钮(commandbutton) 标签(label)

文本框(text) 表格(grid)

初始化(init)

常用方法:

刷新(refresh)

刷新方法(refresh)

重新显示对象,并把它们的各个值设为最新。 语法:

表单.对象.Refresh 备注:

一般说来,系统会自动在需要的时候为对象刷新,但并不总是有效,有时我们会看到在程序执行过程中改变了一个对象的某个值,但这个对象却没有任何反应,这时我们就调用一下刷新方法程序,来将这个对象刷新一下。

常用到的对象

表单(form) 按钮(commandbutton) 文本框(text) 表格(grid)

释放方法(release)

释放一个表单,也就是关闭一个表闭。 语法:

表单.Release

常用到的对象

表单(form)

VFP 初级教程——第五课 常用编程命令及常用函数(1)

注:在语法中如用方括号“[ ]”括起来的词句,表示可以不用。

函数

19

数值转换字符函数(str())

返回与指定数值表达式对应的字符。 语法:

str(数值表达式[,长度[,小数位数]]) 返回值的类型

字符型 参数:

数值表达式:要被转换为字符的数值表达式。

长度:转换后字符的长度。该长度等于小数点和小数点右边第个数字所占字符的数目总和。

如果指定长度大于所需长度,自动在前面加空格补齐。

如果指定长度小于所需长度,返回一串星(*)号,表示数值溢出。

如省略长度,则默认长度为10。

小数位数:指定返回字符串中的小数位数。

如指定位数小于实际位数,则返回值四舍五入。

如指定位数大于实际位数,则加0补齐。

如省略小数位数,默认为0。

在指定了小数位数的情况下,如指定长度(第二个参数)小于总长度,但大于整数长度,则返回对小数部分做了四舍五入的字符。 备注:

返回后的值看起来还是数的形式,但它的数据类型已经变了,不再是一个数值,也就是不能再用来做加、减、乘、除的算术运算,但可以和字符进行加减。比如:

? '季度'+1

就会出错,因为一个字符是不能和一个数值相加的。写成如下形式就可以了:

? '季度'+str(1,1)

结果是:

季度1

注意,这里一定要指定长度,否则由于默认长度是10,就会出现如下结果:

季度 1

20

假如不知道数值有几位数怎么办呢?请参见ltrim()函数。

字符转换数值函数(val())

将数字组成的字符表达式转换成数字值。 语法:

val(字符表达式) 返回值的类型

数值型 参数:

字符表达式:要被转换为数值的字符表达式。该表达式由最多16位的数字组成,若超过16位,则对其圆整。 备注:

val()函数从左到右返回字符表达式中的数字,直到遇到非数值型字符(忽略前面的空格)时为止。 若字符表达式的第一个字符不是数字,也不是正、负号,则返回0。 举例:

a='123'

如果按下面的写法,就会出错,因为一个字符不能与一个数值相加:

? a+3

写成如下形式便可以了:

? val(a)+3

结果是126。

取系统日期函数(date())

返回由操作系统控制的当前系统日期。 语法:

date() 返回值的类型

日期型

[dvnews_page=VFP 初级教程——第五课 常用编程命令及常用函数(1)]

取年份函数(year())

21

从指定的日期表达式中返回年份。 语法:

year(日期表达式) 返回值的类型

数值型 参数:

日期表达式:指定的日期表达式,该函数即是返回其年份值。 举例:

? year(date())

如果当前的系统日期是1999年2月5日,则显示的结果为1999。 取月份函数(month())

从指定的日期表达式中返回月份。 语法:

month(日期表达式) 返回值的类型

数值型 参数:

日期表达式:指定的日期表达式,该函数即是返回其月份值。 举例:

? month(date())

如果当前的系统日期是1999年2月5日,则显示的结果为2。 取天日函数(day())

以数值型返回日期表达式是当月的第几天。 语法:

day(日期表达式) 返回值的类型

数值型

22

参数:

日期表达式:指定的日期表达式,该函数返回该日期是当月的第几天。 举例:

? day(date())

如果当前的系统日期是1999年2月5日,则显示的结果为5。

[dvnews_page=VFP 初级教程——第五课 常用编程命令及常用函数(1)] 取整函数(int())

返回数值表达式值的整数部分。 语法:

int(数值表达式) 返回值的类型

数值型 参数:

数值表达式:指定的数值表达式,该函数返回其整数部分。 举例:

? int(123.47)

结果是123。

四舍五入函数(round())

对指定表达式进行四舍五入运算,并把结果返回。 语法:

round(数值表达式,小数位数) 返回值的类型

数值型 参数:

数值表达式:指定的数值表达式,该函数返回其四舍五入后的值。 小数位数:保留的小数位数。 举例:

23

? int(123.457,2)

结果是123.46。

删除标记函数(delete())

确定当前记录是否已做删除标记,若已做删除标记,返回真(.t.),否则返回假(.f.)。 语法:

delete([表别名|工作区]) 返回值的类型:

逻辑型 参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区去检测另一工作区的表的记录是否做了删除标记,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。

[dvnews_page=VFP 初级教程——第五课 常用编程命令及常用函数(1)]

记录号函数(recno())

返回当前表或指定表中的当前记录号。 语法:

recno([表别名|工作区]) 返回值的类型:

数值型 参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区去得到另一工作区的表的当前记录号,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。

记录数函数(reccount())

返回当前表或指定表中的记录数目。 语法:

reccount([表别名|工作区]) 返回值的类型:

数值型

24

参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区去得到另一工作区的表的记录数,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。

找到记录函数(found())

如果locate、continue、seek等查找记录的命令成功(即找到了记录),该函数返回“真”(.t.),否则返回“假”(.f.)。 语法:

found([表别名|工作区]) 返回值的类型:

逻辑型 参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区知道另一个工作区上次查找记录是否找到,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。 举例:

locate for 姓名='庄稼'

?found()

如找到记录,显示结果为.t.,否则为.f.。

[dvnews_page=VFP 初级教程——第五课 常用编程命令及常用函数(1)]

表结尾函数(eof())

确定记录指针是否在表的结尾处。 语法:

eof([表别名|工作区]) 返回值的类型:

逻辑型 参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区知道另一个工作区表的指针是否在尾部,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。 备注:

25

这里所说的结尾处,并非是指最后一个记录,而是最后一个记录的后面,即没有记录。 举例:

go bottom

skip

?eof()

显示结果为.t.。

如果没有skip,仅仅走到最后一个记录,返回的将是.f.,而必须再往下跳一下才会是“真”结尾。

此时如输入命令:display,则没有记录显示。这一点与bof()函数不同。

表开头函数(bof())

确定记录指针是否在表的开头。 语法:

bof([表别名|工作区]) 返回值的类型:

逻辑型 参数:

表别名|工作区:该参数指定所要确定的表的别名或所在工作区,即您可以在一个工作区知道另一个工作区表的指针是否在开头,而另一工作区的表可用其别名或工作区号来指定。该参数如省略,隐含为当前工作区,如有该参数的话,别名或工作区只选一个。 备注:

这里所说的开头,并非是指第一个记录,而是第一个记录的前面。 举例:

go top

skip -1

?bof()

显示结果为.t.。

如果没有skip -1,仅仅走到第一个记录,返回的将是.f.,而必须再往上跳一下才会是“真”开头。

但与eof()函数不同,此时如输入命令display,将显示第一条记录。

消除后续空格函数(trim())

将字符表达式的后续空格全部删除,并将结果返回。

26

语法:

trim(字符表达式) 返回值的类型:

字符型 参数:

字符表达式:指定的字符表达式,该函数将删除其后续空格。 备注:

trim()函数等同于rtrim()函数。 举例:

如表的“姓名”字段长度为10,当前内容为“庄稼”,那么如下语句:

? 姓名+'先生:'

其显示结果是:

庄稼 先生:

而语句:

? trim(姓名)+'先生:'

显示结果是:

庄稼先生

消除前导空格函数(ltrim())

将字符表达式前面的空格全部删除,并将结果返回。 语法:

trim(字符表达式) 返回值的类型:

字符型 参数:

字符表达式:指定的字符表达式,该函数将删除其后续空格。 举例:

在讲str()函数时我们举过一个例,即把一个字符与一个数值相加时,应采用如下形式: 27

? '季度'+str(1,1)

但是当我们没有办法确定这个数值的位数时,这个办法就行不通了,您不可能规定它的长度只能是1或2等等。那么怎么办呢,采用ltrim()函数可以解决了:

? '项目'+ltrim(str(sz))

这里假设事先已有一个数值存入了sz变量,但不知道这个数值是几,也就更不知道有几位数,那么在隐含状态下,str()函数将把sz转换为长度为10的字符,其前面可能有若干空格,通过ltrim()函数的处理,不论前面有多少空格,都可以得到如下结果(假设数值是15):

项目15

VFP 初级教程——第五课 常用编程命令及常用函数

注:在语法中如用方括号“[ ]”括起来的词句,表示可以不用。

命令

假设语句

根据一逻辑表达式的值,有条件的执行一组命令。 语法:

IF 逻辑表达式 [THEN]

程序组1

[ELSE

程序组2]

ENDIF 参数:

逻辑表达式代表一逻辑值,如果这逻辑值为真,系统执行程序组1,否则不执行程序组1,如果有else及程序组2的话,则执行程序组2,如没有则什么也不执行,直接执行endif后面的语句。 备注:

在if...endif之间还可以有if...endif,即该命令是可以嵌套的。

then可以有也可以没有,对程序没有影响。

else及程序组2可以有也可以没有,但如果有的话,在逻辑表达式的值为假时,程序组2将被执行。

if 与 endif 必须配对使用,即有一个 if 必须有一个 endif, 否则程序会出错。

举例:

例1:假如分数字段的值大于等于90,则让等级字段的值为“优”,并显示该记录。程序如下:

28

if 分数>=90

replace 等级 with '优'

display

endif

例2:在数据库中可以把性别字段设为逻辑型,“真”代表“男”,“假”代表“女”,但显示时不能显示真、假,否则别人看不懂,而应显示男、女,因此我们设一变量xb,根据性别字段的具体值,将xb的值设为男或女,然后在需要的时候显示xb的值即可。程序如下:

if 性别

xb='男'

else

xb='女'

endif

其中第一句:if 性别,表示“假如性别为真”的意思,不要写成“if 性别=.t.”。

以上程序也可以写成如下形式,效果相同:

if .not. 性别

xb='女'

else

xb='男'

endif

即,假如性别不为真的话,xb的值就为女,否则为男。

循环语句(do while...enddo)

根据条件重复执行一组程序。 语法:

DO WHILE lExpression

Commands

[LOOP]

[EXIT]

ENDDO 参数:

lExpression 为一逻辑表达式,其值如果是“真”,即执行commands程序组,如果表达的值为“假”,则跳过该段程序组,执行enddo后面的语句。

Commands 当表达式 lExpression 值为真时所要执行的程序组。

LOOP 此为 do while...enddo 命令的一个子句,它可以放在 commands 程序组中间的任何地方,当程序一旦执行到该子句时,则返回 do while 重新执行。此子句根据需要可以有,也可以没有。

29

EXIT 与loop一样是 do while...enddo 命令的一个子句,它可以放在 commands 程序组中间的任何地方,当程序一旦执行到该子句时,则跳出循环,执行enddo后面的语句。此子句根据需要可以有,也可以没有。 备注:

程序一旦执行到do while 语句,如果 lExpression 的值为真,则执行 commands 程序组,该程序组执行完后,就到enddo语句,enddo会将程序返回 do while 再次验证 lExpression 是否为真,如仍是,则又一次执行 commands ,如此循环往复,直到 lExpression 为假,或遇上 exit 语句。

该语句可以嵌套,即循环中还可以有循环。

do while 与 enddo 必须配对使用,即有一个 do while 必须有一个 enddo 否则程序会出错。 举例:

例1:在数据库中不断将记录指针往下移,直到数据库结尾。程序如下:

do while .not. eof()

skip

enddo

例2:在上面的例子加入此功能,当遇到性别字段为“女”时,跳出循环。程序如下:

do while .not. eof()

if 性别='女'

exit

endif

skip

enddo

例3:不断将记录指针往下移,当遇到数量字段的值大于等于600时退出循环,如遇到值小于400的,将其值乘2,然后再检验一次是否大于600,如是则退出循环。程序如下:

do while 数量<600

if 数量<400

replace 数量 with 数量*2

loop

endif

skip

enddo

分支语句(do case...endcase)

根据不同的条件执行不同的程序组。 语法:

30

DO CASE

CASE 逻辑表达式1

程序组1

[CASE 逻辑表达式2

程序组2

...

CASE 逻辑表达式n

程序组n]

[OTHERWISE

程序组0]

ENDCASE

参数:

CASE 逻辑表达式 程序组 ... 当程序执行到 do case 时,便检验第一个 case 的逻辑表达式(逻辑表达式1)是否为真,如不为真,接着检验第二个 case 的逻辑表达式,以此类推直到逻辑表达式n。当检测到第一个为真的逻辑表达式时,便执行跟随在其后面的程序组,执行完后跳过后面所有的 case ,接着执行 endcase 后面的语句,也就是说即使后面还有为真的逻辑表达式也不执行了。

如果所有 case 后面的逻辑表达式都为假,则执行 otherwise 后面的程序组(如果有的话, otherwise 及其程序组是可以没有的,如果没有则什么都不做,直接执行 endcase 以后的程序) 备注:

在do case 和 endcase 之间可以有任意多个 case 。

do case 和 endcase 必须配对使用,即有一个 do case 必须有一个 endcase ,否则程序会出错。 举例:

如果工资在200元以下,增加50%;300元以下,增加30%;500元以下,增加20%;其它增加10%。程序如下: do case

case 工资<=200

replace 工资 with 工资*1.5

case 工资<=300

replace 工资 with 工资*1.3

case 工资<=500

replace 工资 with 工资*1.2

otherwise

replace 工资 with 工资*1.1

endcase

在这里要注意一个问题,不能把300元的 case 放在200元之前,否则一个100元的,按理应加50%,但当遇到小于300元的 case 时,其逻辑表达式为真(小于200元的肯定小于300元),因此就会执行后面的程序,加30%,并且执行完后,就不再执行其它的 case 了,这样就产生了错误的结果,所以应按从小到大的顺序来排列。

[dvnews_page=第五课 常用编程命令及常用函数 ]

赋值语句(store...to)

将一个数据赋给一个变量。

31

语法:

STORE 表达式 TO 变量名表参数:

表达式的值即为要赋给变量的数据。

变量名表即为要被赋值的各变量。在这里可以是一个变量,也可以是多个变量,如果有多个变量,其间用“,”(逗号)隔开。 备注:

如果是给一个变量赋值,该语句可写成如下形式:

变量名=表达式

表达式可以是一个数值,也可以是一个算术式。

举例:

例1:将3赋给ab、xyz、jfz三个变量,程序如下:

store 3 to ab,xyz,jfz

例2:将变量gz的值加100赋给yfgz。程序如下:

yfgz=gz+100

调用表单语句

运行一个由表单设计器设计的表单文件。该文件是经编译过的。 语法:

DO FORM 表单文件名 [NAME 变量名 [LINKED]]

参数

表单文件名即是要运行的由表单设计器设计的表单文件名称。

变量名为调用该表单所用的变量名称,做为表单,不能直接用这的名称去调用它,必须将其赋给一个变量,然后用这个变量来调用它。如果您不会在这个表单之外调用它,也可以不要这个变量。

在程序中产生的所有变量在程序运行结束后将被释放,即这些变量不再存在,因此也就无法继续调用这些变更,如果为了调试程序需要在程序运行结束后在命令窗口中调用这个表单,必须加上 linked 子句。 举例:

在程序中调用xy7表单,并将赋给一个变量lucky,程序如下:

do form xy7 name lucky

32

调用子程序语句(do)

运行一个VFP程序。当我们要在一个程序中调用另一个程序(子程序)时使用此命令。 语法:

DO 程序名参数:

程序名即为被调用的程序名称。 备注:

如被调用的程序的扩展名是“prg”,调用时可不用带扩展名,否则要带上扩展名。 举例:

有一个程序,名称为 xy7.prg,调用它的程序如下:

do xy7

[dvnews_page=第五课 常用编程命令及常用函数 ]

返回调用程序语句(return)

返回调用本程序(该语句所在程序)的程序。

前面讲过调用子程序的语句,从一个程序A调用另一个程序B后,系统便开始执行B程序中的语句,到一定时候往往要从程序B返回程序A,便可使用该语句。 语法:

RETURN 备注:

程序A调用程序B,当从B返回A后,系统接着执行调用语句(do b)下面的一条语句。 举例:

程序a.prg如下:

do while .not. eof()

if 工资<100

do b

endif

skip

enddo

程序b.prg如下:

33

replace 工资 with 工资*1.5 &&将工资增加50%

display &&显示出该记录,这样可以将所有增加了工资的记录显示出来

return

首先执行程序a.prg,当程序执行到 do b 语句时,便转去执行程序 b.prg ,在程序 b 中执行到 return语句时,又返回程序 a ,并接着执行 do b 的下一条语句 endif 。

启动事件处理语句(read events)

启动VFP的事件处理程序。 语法:

READ EVENTS 备注:

当该命令执行后,系统即停止继续执行后续的语句,这时我们可以调用之前所启动的菜单、表单等对象,并用这些对象的事件程序去完成相应的任务,直到发出 clear events 命令,系统才接着执行 read events 后面的命令语句。

可能初学者看了上面的内容还不是很清楚,不要紧,我们在后面课程中会进一步讲解。

清除事件处理语句(clear events)

终止由 read events 语句启动的事件处理程序。 语法:

clear events 备注:

发出该命令后,系统将继续执行 read events 之后的语句。

[dvnews_page=第五课 常用编程命令及常用函数 ]

开关命令执行状态语句(set talk on/off)

确定是否显示VFP命令执行的状态。 语法:

SET TALK ON | OFF

参数:

ON 显示VFP命令执行的状态。

OFF 不显示VFP命令执行的状态。

备注:

使用时,on、off 二者之中必须选择一个。

34

很多VFP命令执行后,会显示执行后的结果状态,如 locate for 命令执行,如找到记录的会显示被找到的记录号,否则会显示“已到文件尾”,但一般我们在程序中是不需要这些显示的,比如找到了记录就直接显示出来,没找到一般用一个对话框来给出更清楚的提示,所以在程序一开始往往要将 set talk 关闭。

结束程序语句(cancel)

结束当前正在运行的所有程序,返回VFP或操作系统。 语法:

cancel 备注:

应该不需要多说了吧。

VFP 初级教程——第六课 编一个完整的软件

第六课 编一个完整的软件

其实这个标题说的不太严密,因为任何一个能够正常执行的软件都可称之为完整的软件,而我们这里的意思是:一个有菜单、有数据库(表)、各种输入输出、打印等较完整功能的软件。

那么编个什么软件呢?我们就编个人事管理软件吧。当然为了简便起见,这软件中的人事档案是很不完全,但完全可以说明软件编制的整个过程。

第一步:在硬盘上建一个目录,如d:rsgl。

第二步:启动foxpro系统,建一个项目管理器,存在上面的目录里,就如同在第二课中一样,管理器的名称也叫“rsgl”。

第三步:做一个小小的系统分析。

系统分析

系统分析文件虽然并不是软件软件本身的一部分,但也属于本软件开发的文档,故也应该将其放在项目管理器中,方法是:

1. 选择项目管理器中的“其它”;

2. 选择“文本文件”;

3. 按“新建”

35

4. 在新建的文本文件中写入下面的“系统分析内容”;

5. 保存文件,文件名也用“rsgl”,扩展名不用加,系统会自动加上“txt”;

6. 关闭

之后,可在项目管理器中看到该文件,此时还要做一件事,将该文件排除在软件之外,即当需要对软件编译时不把此文件编译到exe文件中,方法是:

1. 用右键单击该文件;

2. 在弹出的菜单中选择排除;

之后会在该文件左边看到一个符号“?”,表示该文件已被排除在软件之外了。今后对于其它不属于软件本身的文件都应同样处理。

系统分析内容

1. 本软件具有以下主要功能:

o 系统功能

?? 打开人事档案(调用表单rsda:do form rsda name rsda)

?? 关闭人事档案(释放表单rsda:rsda.release)

?? 打印花名册(打印报表hmc:report form hmc to printer)

?? 退出软件运行(退出事件处理程序:clear events)

o 编辑功能

?? 新增或修改人员(调用表单bjry:do form bjry name bjry)

?? 删除人员(删除当前记录:delete)

?? 恢复人员(恢复当前记录:recall)

o 查询及统计功能

?? 查询(调用表单cx:do form cx name cx)

?? 查看简历(调用表单ckjl:do form ckjl name ckjl)

36

?? 统计(进入子菜单)

?? 统计各部门人数(调用子程序tjbm:do tjbm) ?? 统计各职务人数(调用子程序tjzw:do tjzw)

以上同时作为软件的菜单结构,括号中的内容为菜单调用各功能的命令。

2. 次要功能

o 人事档案表打开后按编号排序。

o 删除人员后并不马上从表中将该记录删除,只是做上删除标记,还可恢复过来,

即取消删除标记,当退出系统时才真正将该记录删除。在真正删除记录时,系统可能要花较长时间(尤其记录较多时),此时应给出提示,以免操作者误认为死机。

3. 系统数据库

o rsgl.dbc(其中包含以下表)

?? rsda.dbf(人事档案表,包含以下字段)

字段名 编号 姓名 性别 出生年月 联系电话 部门 职务 基本工资 简历

字段类型 数值 字符

逻辑(T男、F女) 日期 字符 字符 字符 数值 备注

字段长度 4 10 15 10 10 7

小数位数 2

?? tjbm.dbf(统计部门表,包含以下字段)

字段名 部门 人数

字段类型 字符 数值

字段长度 10 4

小数位数

37

?? tjzw.dbf(统计职务表,包含以下字段) 字段名

职务

人数 字段类型 字符 数值 字段长度 10 4 小数位数

??

很多人喜欢拿到软件马上就开始编,这种习惯很不好,因为我们必须首先搞清楚这个软件有些什么功能、需要些什么表、有什么报表需要打印等等,这样才能使整个软件开发的过程比较顺利,否则会给后面的软件开发、修改、维护等带来无穷后患,严重时可能导致开发失败。因此我们不管开发什么软件,都应该先做系统分析,即使简单做一下也比不做好,要知道“磨刀不误砍柴功”。

[dvnews_page=第六课 编一个完整的软件]

第四步:编制菜单。

编制菜单

1.

2.

3. 选择项目管理器中的“其它”; 选择其它中的“菜单”; 按“新建”,出现图1;

38

4. 按“菜单”,进入菜单设计器,如图2;

5. 先做主菜单,在菜单名称中分别输入“系统”、“编辑”、“查询及统计”,如图3,

注意在结果栏内应保持显示“子菜单”,而在“菜单级”中显示的是“菜单栏”,表示是主菜单;

6. 进入“系统”子菜单,按“系统”行“子菜单”后面的“创建”按钮,得到图4,注

意此时菜单级中显示的是“系统”,拉下菜单级选择框,可看到在“系统”上面有一个“菜单栏”,表示此时编辑的是顶层菜单下的“系统”子菜单;

39

7. 按照系统分析的内容,编第一个菜单项,在菜单名称中输入“打开人事档案”,在结

果中选择“命令”,在选项中输入“do form rsgl name rsgl”。之后,按同样方法将其它菜单项编好,如图5;

8.

9. 在菜单级中选择“菜单栏”,返回主菜单; 重复6-8步,编好其它子菜单,注意在“查询与统计”下的“统计”也是一个子菜单,

编制方法与编制主菜单的子菜单一样,想必聪明的您一定会做了。

10. 编制初始化代码。初始化代码是在菜单运行之前所需要运行的代码,一般是做一些准

备性的设置。选择菜单上的“显示”-“常规选项”,得到图6,

选择“菜单代码”的“设

40

置”,出现设置代码编辑窗口(也就是初始化代码编辑窗口),按确定得图7,当然此时您看到是一个空的窗口,没有图7中的代码,那么您就按照图7中的代码在您的电脑上输入吧,输入完后“文件”-“关闭”。

11. 编制清理代码。清理代码这个词不太好理解,其实就是在菜单运行之后所需要运行的

代码,此时一般应进入事件处理程序,以及程序退出时要做的一些清理工作,如关闭数据库等。编置清理代码的方法与初始化代码的方法类似,选择菜单上的“显示”-“常规选项”,选择“菜单代码”的“清理”,出现清理代码编辑窗口,按确定,然后输入图8中的代码,输入完后关闭。

12. 保存,文件名仍然是“rsgl”,扩展名为“mnx”,注意应保存在与项目管理器一致的

目录里;

13. 生成菜单程序。我们以上编的是菜单结构,并非是菜单程序本身,结构编好后必须调

用VFP的生成菜单功能才能生成真正的菜单程序,方法是调用系统菜单中的“菜单”下的“生成”,之后系统会让您输入生成后的程序的文件名,此时文件名仍取“rsgl”,扩展名为“mpr”,注意文件名前面的目录名应该与项目管理器所在的目录一致,一般系统隐含就是所需的文件名和目录,故不需输入,直接按“产生”即可,然后系统会自动在您的目录中生成一个“rsgl.mpr”的文件,这就是我们今后所要运行的菜单程序;

41

14. 关闭,即退出菜单设计器,您将会在项目管理器中看到菜单文件,如图9。

如要修改菜单,选择菜单文件后按修改进入菜单设计器,修改的方法与新建时相同,您还可以使用“删除”、“插入”来删除或插入一个菜单项,当然在新建时也可以使用,但不要用“插入栏...”,这个我们以后再讲。修改完要记住保存修改的结果。

千万不能忘记的就是:修改完菜单后一定要再次“生成”,否则您虽然改了菜单结构,但没有改菜单程序,那么一旦运行起来菜单还是原样。

[dvnews_page=第六课 编一个完整的软件]

第五步:编制数据库及表。

编制数据库及表

1.

2. 选择项目管理器中的“数据”; 选择数据中的“数据库”;

42

3. 按“新建”,得到图1;

4.

按数据库设计器工具栏上的“新建表”按钮,出现图

2;

5. 按“新表”,输入新表名(rsda.dbf,扩展名可以不输入),按“保存”,出现图

3;

43

6. 按照系统分析中的数据表结构输入有关人事档案表的结构,如图

4;

7. 建立索引,用鼠标拉下“编号”字段后面的“索引”组合框,选择向上的箭头,按同样方法建好“部门”和“职务”

字段的索引;

8. “确定”以后出现对话框询问“现在输入数据记录吗?”,选择“否”,看到图

5;

9. 重复4-8步建立“统计部门”(tjbm.dbf)和“统计职务”(tjzw.dbf)表;

10. 关闭数据库设计器,可在项目管理器中看到如图

6。

这样该软件所要用到的数据库和数据表就编好了。

有的朋友可能会说:在这个软件中,只有一个数据表,可以不用数据库,而使用自由表就行。

此话有一定道理,但我们之所以要这样做,就是让大家知道数据库是怎么建的,以及数据库和数据表的关系,另外也为了今后我们要对这个软件进行扩充功能时做好准备。

44

如要修改数据表,用鼠标选中所要的表,然后按修改,修改完后按“确定”,会出现一个对话框,如图7,选择“是”即可。

[dvnews_page=第六课 编一个完整的软件]

第六步:编制各功能模块。

编制各功能模块

1. 打开人事档案

打开人事档案模块

首先在rsda数据表中随便输入一点数据,选中rsda数据表,按“浏览”,选择系统菜单上的“表”-“追加新记录”,然后在表中的空记录中输入数据。为什么要这么做,先卖个关子。

一、制作表单

1.

2. 按照第二课的方法建立一个表单,文件名为rsda; 将表单拉到适当大小,宽约470(width属性),高约400(height属性),这

只是个参考值,您爱拉多大多大,但宽度最好不要太小,否则不能将所有的字

段显示出来,要用滚动条才能看见,那多麻烦;

3.

4. 将表单的caption属性设为“人事档案”; 设置档案被打开的标记,在表单的初始化(init)事件中编入如下代码(方法

类似第二课中为按钮的click事件编代码): dkda=.t. &&设置打开标记为“真”,表示人事档案已打开,以便退出程序时先关闭本表单set order to 编号 &&设置主索引为编号,数据按编号排序

45

5. 清理被删除的记录,当档案被关闭时,这次打开后所有做了删除标记的记录应

locate for delete() &&查找是否有做了删除标记的记录

if found() &&假如找到

wait window '正在删除数据' nowait &&给出正在删除数据的提示 pack &&彻底删除

wait window '数据删除完毕' nowait &&给出数据删除完毕的提示

endif &&结束假设

dkda=.f. &&设置打开标记为“假”,表示人事档案已关闭

set order to 0 &&设置表为没有索引

从数据表从彻底删除,因此我们应在表单的destroy事件中编入如下代码: 二、制作表格

6. 调用表格控件按钮;

7. 在表单上拉出表格,最好从表单的左上角到右下角,即表格占满整个表单(我喜欢这样),如图1,

这是一个空表格,我们还要为它设定显示的内容,可以用手动的方法去设定这个表格有多少列、各列显示哪个数据的哪个字段、宽度多少等等,但这样太麻烦了,VFP为我们准备了表格内容生成器(向导),用它可以很方便地做出表格,看官请接着往下看;

46

8. 用鼠标右键单击表格,弹出菜单,如图

2;

9. 选择“生成器”,弹出“表格生成器”,如图

3;

10. 将“可用字段”中除简历以外的所有字段放入“选定字段”,先用鼠标单击一个字段,再按“选定”按钮

也可以按“全部选定”按钮

将简历移出。

* 一般此时数据库都会在“数据库和表”中出现,选中一个数据库,其中的数据表则会在下面出现,选中

一个表,则其字段会在可用字段中出现,如没有所要的数据库及表出现,按

开;

11. 可选择表格的样式,一般就用隐含样式,如果您喜欢也可以试试别的样式。

12. 调整表格各列的宽度,选择布局,这里要注意如果数据表中没有记录,则布局不会显示,所以一开始要……,

然后您可以根据自己爱好调整各列的宽度;

13. 所有都做好后“确定”,得到图

4; 查找所要的数据库并打,将所有字段放入选定字段,再选择简历,

按“移出”按钮

14. 将表格的readonly属性设为“真”,即当表格打开时,不能随便更改其中的数据,以防误操作;

15. 如此时想要调整表格的列宽,不要再用生成器,否则会其它列的宽度也发生变化,而应直接在表格上调整,

方法是:在属性窗口中选择表格的一列,任一列都可以,如图5,

47

选择后,将鼠标移到表格列标题列分隔线处,光标变成一个双向箭头,如图

6,

按下左键即可调整列宽了。

全部做好后保存表单(文件名rsda,注意要保存在正确的目录中,以后不再提醒了哦),然后关闭表单设计器。 这样我们软件的第一个主要功能就做好了,一定迫不及待想试试了吧,选择项目管理器“其它”中我们所做的菜单,按下“运行”,然后在表单上打开人事档案,效果怎样,再试试关闭、退出、删除(记录被做上删除标记后前面有一个黑块)、恢复(黑块消失),但别的功能先别试,因为相应的模块还没有编好,一旦调用会出错。

2. 关闭人事档案:只须菜单中的那一条命令即可,不需要再劳您大驾写什么代码。

3. 打印花名册:在第七步“编制打印报表”中讲解,先请您往下看。

4. 退出软件运行:也只须菜单中的命令。

[dvnews_page=第六课 编一个完整的软件]

5. 新增或修改人员

新增或修改人员模块

1. 新建一个表单(文件名为bjry);

2. 表单的caption属性设为“编辑人员”;

3. 将表单的windowtype属性设为“1-模式”,即当此表单打开时其它已打开的表单包括菜单都不能被激活,

因为这时如果做一些别的操作可能会给操作者造成混乱,故将其设为此模式。

4. 在其中放入如图1

的控件,

注意放简历内容的控件是编辑框,与文本框的使用方法基本相同,不同的是可以输入和显示多行内容,按

创建编辑框;

5. 从text1到text8以及edit1,每个控件的controlsource属性都设为与它们左边的标签一致,比如:“编

号:”右边的文本框的controlsource属性设为“编号”,也就是这个文本框的内容取自数据表当前记录的编号字段,当在里面输入了内容,也将新内容放入该字段,其它文本框和编辑框也同样处理;

48

6. 在“新增”按钮的click事件中输入如下代码:

append blank &&增加一条空记录

*以下将各控件的内容刷新

thisform.text1.refresh

thisform.text2.refresh

thisform.text3.refresh

thisform.text4.refresh

thisform.text5.refresh

thisform.text6.refresh

thisform.text7.refresh

thisform.text8.refresh

thisform.edit1.refresh

thisform.text1.setfocus &&将焦点设到text1

7. 在“上一条”按钮的click事件中输入如下代码:

skip -1 &&记录指针向上跳一行

*以下一段程序检测记录指针是否到了开头,如果是给出提示,

*并把指针定位到第一个记录

if bof() &&假如指针已到开头

wait '已到开头' window nowait &&显示提示,鼠标或键盘一动提示消失 go top &&将指针定位到第一个记录

endif &&假设结束

*以下将各控件的内容刷新

thisform.text1.refresh

thisform.text2.refresh

thisform.text3.refresh

thisform.text4.refresh

thisform.text5.refresh

thisform.text6.refresh

thisform.text7.refresh

thisform.text8.refresh

thisform.edit1.refresh

thisform.text1.setfocus &&将焦点设到text1

8. 在“下一条”按钮的click事件中输入如下代码:

skip &&记录指针向下跳一行

*以下一段程序检测记录指针是否到了结尾,如果是给出提示,

*并把指针定位到最后一个记录

if eof() &&假如指针已到结尾

wait '已到结尾' window nowait &&显示提示,鼠标或键盘一动提示消失 go bottom &&将指针定位到最后一个记录

endif &&假设结束

*以下将各控件的内容刷新

thisform.text1.refresh

thisform.text2.refresh

thisform.text3.refresh

thisform.text4.refresh

thisform.text5.refresh

thisform.text6.refresh

thisform.text7.refresh

thisform.text8.refresh

49

thisform.edit1.refresh

thisform.text1.setfocus &&将焦点设到text1

9. 在“退出”按钮的click事件中输入如下代码:

thisform.release

10. 编好后存盘退出即可,运行起来您就可以在菜单中调用“新增或修改”的功能了。

这一部分虽然看起来内容很多,编写的代码也挺多,但大部分都是重复的,去掉那些重复的东西也就没啥玩艺了,所以还是很简单。不管怎么说我们又向成功迈进了一步。

6.

7.

8. 删除人员:就菜单中的命令够了。 恢复人员:一样。 查询

查询模块

1. 新建一个表单(文件名为cx);

2. 表单的caption属性设为“查询”;

3. 将表单的windowtype属性设为“1-模式”,即当此表单打开时其它已打开的表单包括菜单都不能被激活,

因为这时如果做一些别的操作可能会给操作者造成混乱,故将其设为此模式。

4. 在其中放入如图1

的控件;

5. “取消”按钮的click事件中还是那一句:

thisform.release &&本表单释放

6. “确定”按钮的click事件代码如图

2。

OK了!去试试(废话,还用我说吗)。

50

9. 查看简历

查看简历模块

1. 新建一表单,文件名为ckjl.scx;

2. 表单的caption属性设为“简历”;

3. 表单的windowtype属性设为“1-模式”;

4. 在其中放入如图1

控件;

5. edit1控件为编辑框,readonly属性设为.t.,即设为只读,这样可以避免在查看时不小心改了里面的数

据;

6. text1的controlsource属性设为“姓名”,这样查看时知道是谁的简历,enabled属性设为.f.,即不可

访问,也就是光标都不能去到这个控件,而上面的edit1就不能设为不可访问,因为它的滚动条还需要操作;

7. “确定”按钮中的click事件代码如下: thisform.release &&本表单释放

8. 存盘,注意文件名是ckjl.scx,扩展名可以不输入。

10. 统计各部门人数

统计各部门人数模块

一、编制子程序

1. 在项目管理器中选择“代码”;

2. 在代码中选择“程序”;

51

3. 按“新建”,出现程序编辑窗口,如图

1;

4. 在其中输入如下代码: use tjbm in 0 &&在另一工作区打开统计部门用数据表

select tjbm &&选择统计部门表

zap &&清空里面的数据

select rsda &&选择人事档案表

set order to 部门 &&设置主索引为部门,即按部门排序

go top

do while .not. eof() &&建立循环,当没到数据表结尾时,循环执行下面的程序 bm=部门 &&将当前记录的部门存入bm变量

count while 部门=bm to rs &&统计以下记录数直到部门不与bm的值相同 &&结果放入rs变量

select tjbm &&选择统计部门数据表

append blank &&增加一空记录

replace 部门 with bm,人数 with rs &&将bm和rs的值存入空记录 select rsda &&选择rsda表以便继续统计下面的记录

enddo &&循环结束

do form tjbm name tjbm &&调用统计结果显示表单

select tjbm &&选择统计部门表

use &&将统计部门表关闭

select rsda &&选择人事档案表

set order to 编号 &&将主索引设回编号,以便在人事档案表显示

return &&返回

5. 保存,文件名为“tjbm.prg”

二、编制表单

6. 新建一表单,文件名为tjbm.scx;

7. 表单的caption属性设为“统计部门人数”;

8. 表单的windowtype属性设为“1-模式”;

52

9. 在其中放入如图3

控件,

在查看时不小心改了里面的数据;

10. “确定”按钮中的click事件代码如下: 其中表格的处理方法与人事档案表中表格一样,注意要设为只读,readonly属性设为.t.,这样可以避免

thisform.release &&本表单释放

11. 存盘,OK。

11. 统计各职务人数

统计各职务人数模块

方法与统计部门人数几乎一样,只需把文件名由“tjbm”改为“tjzw”(注意有两个文件,一个是子程序,一个是表单,这两个文件的主名一样,扩展名不同),代码中的“bm”全部改为“zw”,“部门”改为“职务”就行了。我不讲具体方法了,看看您自己会不会做,如果实在不行,不要紧,可以下载我们的源代码来看。

第七步:编制打印报表。

编制打印报表模块

1. 选择项目管理器中的“文档”;

2. 选择文档中的“报表”;

3. 按“新建”,出现图

1;

53

4. 按“报表向导”,出现图

2;

5. 选择“报表向导”,按“确定”,出现图3,有可能在“数据库和表”的位置看不到所要的数据库和表,那么按后面的

按钮到相应的目录中查找所要的数据库和表,找到后打开;

6. 与做人事档案表格时类似,将“可用字段”中除简历外的所有字段放入“选定字段”,如图

4;

54

7. 按“下一步”,得到图

5;

8. 选择“帐务式”,这种表比较符合中国人的习惯,如果您喜欢西餐我也不反对,然后“下一步”,出现图

6;

9. 什么也不做,继续“下一步”,得图

7,

如图

8,也就是说在报表中记录按编号排序; 在“可用的字段或索引标识”中向下移动选择“编号 *”,即编号索引标识,按“添加”,将其放入“选定字段”,

55

10. “下一步”,图

9,

在报表标题中输入“人员花名册”,如图

10,按“完成”;

11. 输入文件名(hmc.frx),注意文件应放在相应的目录中;

12. 完成以后,可在项目管理器的报表下看该报表文件,如图

11,要是看不见,按一报表左边的“+”号;

13. 本来就已经做完,可向导产生的报表下边界留的很少,如果您用的是1600等针式打印机可能打不出下面的页号,

甚至报表都打不完整,因此还要做点调整(烦人)。在项目管理器中选择报表文件,按“修改”,得到图12,即 56

报表设计器;

14. 用鼠标按住“页注脚”向下拉,如果看不到页注脚,报表设计器的窗口扩大些,大约拉到1.5英寸的地方(多点少

点问题不大),如图

13;

15. 保存,关闭,大功告成,行动程序后在菜单中调打印花名册就可以打印了,当然打印机要先联机。

到此为止,真的是大功告成了,整个软件编完了。如有什么不清楚可下载我们的源代码来看,也可来信提问。

当然这个软件还是很不完善的,比如不能用男、女显示和输入性别,也不能用选择的办法来输入部门、职务,这其实是我们故意留的缺陷,以备后面的课程用,这叫用心良苦。

【下载人事管理程序源代码】

注意:

1、当您第一次打开源代码中的项目管理器时,如果其所在的目录不是“c:rsgl”,系统会问您“项目已移动,设置(新目录)为其新目录”,应回答“是”。

2、如果目录改变了,第一次运行程序前,应将菜单初始化代码中的“set default to c:rsgl”语句中的目录改为您的新目录,否则程序运行会出错。

VFP 初级教程——第七课 编程中的一些技巧

一、 复制

57

我们在编程会发现一个软件中有许多代码是重复,我们可以不用重复地写,而可以使用菜单中的“复制”、“粘贴”等命令来复制重复的内容,这一点想必大家都会想到。

但有一点可能有的人没有想到,即表单中的控件也是能复制的,而且可以一次复制多个控件。比如我们在制做人事档案的录入表单时,我们做好第一个(编号的)标签和文本框,接着做姓名的标签和文本框时,就可以采用复制的办法。

用鼠标同时选择编号的标签和文本框,方法是用鼠标画一框将以上两个控件框住,框住每个控件的一部分也可以,这样我们可以看到每个控件四周都有六个黑点,表示它们都被选择了。然后与复制文字一样调用菜单上的“复制”命令或工具栏上相应按钮,然后再用鼠标按“粘贴”,就会出现两个同样的控件,用鼠标把它们移到适当的位置(可以一起移),再将caption、controlsource以及大小等属性做相应改动即可。

注意控件的name属性在粘贴时已自动做了改动,比如原来的文本框叫text1,复制出来的文本框会自动叫text2(再复制就会叫text3),在一个表单中是不允许出现两个同名的控件,系统会自动遵守这一点,故我们可以不用去修改name了。当然如果您喜欢的话,您可以把它改为一个易记的名称。

另外在复制控件时不光属性会被复制,其中的事件代码也会被复制。还有我们可以把一个表单中的控件复制到另一个或更多的表单中。比如很多时候退出或取消按钮都是一样的,其中的click事件中都是“thisform.release”语句,我们就可以把一个表单中的按钮复制到另一个表单中,有时可能会要改一下caption或在click事件中加减一两条语句。还有向上移动记录和向下移动记录的按钮,其中的事件程序也都差不多,也可以复制,然后将不同的地方修改一下。

这里还有个技巧,将鼠标放在要复制的地方,按右键,选择弹出菜单中的“粘贴”,控件就会被复制在鼠标的地方,虽然还不够精确,只要稍微动一下就行了,不用移那么远。

二、 缩格及空行

一些网友可能发现我们的程序中有的语句开头会向里缩几格,这是为什么呢,又有什么规律吗?

这是为了使程序易于阅读,我们知道VFP中有很多语句是成对的,比如if和endif、do while和enddo,我们为了把成对语句之间的语句和之外的语句区别开来以利于程序的阅读和理解,有经验的程序员往往都是把成对语句之间的语句往里缩。如果不这样做,在看程序和修改程序时很容易出错,特别是在成对语句里面又套成对语句的情况(这叫嵌套),如下面的程序:

if a=1

b=a+c

if b=3

c=b+a

if c=5

d=9

endif

d=c+a

if c=3

d=c+b

endif

endif

c=4

endif

我们很难看清哪个if和哪个endif配对,这就给修改程序带来了很大麻烦,如果写成如下形式则一目了然:

58

if a=1

b=a+c

if b=3

c=b+a

if c=5

d=9

endif

d=c+a

if c=3

d=c+b

endif

endif

c=4

endif

这就是缩格的好处,希望大家今后在编程序时都要采用缩格,虽然在编程序时要多打几个空格,但会给以后修改程序带来很大方便。

但也有不方便的时候,比如我们修改程序时要把一大段程序套在一对if和endif中(本来是没有套的),那么要挨个将这一大段程序往里移,那也是一件很烦人的事。

不过不用怕,VFP已为我们准备了解决这一问题的功能。在程序中的任意位置按鼠标右键,在弹出的菜单中选择“修饰”,在弹出的设置框中直接按运行即可完成对程序的缩格修饰,并且系统还会对程序做适当的大小写处理。

但我不喜欢直接按运行,而喜欢做一些设置,具体设置如下:将关键字和符号全部选择为小写字母,即不做大小写处理,缩进类型选择空格,这是为了调整缩进的空格数,然后把下面的空格调为2。

以上设置纯属个人喜好,并非必要!

另外我们在编程时还应该在适当的地方空一行,那么什么叫适当的地方呢?这就有点象我们写文章分段,一段相对完整的意思做为一段,我们也把一段相对完整功能的程序做为一段,一段完了可以空上一行,也是便于程序的阅读。当然这里的“相对完整”相对不同的人来说可能不一样,我们完全可以根据自己的习惯,不过给个建议就是既不要太少,也不要太多(好象又说了一句废话),初学者可根据我们上一课中的程序去揣摸。

三、 在程序中插入对象名称

我们在编面向对象的程序时,经常要引用一个对象的名称,而且要用这个对象的全名,比如:thisform.text1,老是这样写也是个叫人打嗑睡的事,有没有简便的办法?有!

在要插入一个对象引用名称的位置单击鼠标右键,在弹出的菜单中选择“对象列表”,在出现的对象列表中选择所要的对象,虽然这里看到的不是它们的全名,但按了插入之后......。

插入的名称会以大写字母开头,如不喜欢,可按我上面的设置对整个程序修饰一下。

四、 精确调整对象位置

我们在编程序时一定发现当移动一个控件时它是跳着走的,而且是按照表单上的虚线格跳,这主要是为了我们的控件都能对整齐。但有时候我们不需要对整齐,而要做一些小小的调整,即不按格跳,怎么办呢?

选择菜单上的“格式”,再选择其中的“对齐格线”,将其左边的钩去掉,然后您再移一下控件看,想到哪就到哪。如要恢复原来的状态,再把钩加上。

59

五、 为文件写说明

当开发一个较大的软件时,可以想象在项目管理器中会有很多的文件,当多到一定程度可能我们自己都搞不清这些文件是干什么的了,为了防止这一点,我们应该为每个文件写上说明,使我们随时知道每个文件的作用。

用鼠标右键单击一个文件名,在弹出的菜单中选择编辑说明,在出现的表单中写入该文件的说明文字,确定。之后当我们用鼠标选中这个文件时,相应的说明就会显示在项目管理器的底部。

六、 重命名文件

在编软件时免不了有时要改文件名,方法是:在项目管理器中用鼠标右键单击一个文件名,在弹出的菜单中选择“重命名”,往下您就看着办吧。

七、修改数据表时调整字段位置

当我们需要调整数据表中的字段位置,可在表设计器中很方便地移动。进入表设计器,用鼠标按住字段移动钮,如图1,然后上下移动到所需的位置即可。

图1

八、调整表格中各列的位置

1.在表格生成器中移动,用鼠标按住选定字段中的字段移动钮上下移动,如图2,与上面移动数据表的字段一样。

60

图2

2.在表单中移动,在属性窗口中选择表格的任一列,然后在表格上抓住要移动列的表头左右移动到所需位置。

3.在程序运行时移动,直接抓住要移动列的表头左右移动到所需位置。

九、设置控件的tabindex位置

一般说来,进入一个表单后,我们希望光标停在第一个输入位置,按回车则光标依次向后跳,但有时我们可能会发现,光标不依这个顺序走,这是因为我们在创建这些控件时没有按照所需的顺序,也就是在表单上先创建的控件,光标就先到。那么有没有办法改变这种顺序呢?有的!

我们可以设置控件的tabindex属性来改变这个顺序,但直接改变tabindex属性很麻烦,VFP提供了很方便的改变方法。用鼠标按下表单设计器工具栏上的按钮,每个控件的tabindex顺序即会标示出来,如图3,如果顺序不对,用鼠标按顺序将所有控件点一遍就行了,点完后在表单空白地方按一下,tabindex显示消失。

61

图3

十、改程序前应先改系统分析

当我们要对程序做功能上的修改(不是修改语句中的错误),我们应先看看系统分析,因为一个程序的各个部分是有着千丝万缕的联系的,当改了一个地方,可能导致另一个地方出问题,所以我们应先对系统分析修改,并平衡各方面关系,觉得系统分析上没有逻辑上的冲突和错误后,再按照新的系统分析去修改程序。

这样修改永远都可以保持有一个清晰的思路,而不会把程序改来改去,到最后自己都不知道改成什么样了,如果这时需要别人来帮忙修改更是不可想象。

十一、删除项目管理器中的文件

如果不想要项目管理器中的某个文件,可用“移去”按钮将这个文件从项目管理器中移出去。当您调用移去功能时,系统会问您是移去还是删除,移去一个文件并没有从磁盘上将其真正删除,如要真正删除,则应选择“删除”。

当然移去后的文件可用其它方式删除,但千万不要用其它方式删除一个在项目管理器中存在的文件,这样会造成项目管理器打开出错。一旦您很不走运出现这种情况,在打开项目管理器时会出现提示找不到某个文件,这时选择忽略,然后进入项目管理器,再将这个文件移去,这时不要用删除,因为这个文件已不存在,删除则又会出错。

十二、将磁盘上的文件添加到项目管理器中

按“添加”按钮,找到所要的文件,确定即可。

十三、设置主程序

当您在程序管理器中开始编第一个程序时(prg、菜单、表单等等),管理器会默认将其做为主程序,好比我们第六课中的菜单程序,但有时可能不是第一个编主程序,这时就要重新设置主程序,方法是找到您将要设为主程序的程序(绕口令), 62

用鼠标右键单击,然后选择“设置主程序”即可,设为主程序后,该程序的文件名会变为粗体。如果您要改变主程序也可用同样方法。

VFP 初级教程——第八课 程序的调试

我们在编程时难免都会出错,出错以后怎样找出错误的地方就变得很重要了,只有正确找出错误的地方才可以将其改正,下面我们就谈一些查错的常用方法。

一、如果在我们程序中有语法性的错误,当程序运行到错误的语句时系统就会停下来,并提示我们程序有错,往往还会说出是什么错误,如“命令中含有不能识别的短语或关键字”,并给出选择“取消”、“挂起”、“忽略”、“帮助”四个选择,它们的意思分别是:

取消——中止程序运行,回到命令窗口,相当于执行了cancel命令,在程序中创建的所有变量被释放(除公共变量),但数据库及数据表一般保持当时的状态,您可以用browse命令查看数据表的内容即记录指针所在的位置等等;

挂起——暂停程序,相当于执行了suspend命令,这时程序中的所有变量都保持原值,您可以用?命令查看变量的值,当然也可以查看数据表的情况;

忽略——忽略所出现的错误,即跳过出错的语句继续执行后面的语句;

帮助——显示有关出错的帮助信息,对于错误做更详细的说明。不过很多时候都没什么帮助。

如果这时您一眼就能看出问题出在哪,那么您可以用取消,然后进到程序中找出错误所在,将其改正。在选择了取消后,可能这时有表单是打开的,那么用鼠标点一下该窗口,然后调菜单上的文件—关闭。如果菜单是您自己的自定义菜单,用set sysmenu to default回到系统菜单。改完后,再次运行程序前,最好将所有的数据库及表关闭,以免在程序打开一个数据表时出现表已打开的错误,比较好的办法是在程序开头先关闭所有的数据库及表。关闭所有数据库的命令是:close databases all,关闭所有表的命令是:close tables all。

如果您不知道问题出在程序的哪个地方,那么就选择挂起,系统会弹出一个调试器窗口显示出错的语句,如图1(看上去有点头大吧)

63

,在跟踪窗

口的黄色箭头所指的语句就是出错的语句。这时不要马上改程序,因为程序还没有结束运行,如要改程序应先终止程序运行,按调试中的终止按钮,然后退出调试器(菜单上的文件—退出),接下来与上面选择取消后的处理方法相同。

一般不要选择忽略,因为程序中上下语句都有很紧密的关系,当一条语句出错后,如果继续运行,可能会出现很多错误,而后面出错的语句可能并没有错,是因为前面错了才导致后面的语句出错,如果前面正确,后面也会正确,因此对于初学者来说,选择忽略不利于找出错误所在。

二、有些时候,程序中的语句并没有出错,但是运行的结果却不是我们所要的结果,这往往是因为我们用错了语句,虽然语句本身是正确的,但用在了不该用的地方、或者该用的地方没有用、又或者语句的先后顺序错了等等,都可能导致这种情况。 一旦出现这种情况往往比上一种情况麻烦些,不容易一下看出问题出在哪里,那么需要我们仔细分析程序中的语句,看是否用的对,是否达到了我们的要求。

比如在我们前面的人事管理程序中,调出编辑人员表单,按了新增后,表单中的各控件的内容没有变,那么这就有两种可能,一是没有增加一条空记录,二是没有将控件刷新,这就要我们根据具体问题具体分析。

有时光看语句,怎么也看不出问题在哪,这就用采用一些辅助手段,最常用的辅助手段是在可能发生问题的地方将程序挂起,即在程序中加一句suspend,当程序运行到这里时将程序挂起,然后在命令窗口中查看各有关变量的值或数据表的情况。比如上面的错误,我们可以在新增按钮的click事件的结尾加一句suspend,当程序运行到这里时就会挂起,我们可以用browse查看数据表,如果发现里面没有一个空记录,那么说明漏了加空记录的语句。如找到错误了,记得在命令窗口中运行cancel,将程序终止再去修改程序。

64

另外在程序挂起时我们可以调出器,看程序执行到什么地方,调的方法是在菜单上的工具—调试器,可以在其中的监视窗口查看各表达式的值,在局部窗口查看各变量的值,而且您可以在窗口中长条文本框(如图2)中输入一个表达式或变量,调

试器会显示出它们的值。

如还不能找到问题,按可单步执行程序,即按一下运行一条语句,这样可以更有助于找到问题。至于调试器的其它功能,我们将在以后的课程中讲解。

常见错误

1. 启动一个表单后,再用代表这个表单的变量去调用它时却不行。调用表单的变量与其它变量一样,如是在一个子程

序中创建的,当这个程序运行结束后,这个变量也就释放了,即这个变量不再存在了,虽然表单还在,故不能用这个变量去调用该表单了。解决的方法是将其设为公共变量,或在更高层的程序中创建,还可以在调用表单命令中加上“linked”子句(do form...name...linked)。

2. 在表单中的一个事件(如init)中创建了一个变量,但在表单的其它地方却不能使用。一个事件程序相当于一个

子程序,当子程序运行完后在其中创建的变量也就没不存在了。解决的方法是将其设为公共变量,或在更高层的程序(如调用表单的程序)中创建。

3. 常常在表格中或其它地方看不到数据表中的记录。这往往是因为记录指针到尾部,即EOF()为真。

4. 修改记录时却个修改了另一个记录。这往往是因为进入修改后又做了查询,查询后没有将记录指针返回到原记录。

5. 进入一个循环后就死机了。这是因为没有设置跳出循环的条件,或者条件永远不能满足,比如一个循环的跳出条件

是当一个变量的值达到某一数值,每循环一次应将该变量加1,但忘了加1的语句,就会造成死循环,因为变量的值永远达不到预定的数值。

6. 查询英文时,明明有的记录查不到。这可能是大小写不一样造成的,比如:locate for name='crops',如果数据

表中的是“CROPS”那么就查不到。解决的办法:locate for upper(name)='CROPS'。

7. 不管是英文还是中文,还是查不到所要的记录。这种情况很可能是输入的查询值后面有空格,比如还是上面那个查

询语句,如果操作者不小心在文本框中输入了“crops_”(下划线代表有一个空格),而这个空格往往是不容易被发现的,那么查询时就可能找不到,除非数据表中的数据也刚好是这样,这里要注意一点一个字符字段的内容如小于字段的长度,系统会自动为该字符后面加上空格,比如上面那个例子,如果name的长度是8,则“crops”这个记录name的值就是“crops___”(后面有三个空格),那么有三个空格和有一个空格的就不相等。解决的办法是用trim()函数将输入的查询值后面的空格去掉,更进一步,为了防止不小心前面也加了空格,可用alltrim()将前后的空格都去掉。

8. 用do form启动一个表单后,应等关闭表单后再执行do form后面的语句,但表单启动后却接着运行后面的语句。

这是因为表单的windowtype属性没有设为“1-模式”,解决的办法不用我说了吧。

9. 当在数据表中移动记录指针等操作时出现意想不到的错误。往往是因为没有记录。

10. 按索引查询时找不到所要的记录,当然记录是有的。索引没有及时更新,对于不是用的结构化索引,即所用索引不

是在建表时创建的,而是以后用别的文件名创建的索引,不会与数据表一起打开,那么当数据表更新后......,解决的办法想必大伙儿都知道吧。

VFP 初级教程——第九课 软件的编译及制作安装盘

第九课 软件的编译及制作安装盘

软件的编译

65

如果您的程序全部都编好了就可以开始编译您的软件了,很简单,方法如下:

1. 按下项目管理器中的连编,出现对话框图

1;

2. 选择“连编可执行程序”,确定;

3. 输入编译后的EXE文件名,注意目录,然后保存;

4. 接着系统便进入编译过程,这一过程是电脑自动完成的。在这一过程中系统会首先检查您的程序是否有错误,如有

错误有时会给出提示,在提示中您一般可以选择“忽略”、“全部忽略”、“取消”,这里的“忽略”就是不管出现的错误继续编译,当然一般不应该这样,一旦出现错误提示应选择取消,然后找出相应的错误,改正后再编译。为了容易查找错误,系统还将错误记录下来,在菜单的“项目”-“错误”中可以看到,其中会讲明是什么错误,发生在哪个程序的哪一条语句中。对于有些错误会不给出提示而直接忽略,但它仍然会把错误记录下来。

如果系统编译时没有记录错误,那是因为在菜单上的“工具”-“选项”-“常规”-“编程”中的“记录编译错

误”没有打开。

制作安装盘

VFP编译生成的EXE文件是不能直接在另一台电脑上运行的,除非该电脑中已经装有VFP系统,因为EXE文件的运行要依赖于安装在WINDOWS系统中的运行时刻库。为此我们要为该软件制作一套安装盘,方法如下:

1. 在您开发的软件的目录下建一个子目录,比如叫exe,当然您也可以建在别什么地方或叫别的什么名字;

2. 将该软件所要用到的数据库(dbc)、数据库备注(dct)、数据库索引(dcx)、表(dbf)、表索引(cdx、idx)、表备注(fpt)、

内存变量文件(mem)等等,再就是编译后的exe文件通通复制到上面所建的目录中,然后将复制过去的数据表中试运行用的记录清除,但要注意有些数据可能是软件预先应提供的,那么就不应该删除,比如在一个数据表中预先存入全国各省份名称与软件一起提供给用户,以免用户再去输入。

注意:prg文件、菜单文件、表单文件、报表文件、标签文件等等不要复制进去,因为它们已经被编译在exe文件中了,还有就是不属于软件运行的文件,如系统分析文件,也不要复制进去。

3. 启动VFP系统,如果VFP系统已经启动,最好关闭所有打开的文件。

66

4. 选择菜单上的“工具”-“向导”-“安装”,出现图

2;

5.

按“发布树目录”后面的按钮,找到在第1步中建的那个目录,选定,按下一步,出现图

3;

67

6. 选择Visual Foxpro运行库,其它三个一般不选,下一步,图

4;

7. 选择生成的安装文件存放的目录,一般可在软件目录中,即与exe目录在一起,还要选择安装方式,要么是1.44M

盘、要么是网络安装、或者两都选,我想现在没有人再用1.2M软盘这样古老的东西了吧。下一步,图

5;

68

8. 在安装对话框和版权信息中输入适当内容,安装对话框主要是用在安装软件时显示的信息,版权信息中一定要输入

内容,随便输入点啥都成,否则按不了下一步,执行程序中不要输入内容,它不是指软件所要执行的程序。接着再下一步,图

6;

序管理器组都可更改,一般就设为都可更改,下一步,图

7; 9. 输入安装的默认目录,在开始菜单的中的程序管理器组的名称,确定用户安装时是仅可以更改目录,还是目录与程

10. 在文件列表中找到编译的exe文件,按下它后面的程序管理器项小方框,出现图8,

在说明中输入开始菜单中启动该软件的

69

图标说明,命令行中输入exe文件名,记得前面加上“%s”,这是为了软件安装在不同目录中也能正常运行,如果您喜欢,还可为它选择一个图标(按“图标...”按钮选择),否则就是狐狸头

文件后的程序管理器项小方框中应有一个钩,下一步,图

9;,接着确定,在图7的exe

11. 一级方程式赛车就要到终点了,如果没有问题就按下“完成”开始冲线,要是有问题,想起点什么来,就按“上一

步”回去看看;

12. 一旦按下“完成”就不能再回头了,系统便开始按照您的设置制作安装盘,可能需要那么几分钟时间,期间会有如

图10的显示,制作完成后会有一个报告,如图11,没多大意义;

70

13. 看完报告后,按完成,这次可就真的完成了,您会在磁盘上看到生成的安装文件目录,如果是网络安装,目录是

“netsetup”,其中是安装您的软件所需的文件,如果是3'盘,目录是“disk144”,其中还会有disk1 、disk2、disk3.......等子目录,分别把每个目录中的文件复制到一张盘上,安装时从第一张盘开始,运行setup即可。

VFP 中级教程:第一课 更多对象

第一课 更多对象 编辑框(editbox)

复选框(checkbox)

微调控件(spinner)

页框(page)

形状(shape)

命令按钮组(commandgroup) 列表框(listbox) 图象(image) 容器(container) 选项按钮组(optiongroup) 组合框(combobox) 计时器(timer) 线条(line)

[dvnews_page=VFP 中级教程:第一课 更多对象]

编辑框对象(editbox)

71

使用编辑框控件可以编辑诸如字符型变量、数组元素、一般字段以及备注型字段的内容,而用得最多的地方就是编辑备注型字段。

VFP所有的标准编辑功能都能在此使用,比如剪切、复制、粘贴等。编辑框中的文本可以垂直滚动,并且是自动换行的。

命令按钮组

对象(commandgroup)

创建一组命令按钮使您可以同时对多个按钮进行设置,比如移动它们的位置,如果有5个按钮是分开的,要分别设置它们的位置,假如5个按是和某一个主题有关的,就可以把它们设为一个命令按钮组,这样只要设好这个组的位置,各按钮也就各就各位了。当然也可以分别设置每个按钮的属性、事件程序等。

使用buttoncount属性可以设置组中按钮的个数。

选项按钮组

对象(optiongroup)

选项按钮组是一个容器,其中包含若干选项按钮,它可以让您在一组按钮中选择一个,它是单选的,即您选了一个按钮,原来所选的按钮就释放,始终只能有一个按钮被选中。

被选中的按钮是怎样的不需要我再啰嗦了吧。

使用buttoncount属性可以设置组中按钮的个数。

vfp编程实例及提高 vfp教程 VFP编程入门到精通教程 vfp教程

在程序运行时选了第几个按钮,它的value属性就是几,如果您设置了controlsource属性为一个变量,那么该变量的值也是这个数,您可以通过这个变量或其value值得知是那个按钮被选了。

在线条对象中供下载的秒表程序里面有对选项按钮组的应用。

[dvnews_page=VFP 中级教程:第一课 更多对象]

复选框

对象(checkbox)

复选框用于在两种状态之间切换,比如“真”、“假”或者“是”、“否”等等。

当被选中时,一般条件为真,这时在复选框中有一个钩,其value值为1,否则为0,也可以是“.t.”或“.f.”,如设置了controlsource,其中的变量也一样。

在caption属性中输入文字,可显示在复选框的边上。

72

在线条对象中供下载的秒表程序里面有对复选框的应用。

列表框对象

(listbox)

列表框用于显示一个选项列表,在其中您可以选择所需要的项目。

可用于选择的数据类型有值、别名、SQL语句、查询、数组、字段,具体使用哪种由rowsourcetype属性确定。

较常用的是值和字段,如果选择的是值,也就具体指定几个选择项,如“男”、“女”,那么就将这些候选值放入rowsource属性,各项目之间用逗号隔开,字符不需引号。

如果是字段,就将字段名放入其中,最好带上所在表的别名,那么当程序运行时该字段的所有记录(除了用set filter等语句屏蔽掉的记录)就将成为侯选项。

当一个项目被选定后,其value以及controlsource都将是这个值。

可以在程序设计时给value设个初始值,这样在运行时光标就会停在这个值上。如果在编程序时不可预知候选项将会是些什么值,如使用字段常常是这样,那么就在程序中编入相应语句,在列表打开之前先给它的controlsource变量赋值,要注意的是一般不要赋候选项中没有的值。

选中某个选项后,controlsource变量的值就是该选项的字符。

最后再提醒一下,rowsource和rowsourcetype属性都要做相应设置。

在线条对象中供下载的秒表程序里面有对列表框的应用。

组合框

对象(combobox)

当选择了组合框以后,组合框将打开,显示一个选项列表,您可以从其中选择一个。

组合框组合了文本框和列表框的功能,您可以在文本框部分中输入信息,也可以在列表框部分中选择。它的优点是不象列表框要占很大地方,缺点是要点击一下拉下列表部分才能选择。

与列表框一样也有rowsource和rowsourcetype属性,使用方法与列表框一样,都要设置。

[dvnews_page=VFP 中级教程:第一课 更多对象]

微调控件

对象(spinner)

73

微调控件可以让用户在一个范围内对数值进行微调,所谓微调就是将数值一点点地增加或减少,而增加减少的方法就是按向上或向下的箭头,每按一下加1或减1,您还可以直接将数值通过键盘输入进去。

微调控件有个keyboardhighvalue属性,它限制了键盘输入的最大值,既然有最大值就肯定有最小值喽,最小值的限制属性就是keyboardlowvalue。

对鼠标输入自然也有上、下限制,属性分别是spinnerhighvalue和spinnerlowvalue。

在线条对象中供下载的秒表程序里面有对微调器的应用。

图象

对象(image)

图象对象用于显示图片,5.0版只能显示bmp格式的图形文件,而6.0可显示除bmp外包括jpg、gif等当前流行的图形文件。

但图象对象只能显示图片,而不能修改。然而它同样有许多的属性、事件和方法,可以响应各种事件并在程序运行时更换图象文件。

这里最主要的属性就是picture,将其设置为所需要的图形文件名,当程序运行时,该图象就会在表单显示出来。 在线条对象中供下载的秒表程序里面有对图象的应用。

计时器

对象(timer)

计时器的主要作用就是当到一定时间激活一事件程序。

程序运行时该控件是看不见的,只在后台运行。

其主要属性是interval,将该属性设为所需要的时间值,注意该值是以毫秒为单位的,即1秒应设为1000。

主要事件是timer,当计时时间一到,该事件即被激活,其中的程序将被执行。

比如我们可以在程序启动时,显示一欢迎窗口,在其中放入一计时器,interval设为3000,timer事件中写入

thisform.release,这样显示3秒钟后窗口自动消失。

激活事件后,计时器重新回零,如果计时器仍然有效,则又开始另一次计时。因此我们可以用该控件控制一段程序每隔一定的时间重复执行。

[dvnews_page=VFP 中级教程:第一课 更多对象]

页框

对象(page)

74

页框是一种容器型控件,其中可以包含若干页,每一页又是一个容器控件,用法就好象表单,也就是说可以在其中放入其它各种控件,如果您高兴还可以在其中放入一个页框,单击各页左上角的标签来选择页。但是整个页框仍是一个控件,它也必须放入表单这个更大的容器才能使用。

页框的主要属性就是pagecount,该属性的值决定了页框中页数的多少,隐含是2。

页框最主要的一个作用是当要显示的内容在一屏显示不完时,可将要显示的内容分成若干页放到页框中,比如在一个表单上显示或修改一个有很多字段的记录,就可这样做。

注意:象上面的做法有个很大问题,就是我们调用表单或页框的refresh方法时只有当前活动的页才被刷新,也就是说,如果您将一条记录的多个字段放在了不同的页,当您从一个记录跳到另一个记录,比如有一个向我们的人事管理软件编辑人员表单中的“下一个”按钮被按下,接着调用页框的refresh方法,比如执行如下语句:

thisform.pageframe1.refresh

那么就只有您所看到的页面被刷新,即其中的字段显示的是下一条记录的内容,而其它看不见的页面中的字段还是显示的原来的内容,如这时您选择其它页面就可以看到这种情况,无疑这将带来混乱。解决的办法是要刷新页框中的每一个页面,比如:

thisform.pageframe1.page1.refresh

thisform.pageframe1.page2.refresh

...

每个页面的标签文字可用页面(page)的caption属性设定。

在线条对象中供下载的秒表程序里面有对页框的应用。

容器

对象(container)

容器对象就好象只有一个页面的页框,在它里面可以放入各种控件。

您可能会问,用它有什么好处呢,直接将控件放在表单上不就行了吗?

好处在于您可以把一些相关控件放在一起,比如要移动位置时只需移动容器就行了,里面的所有控件都会一起移动,另外我们可能在运行过程由中程序根据某种情况把一些控件取消或添加到表单,如果把它们放在一个容器中,就可以很方便地一起取消或添加。

线条

对象(line)

线条是一图形控件,作用就是在表单上画线,主要用来美化我们的表单。

该控件有几个主要属性:height、width、lineslant。

75

这里的height不是指线条的长度,width也不是指粗细,这两个属性构成一个矩形(当然这个矩形是看不见的),线条则为这个矩形的对角线,由此可确定线条的长度和倾斜度。而倾斜的方向由lineslant确定,将其设为“”则从左上向右下倾斜,设为“/”则从右上向右下倾斜,隐含为“”。

线条的粗细由borderwidth确定。

应用一些技巧也可以让该控件有实际作用,比如可以让它做为一个秒针,在计时器控制下改变它的height和width属性,使它走动。

形状

对象(shape)

用于创建矩形、圆角矩形、圆和椭圆。

这里最重要的属性是curvature,用于设定曲率,为0时没有曲率,形状为矩形;99为最大曲率,为圆;0-99之间的数值可创建圆角矩形,将圆角矩形压扁到一定程度,使其两边的直线没有,而是圆角对圆角就成了椭圆。

另外设置backcolor,可改变它的填充颜色。

在线条对象中供下载的秒表程序里面有对形状的应用。

VFP 中级教程:第二课 用新的控件改进人事管理(一)

第二课 用新的控件改进人事管理(一)

这一课我们用上一课讲的一些控件来对前面的人事管理软件进行改进,使其更易用,更不容易出错,最主要当然是为了进一步说明这些控件的用法。

先讲第一个改进,用选项按钮来选择性别

在新增和修改人事档案的表单中,本来性别是要输入的,虽然只能输入“.t.”或“.f.”,倒是不会出现输入其它字符的错误,但是很不直观,所以我们用选项按钮来改进。

(1)、将表单上输入性别的文本框换为选项按钮组

性别就两种(大概不会有第三种)。 ,选项按钮组做好后隐含两个按钮,这里正好我们需要两个,因为

但是按钮隐含的排列是上下排列,这样很占地方,我们将其调整为水平排列,方法是:先把选项按钮组拉的比较大,使两个按钮都能看见,暂时将别的控件盖住也不要紧。然后在属性窗口中选择optiongroup1下面的option2,然后将其放到与option1水平排列的地方,如不能精确调整它的位置,看一下初级教程第七课中的精确调整对象位置。

76

接着设置标题,将option1的caption设为“男”,option2该设为......,设好如图

1。

将optiongroup1的controlsource设为xb,为什么要这样,直接设为“性别”字段不行吗?不行!因为性别字段的值是逻辑型,而optiongroup1的值为数值型,因此要用一个数值型的变量来接收optiongroup1的选择值,然后用适当方法将其变换为逻辑型存入数据表。具体方法下面会讲。

(2)、设置xb的初始值,进入“编辑人员”(bjry)表单的修改,在其 load 事件中输入如下代码: public xb

if 性别

xb=1

else

xb=2

endif

在这里先将xb设为公共变量,因为 load 事件程序是一个子程序,当这个程序运行完毕,其中所有的私有变量(即在本程序中创建的变量)都将释放,之后就再也找不到这个变量了,但这个变量在其它地方又要用,故将其设为公共变量 这段代码不能放在 init 事件中,这是因为表单的 init 事件是在所有控件的 init 事件发生之后,而当控件的

注意 init 事件发生时,即初始化时,就需要这个变量,而这时还没有,所以会发生找不到变量的错误,而 load 事

件则是发生在所有事件之前,即表单启动的时候。 然后根据当前记录的性别字段设置xb的值,如果是“真”就设为1,即“男”,否则为2,即“女”,这样进入表单后选项按钮的黑点就会根据xb的值来显示。

注:在需要逻辑表达式作判断时,如果是逻辑变量,因为其本身就是一个逻辑表达式,为“真”的话就直接写这个变量名,为假的话就写为“.not. 变量名”,而不要写成“变量名=.t.”或“变量名=.f.”

(3)、将“新增”按钮的click事件程序改为如下:

*根据选项按钮所做的选择,将相应的值存入性别字段

if xb=1

replace 性别 with .t.

77

else

replace 性别 with .f.

endif

append blank &&增加一条空记录

xb=1 &&将xb设为1

*这是一个新记录,还不知道是男或女,因此一律设为1,即“男”,作为初始值 thisform.text1.refresh &&将text1的内容刷新,下同

thisform.optiongroup1.refresh

thisform.text2.refresh

thisform.text4.refresh

thisform.text5.refresh

thisform.text6.refresh

thisform.text7.refresh

thisform.text8.refresh

thisform.edit1.refresh

thisform.text1.setfocus &&将焦点设到text1

仔细看看与以前有什么不同

(4)、将“上一条”按钮的click事件程序改为如下: skip -1 &&记录指针向上跳一行

*以下一段程序检测记录指针是否到了开头,如果是给出提示,并把指针定位到第一个记录 if bof() &&假如指针已到开头

wait '已到开头' window nowait &&在右上角显示的提示,鼠标或键盘一动提示消失 go top &&将指针定位到第一个记录

endif &&假设结束

*根据当前记录的性别字段设置xb的值,如果是“真”就设为1,否则为2

*在需要逻辑表达式作判断时,如果是逻辑变量,因为其本身就是一个逻辑表达式, *为“真”的话就直接写这个变量名,为假的话就写为“.not. 变量名”,

*而不要写成“变量名=.t.”或“变量名=.f.”

if 性别

xb=1

else

xb=2

endif

thisform.text1.refresh &&将text1的内容刷新,下同

thisform.optiongroup1.refresh

thisform.text2.refresh

thisform.text4.refresh

thisform.text5.refresh

thisform.text6.refresh

thisform.text7.refresh

thisform.text8.refresh

thisform.edit1.refresh

78

thisform.text1.setfocus &&将焦点设到text1

“下一条”按钮也做相应改动,看看自己会不会做。

(5)、将“退出”按钮的click事件程序改为如下: *根据选项按钮组所做的选择,将相应的值存入性别字段

if xb=1

replace 性别 with .t.

else

replace 性别 with .f.

endif

thisform.release &&本表单释放

OK了!

VFP 中级教程:第三课 用新的控件改进人事管理(二)

三课 用新的控件改进人事管理(二)

这一课讲用组合框选择输入部门和职务。之所以要用选择输入,是因为对于同一值不同人在不同时候输入是不一样的,比如“人事部门”,有人可能会输入成“人事部”,再加上无意识的输入错误,使得一个部门会有好几种说法,这将给查询、统计等带来很大问题,因此我们对于一个其内容只有有限个选择的字段的输入,最好采用选择输入。

从我们前面所讲过的控件中可以看出,复选框、选项按钮组、列表框、组合框都可以用作选择输入,那么时候该用哪一个控件呢?一般原则是:

1、对于具有“是”、“否”两种选择的,用复选框;

2、选项是固定的,并且选项不太多,可以用选项按钮组;

3、对于选项是可变的,并且选项不是非常多,可用列表框;

4、对于选项是可变的,并且选项非常多,可用组合框,因为选项非常多的情况下,用列表框找起来是很麻烦的,而组合框可以直接输入,只是在必要的时候才查一下。另外如果不希望该输入控件占太多地方,也可用组合框,因为它平时只占一行,拉下时才显示框,而列表框始终要占一块位置,当然您高兴也可以让列表框只占一行,但那样操作起来一定别扭。

要实现选择输入,就要将可选择的项目事先准备好,对于选项来说有两种情况,一种是固定的、一种是可变的。比如性别就是固定的,而部门就可能是不固定的,因为一个单位随时可以增减部门。

固定的选项可以在编程时编好,而不固定的一般就要有个数据表来存放选项,而且这个表可由使用的人任意增删和修改,下面我们就来讲讲怎样实现这种功能。

1. 在人事档案数据库中建立一个数据表,就一个字段,字段名为“部门”;

2. 在菜单中加一项“维护”,下面有一个子菜单项“部门字典”使用的命令是:

do form bmzd name bmzd

79

3. 建立一个表单,界面如图

1;

4. 在“新增”按钮的click事件中写入如下程序: if this.caption='新增' &&假如本按钮的标题为“新增”,表示第一次新增 this.caption='继续新增' &&将本按钮标题设为“继续新增”

endif &&结束假设

select bmzd &&选择部门表

append blank &&加一空记录

thisform.grid1.readonly=.f. &&将表格设为非只读,即可以修改

thisform.grid1.refresh &&表格刷新

thisform.grid1.setfocus &&将焦点放到表格上以利于输入

5. 在“修改”按钮的click事件中写入如下程序:

if this.caption='修改' &&如果本控件的标题是“修改”

this.caption="结束修改" &&将本控件的标题设为“结束修改”

thisform.grid1.readonly=.f. &&将表格设为非只读,即可以修改

else &&否则

this.caption="修改" &&将本控件的标题设为“修改”

thisform.grid1.readonly=.t. &&将表格设为非只读,即可以修改

endif

thisform.grid1.refresh &&表格刷新

thisform.grid1.setfocus &&将焦点放在表格上

6. 在“删除”按钮的click事件中写入如下程序:

select bmzd &&选择部门字典

*虽说目前肯定是bmzd表,不用选也可以,但这是为了防止以后程序做了某种改动, *造成当前表不是bmzd而使程序出错,这样就万无一失了。

delete &&删除当前记录

80

thisform.grid1.refresh &&表格刷新

thisform.grid1.setfocus &&将焦点放在表格上

7. 在“恢复”按钮的click事件中写入如下程序: select bmzd &&选择部门字典

*虽说目前肯定是bmzd表,不用选也可以,但这是为了防止以后程序做了某种改动, *造成当前表不是bmzd而使程序出错,这样就万无一失了。

recall &&恢复当前记录

thisform.grid1.refresh &&表格刷新

thisform.grid1.setfocus &&将焦点放在表格上 8. 在“退出”按钮的click事件中写入如下程序:

select bmzd &&选择部门表

locate for delete() &&查找是否有做了删除标记的记录

if found() &&假如找到

pack &&将做删除标记的记录彻底删除

endif &&结束假设

thisform.release &&关闭本表单

9. 在表格下的column1下的text1控件的lostfocus事件中写入如下程序:

*当处于新增状态时,即command1的标题为“继续新增”,

*一旦光标离开本格,表示新增结束,就将表格设为只读,

*同时将标题设回“新增”

if thisform.command1.caption='继续新增'

thisform.grid1.readonly=.t.

thisform.command1.caption='新增'

endif

10. 存盘退出

这样用于输入和维护部门选项的子功能就做好了。

下面在编辑人员的表单中设置一组合框用于输入部门:

1. 将原来用于输入部门的文本框删除;

81

2. 换上一组合框,如图

2;

3. 将组合框的controlsource设为“rsda.部门”;

4. 将rowsourcetype设为“6-字段”;

5. 将rowsource设为“bmzd.部门”;

6. 重新设置所有控件的tabindex,设置方法在第七课的第九条“设置控件的tabindex位置”中有讲解。

7. 将菜单的初始化代码改为如下: set talk off &&关闭命令响应

set safety off &&当覆盖磁盘上的文件时不提示,当程序编好后,不会错误覆盖文件

set date ansi &&设置日期为“年.月.日”方式

set century on &&设置年为4位数表示

set default to d:softjfzccmccmcc2rsgl2 &&设置软件所在目录,应根据您的目录做适当修改

use rsda &&打开人事档案数据表

select 0 &&选一空工作区(新增语句)

use bmzd &&打开部门表(新增语句)

dkda=.f. &&设置一变量用于检测档案是否打开,真为打开,假为关闭

至此用选择输入部门的功能就编好了,用同样方法,我们可以编出用选择输入职务的功能,这做为本课的练习,大家自己做一做。

VFP 中级教程:第四课 用新的控件改进人事管理(三)

第四课 用新的控件改进人事管理(三)

在我们的人事管理软件中,您可能会感觉输入简历的地方小了,这一课我们就来讲利用页框对这一问题进行改进。

1. 进入bjry表单的修改状态;

2. 将表单拉大,把所有控件移到一边;

82

3.

按工具栏上的页框按钮,在表单上做上页框,如图

1;

4. 在属性窗口中选择pageframe1下的page1,将其caption属性设为“基本情况”,再将page2的caption设为“简

历”;

5. 回到表单把除简历和按钮以外所有控件剪切,再选择page1,将控件粘贴到page1中,如图2,接着按同样方法把

简历控件粘贴到page2中,如图3,由于这个页面中只有一个控件,页标题已说明是“简历”,故“简历”标签可

以删掉。

83

6. 表单恢复原状;

7. 将“新增”按钮的click事件程序改为如下: select rsda &&选择人事档案表

*根据选项按钮所做的选择,将相应的值存入性别字段

if xb=1

replace 性别 with .t.

else

replace 性别 with .f.

endif

append blank &&增加一条空记录

xb=1 &&将xb设为1

*这是一新记录,还不知道是男或女,因此一律设为1,即“男”,作为初始值

*将部门设为部门字典中的第一个记录,这样可防止部门没有选择而为空 select bmzd

go top

select rsda

replace 部门 with bmzd.部门

thisform.pageframe1.page1.refresh &&将页面及其控件的内容刷新 thisform.pageframe1.page2.refresh &&将页面及其控件的内容刷新

thisform.pageframe1.page1.text1.setfocus &&将焦点设到text1

8. 将“上一条”按钮的click事件程序改为如下: skip -1 &&记录指针向上跳一行

*以下一段程序检测记录指针是否到了开头,如果是给出提示,

*并把指针定位到第一个记录

if bof() &&假如指针已到开头

wait '已到开头' window nowait &&显示提示,鼠标或键盘一动提示消失 go top &&将指针定位到第一个记录

endif &&假设结束

*根据当前记录的性别字段设置xb的值,如果是“真”就设为1,否则为2 *在需要逻辑表达式作判断时,如果是逻辑变量,因为其本身就是一个逻辑表 *达式,为“真”的话就直接写这个变量名,为假的话就写为“.not. 变量 *名”,而不要写成“变量名=.t.”或“变量名=.f.”

if 性别

xb=1

else

xb=2

endif

thisform.pageframe1.page1.refresh &&将页面及其控件的内容刷新 thisform.pageframe1.page2.refresh &&将页面及其控件的内容刷新

thisform.pageframe1.page1.text1.setfocus &&将焦点设到text1

84

9. 将“下一条”按钮的click事件程序改为如下: skip &&记录指针向下跳一行

*以下一段程序检测记录指针是否到了结尾,如果是给出提示,

*并把指针定位到最后一个记录

if eof() &&假如指针已到结尾

wait '已到结尾' window nowait &&显示提示,鼠标或键盘一动提示消失 go bottom &&将指针定位到最后一个记录

endif &&假设结束

*根据当前记录的性别字段设置xb的值,如果是“真”就设为1,否则为2 *在需要逻辑表达式作判断时,如果是逻辑变量,因为其本身就是一个逻辑表 *达式,为“真”的话就直接写这个变量名,为假的话就写为“.not. 变量 *名”,而不要写成“变量名=.t.”或“变量名=.f.”

if 性别

xb=1

else

xb=2

endif

thisform.pageframe1.page1.refresh &&将页面及其控件的内容刷新 thisform.pageframe1.page2.refresh &&将页面及其控件的内容刷新

thisform.pageframe1.page1.text1.setfocus &&将焦点设到text1

10. 完事了。

VFP 中级教程:第五课 更多属性

自动对中属性(autocenter)

指定表单对象第一次显示于VFP主窗口时,是否自动居中放置。

设置为.t.自动居中,隐含设置为.f.。

应用的对象

表单

自动关闭表属性(autoclosetables)

指定数据环境中的表是否在表单或表单集释放时关闭。

运行时只读。

设置为.t.自动关闭,隐含为.t.。

如果表在表单启动以前就打开了,那么不会自动关闭。

85

自动打开表属性(autoopentables)

指定数据环境中的表是否在表单或表单集启动时打开。

运行时只读。

设置为.t.自动打开,隐含为.t.。

如果表在表单启动以前就打开了,那么此功能失效。

[dvnews_page=VFP 中级教程:第五课 更多属性]

背景色属性(backcolor)

指定对象中文本和图形的背景色。

在设置时可单击设置框右边的“三点按钮”来选择颜色。

应用的对象

绝大多数对象都适用,但不适合于按钮。

前景色属性(forekcolor)

指定对象中文本和图形的前景色。

在设置时可单击设置框右边的“三点按钮”来选择颜色。

应用的对象

绝大多数对象都适用,但不适合于按钮组、选项组或形状。

设置取消属性(cancel)

指定一个命令按钮是否为“取消”按钮;即当操作者按下“Esc”键时,该按钮的click事件发生,也就是说可设置一个按钮用Esc键代替鼠标单击,当然鼠标单仍然起作用。

一般用于“取消”、“退出”等按钮。

[dvnews_page=VFP 中级教程:第五课 更多属性]

设置隐含属性(default)

指定一个命令按钮是否为隐含的“确定”按钮;即当操作者按下回车键时,该按钮的click事件发生,也就是说可设置一个按钮用回车键代替鼠标单击,当然鼠标单击仍然起作用。

一般用于“确定”按钮。

86

可关闭属性(closable)

指定表单可否单击右上角的“叉”按钮来关闭表单。

设置.t.为可以,隐含为.t.。

当设置为.f.时,不能由双击窗口左上角的图标关闭表单,也不能单击图标,在弹出菜单中选择“关闭”。

删除标记属性(deletemark)

指定在表格中是否在左边出现删除标记列。

设置为.t.时显示删除标记列,隐含为.t.。

有时为了防止操作者误按而导致记录被删除,故可将该列隐去。

[dvnews_page=VFP 中级教程:第五课 更多属性]

动态背景及前景色属性(dynamicbackcolor、dynamicforecolor)

用于指定表格列对象的背景色和前景色。

所谓动态,是指同一列的不同记录可以为不同颜色,用表达式设置该属性,表格每次刷新会重新计算表达式的值,并根据此值重新设置颜色,因此常常用于根据不同的条件设置颜色。

比如前面有一个删除标记属性,我们为了防止误删除而将删除标记列隐藏,但这样就使得那些做了删除标记的记录看不出来,这又是一个不足,为此我喜欢将列的dynamicbackcolor属性设为:

iif(delete(),rgb(192,192,192),rgb(255,255,255))

这样做了删除标记的记录背景将变为灰色,而没有作删除标记的为白色,为了显示效果好,表格每一列的该属性都要设置为这一表达式,麻烦点。

类似的属性还有dynamicalignment、dynamiccurrentcontrol、dynamicfontbold、dynamicfontitalic、

dynamicfontstrikethru、dynamicfontunderline、dynamicfontname、dynamicfontoutline、dynamicfontshadow、dynamicfontsize、dynamicinputmask等。

可用属性(enabled)

指定对象能否响应用户引发的事件。

也就是说使控件不可以用,很多时候由于某种原因,我们会不让操作者操作某个控件,这时我们就可将该控件的enabled属性设为.F.,这样该控件就不能使用了,但是通过程序还是可以操作它的,比如一个文本框设为非可用后,用户不能在其中输入字符,但如下语句仍起作用:

thisform.text1.value='ABC'

87

隐含设置为.T.。

[dvnews_page=VFP 中级教程:第五课 更多属性]

输入输出格式属性(format)

指定某个控件的value属性的输入和输入格式。

它很类似以前版本的FOXPRO和FOXBASE的@...say中的function子句的用法。

比如我们想在标进入一个文本框时,将框中的字符全选,以便我们一输入内容就可将原来的内容覆盖,那么就可以将format设为“k”(具体设时不用引号),有人问我为什么是“k”,我也不知道,反正就是这个意思。

表单数属性(formcount)

返回一个表单集中的表单数。

该属性只读。

您可以用它来循环遍历表单集中的所有表单。

图标属性(icon)

您可能很讨厌在所有您编的表单上都有个狐狸头,而想用自己的图标来代替(那多有个性),在表单的这个属性写入您的图标文件名就能实现这一心愿。

可以是ico或bmp格式文件。

[dvnews_page=VFP 中级教程:第五课 更多属性]

增量属性(increment)

单击上箭头或下箭头时,微调控件中数据值增加或减少的量。

默认值为1

递增搜索属性(incrementalsearch)

指定控件是否支持对键盘操作的递增搜索。

比如在列表框中,要搜索“ELASTIC”这个单词,可以键入E-L-A...等等,在键入字母的时候,VFP会逐步搜索所键入的字母组合,逐字匹配要找的单词。在非递增搜索的情况下,它会找首字母为E的第一个单词,然后再找首字母为L的第一个单词...,以此类推。

设为.T.时为递增搜索,默认为.T.。

注意:在递增情况下,如果键入第一个字母后隔太长时间键入第二个字母,则不会递增搜索。_dblclick系统变量的设置确定时间长短。

88

应用于:组合框、列表框。

输入输出样式属性(inputmask)

指定控件数据的输入格式和显示方式。

类似于以前@...say...中的picture子句用法。具体用法请下载附录2:VFP6.0中文版语言参考手册参考。

比如我们要控制一个文本框只能输入3个字符,就可以在此属性设为“XXX”,在属性框中设定时不需要引号,如用程序设置要引号,如下:

thisform.text1.inputmask='xxx'

如果限制20个字符是否要打20个“X”?没必要这么麻烦,可在属性框中做如下设置:

=replicate('x',20)

如是用程序设置:

thisform.text1.inputmask=replicate('x',20)

应用于:列、组合框、微调、文本框

[dvnews_page=VFP 中级教程:第五课 更多属性]

触发时间属性(interval)

指定计时器控件的timer事件之间的时间间隔毫秒数。

默认为0,不触发timer事件。

当我们要定时做一件事时,就在表单上放一个计时器,将其interval属性设为所需要的时间,然后在timer事件中写入所要执行的代码。

触发时间属性(interval)

指定计时器控件的timer事件之间的时间间隔毫秒数。

默认为0,不触发timer事件。

当我们要定时做一件事时,就在表单上放一个计时器,将其interval属性设为所需要的时间,然后在timer事件中写入所要执行的代码。

最小化按钮属性(minbutton)

指定表单是否含有最小化按钮。

设置为.T.时可以最小化,默认为.T.。

89

但即使设置为.F.,仍然可以用程序的方法将窗口最小化:

thisform.windowstate=1

[dvnews_page=VFP 中级教程:第五课 更多属性]

边框样式属性(borderstyle)

指定对象的边框样式。

对于不同的对象设置是不同,这里只讲一点,对于表单,将该属性设为“2-双线边框”,则表单不能被改变大小,默认是设为“3-大小可调边框”。

我以前就不知道这个属性的设定可使表单不能改变大小,曾经为了做到这一点编了一大堆代码,想想真可笑。

应用于:命令组、编辑框、表单、图像、标签、线条、选项组、_screen(VFP主屏幕)、形状、文本框。

页框页数属性(pagecount)

指定一个页框控件中的页面数。

设计及运行时只读。

最小值为0,最大值为99,隐含为2。

如果减少该属性的设置值(例如,由3修改为2),将丢失所有超出新设置值的页面及它们包含的对象。

表格拆分属性(partition)

指定一个表格是否拆分为两个窗格,并且指定相对于表格左边的拆分位置。

隐含设置为0,即不拆分,如果设置为一定的数值,这个数值就是左边窗口的宽度,它不是列数,而是点数,就如同width属性一样。

这个属性也可以交互式设置,在表格左下角有个黑色的小方框,用鼠标按住它左右拉动即可设置,运行可以操作。 这个小方框叫分隔栏,可用splitbar属性设置其是否可用,隐含设置为.T.,即可用。

splitbar属性优先于partition属性。

[dvnews_page=VFP 中级教程:第五课 更多属性]

密码字符属性(passwordchar)

确定文本框中输入的字符以什么字符来代替显示。

一般用作密码的输入,设为“*”,这样输入时就会用“*”来代替所输入的字符显示,注意是显示,实际输入的内容还是没变。

其实您也可以用其它字符,不一定非用“*”不可,这样可以显得与众不同。

90

默认设置为空字符串,这时输入的字符不会被代替显示。

图片属性(picture)

指定在控件中显示的图形文件。

比如您可以用一个图形放在按钮中来代替文字,就可以将按钮的该属性设为一个图形文件,如果文件不在当前路径,那么文件名前要加上路径,另外您可以在设置时按设置框右边一个带三个点的按钮来查找所要的文件,找到后确定,文件名即路径就会加到设置框中。

文件可以是.bmp、.gif、.jpg、.ico格式。

对于复选框和选项按钮,style属性必须设置为1(图形)。

对于命令按钮,style必须设置为0(标准)。

命令按钮还可以有downpicture和disabledpicture属性,即可为按下按钮和按钮不可用时设置一个不同的图片,如果这两个属性没有设置,那么隐含与picture一样。

应用于

复选框,命令按钮,容器对象,表单,图像,选项按钮,页面,_screen。

行数据源属性(rowsource)

指定组合框或列表框控制中值的来源。

值的来源可以是用逗号分隔的值列表、表、创建表或临时表的SQL语句、查询、数组、用逗号分隔的字段列表、文件梗概(如*.dbf)、表的字段名或菜单。

这个属性应与rowsourcetype属性配合使用,即在其中指定行源是以上哪一种类型的数据。

[dvnews_page=VFP 中级教程:第五课 更多属性]

滚动条属性(scrollbars)

指定编辑框、表格所具有的滚动条类型。

对于表格的设置有:

0 无滚动条(默认值)

1 只有水平滚动条

2 只有垂直滚动条

3 水平滚动条和垂直滚动条都有

当表格的宽度能容纳所有的字段时,就可以不要水平滚动条,这样可以多显示一条记录。

91

对编辑框的设置有:

0 无滚动条

2 有垂直滚动条

对象样式属性(style)

指定控件的样式。

对于不同对象有不同的设置,比如组合框,设置有:

0 下拉组合框,包括一个下拉列表和一个编辑区。用户可以从列表中选择或在编辑区中输入字符。也就是一般我们所见到的样式,隐含也是这种方式。

2 下拉列表。用户必须从下拉列表中选择,不能输入。这样可以有效避免用户输入不合法的数据。

应用于:

复选框,组合框,命令按钮,选项按钮,文本框。

控件顺序属性(tabindex)

指定表单上控件的Tab键次序,以及表单集中表单对象的Tab键次序。

当我们将一个控件加入表单时,这个控件就有一个Tab键次序值,它是按照控件加入的顺序来设置的。

如果我们需要可重新设置,当然您可以直接将数值输入这个属性,另外可以交互式的设置,方法在初级教程第七课“编程中的一些技巧”中“设置Tab键顺序”有详细讲解。

注意:如果改变了一个控件的Tab键次序,可能其它控件也要做相应设置,否则在运行时可能产生意料不到的结果。 应用于几乎所有对象。

[dvnews_page=VFP 中级教程:第五课 更多属性]

窗口状态属性(windowstate)

指定表单窗口在运行时是否最大化或最小。

设置有:

0 正常

1 最小化

2 最大化

92

应用于:

表单,_screen

窗口形式属性(windowtype)

指定表单或表单集对象的动作。

对于表单的设置有:

0 无模式。当表单处于活动时,其它对象,如表单、菜单等也可以使用。

1 模式。当表单活动时,除了当前表单,其它对象都不能活动,也就是不可以使用。

VFP 中级教程:第六课 更多事件 激活(Activate)

释放(Destroy)

按键(KeyPress)

滚动(scrolled)

到达(when) 行列变换后(AfterRowColChange) 获得焦点(GotFocus) 鼠标按键(MouseDown) 时间到(timer) 删除(Deleted) 改变值(InteractiveChange) 改变尺寸(Resize) 合法校验(valid)

激活事件(Activate)

当激活表单、表单集或页对象时发生。

本事件与init事件不同,init是在表单创建时发生,对于一个表单它只发生一次,而本事件是在表单成为当前使用的表单时发生,一般活动的表单其标题栏是蓝色的,而不活动的是灰色的,当用鼠标单击一个不活动的表单时就会激活一个表单,对于一个表单此事件可多次发生。

[dvnews_page=VFP 中级教程:第六课 更多事件]

行列变换后事件(AfterRowColChange)

当用户将光标移到表格的另一行或列时发生。

这个事件会返回一个参数(ncollndex),该参数为最新选择的行号或列号。

我们可以利用该事件显示一个记录的详细内容,比如有一个数据表有很多记录,用表格不能将所有记录在一屏显示出来,要移来移去才能看全,可以在表格中显示其主要的几个字段,然后用另外一些文本框显示其详细内容,每当表格的

aferrowcolchange事件发生时就将其它的文本框刷新,这样当记录指针移到一个新记录时,文本框中的数据也会相应变化。 另外与此类似的还有一个beforerowcolchange事件,即在用户将光标移到表格的另一行或列之前发生。

删除事件(Deleted)

当用户在表格的记录上做删除标记、清除删除标记,或者执行delete命令时,此事件发生。

93

这个事件会返回一个参数(nrecno),为被删除的行记录号。

[dvnews_page=VFP 中级教程:第六课 更多事件]

释放事件(Destroy)

当释放一个对象时发生。比如用thisform.release关闭一个表单时。

应用于几乎所有对象。

获得焦点事件(GotFocus)

当对象接收到焦点时发生。

该事件会返回一个参数(nindex),该参数用以唯一标识控件数组中的某个控件。

应用于所有控件。

另有一事件为lostfocus,即当对象失去焦点时发生。

[dvnews_page=VFP 中级教程:第六课 更多事件]

改变值事件(InteractiveChange)

当用键盘或鼠标改变控件的值时发生。

在每次交互地更改对象时,都要发生该事件。例如,当用户在文本框中键入字符时,每一次击键都会触发该事件。 该事件会返回一个参数(nindex),对于控件数组中的控件,指定唯一标识号。

应用于:

复选框,组合框,命令组,编辑框,列表框,选项组,微调,文本框。

按键事件(KeyPress)

当用户按下并释放某个键时发生。

该事件会返回两个参数,nkeycode用以标识被按下的键值,其键与inkey()函数的返回值相同。nshiftaltctrl标识所按下的控制键(shift、ctrl、alt),其返回值分别为:

shift

ctrl

alt 1 2 4

根据这两个值便可知道所按下的是什么键,并可根据所按下的键执行相应的程序。

如果该控件是属于一控件数组中的一个控件,还会返回一个nindex,标识其为数据中的第几个控件。

94

[dvnews_page=VFP 中级教程:第六课 更多事件]

鼠标按键事件(MouseDown)

当用户按下一个鼠标键时发生。

该事件返回5个参数:

(1)nindex:标识控件数组中的一个控件,仅当控件是控件数组的一部分时,才传送此参数;

(2)nbutton:标识按的哪个键:1(左),2(右),4(中);

(3)nshift:标识控制键(shift, ctrl, alt)的状态

shift 1

ctrl 2

alt 4

(4)nxcoord:存放鼠标指针的横坐标

(5)nycoord:存放鼠标指针的纵坐标

与click事件不同,可以用此事件区别左、中、右键,还可以识是否有控制键与鼠标键组合。 应用于所有对象。

另外有以下一些相关事件:

mousemove:鼠标移动时。

mouseup:释放鼠标键时。

mousewheel:滚动鼠标轮时,有一种鼠标上面有一个可滚动的轮子。

改变尺寸事件(Resize)

当调整对象大小时发生。

该事件会返回一个参数用于唯一标识控制数组中的控制。

我们可以利用此事件调整表单中对象的位置,使得表单大小调整时都可保持其中控件排列美观。 应用于:

列,容器,表单,表格,页框。

[dvnews_page=VFP 中级教程:第六课 更多事件]

滚动事件(scrolled)

95

在表格中移动滚动条时发生。

该事件将返回两个参数:

nindex:唯一标识控制数组中的某个控件。

ndirection:指定用户如何滚动表格控制中的内容。可能的参数有: 0

1 2

3

4

5

6

7 上箭头 下箭头 单击垂直滚动条滚动块上面的区域 单击垂直滚动条滚动块下面的区域 左箭头 右箭头 单击水平滚动条滚动块左边的区域 单击水平滚动条滚动块右边的区域

如果在程序代码中调用doscroll方法,也发生此事件。

应该避免在scrolled事件中制造等待状态(例如,wait window),因为当滚动表格时会出现屏幕刷新,如果有等待可能会出问题。

时间到事件(timer)

当计时器的计时时间到时此事件发生。也就是经过了interval属性中指定的毫秒数时。

该事件会返回一个参数nindex,唯一标识控件数组中的一个控件。

[dvnews_page=VFP 中级教程:第六课 更多事件]

合法校验事件(valid)

在控件失去焦点之前发生。

有点象lostfocus,但不同的是该事件要求其中的程序最后要返回一个数据,可以是逻辑型的,如果返回的是“真”,则控件失去焦点,否则控件不会失去焦点。

这可以用来校验输入的值是否合法,如不合法,则光标不离开,直到输入合法值为止。例如如下程序:

if this.value<18

wait window '输入值必须大于等于18' nowait

return .f.

else

return .t.

endif

96

该程序的作用是,当控件(文本框、微调等)中输入的数值小于18时,光标不会离开,并提示输入不正确,如大于等于18则可以离开。

也可返回数值,对应于以下情况:

?

?

? 若返回0,则控件不失去焦点,类似于上面的“假”; 若返回正值,则该值指定焦点向前移动的控件数。例如返回2,则焦点下移两个控件,而并非按正常跳到下一个控件; 若返回负值,则该值指定焦点向后移动的控件数。例如返回-1,则焦点上移一个控件,也就是回到进入当前控件之

前的哪个控件。

应用这一点可以实现这样的功能,当输入某一值时,下一个控件不用输入了,而直接跳到后面的某个指定的控件上,至于跳到哪个就用返回的数值指定喽,或者输入了某个特殊值时回到上面某个控件重新输入。

应用于大多数控件。

到达事件(when)

在控件接收焦点之前此事件发生。

该事件会返回一个参数(nindex),唯一地标识控件数组中的某个控件。

此事件有点象gotfocus,但该事件中的代码必须返回一个逻辑值,如果是“真”,该控件可以接收焦点,否则不能收到焦点。

可以利用该事件使得在某些条件下一个控件不能接收焦点,比如这种情况,前一个控件输入了“不知道”,那么下一个控件就不需要输入了。

对于列表框控件,每当用户单击列表中的项或用箭头键移动使焦点在项之间移动时,when事件发生。

应用于所有控件。

第七课 更多方法

第七课 更多方法 激活单元格(ActivateCell)

移动(Move)

重置时间(Reset) 滚动(DoScroll) 显示(Print) 显出(Show) 隐藏(Hide) 置点(PSet) 全部设置(SetAll)

激活单元格方法(ActivateCell)

将光标移到表格的某个单元格。

语法:

表格.ActivateCell(行号,列号)

97

参数:

行号、列号:用于指定所要激活单元格所在的行和列。

备注:

有时为了做相对移动,即将光标移到当前所在行的下一行,或当前所在列的下一列,那么就要知道当前行或列的位置,用表格的activecolumn和activerow可获得。

滚动方法(DoScroll)

使表格做滚动操作。

语法:

表格.DoScroll(滚动方向)

参数:

滚动就是用于确定滚动的方向(废话),可做的设置如下:

参数 方向

0 上一行

1 下一行

2 上一页

3 下一页

4 左移一列

5 右移一列

6 左移一页

7 右移一页

备注:

Scrolled事件于DoScroll方法之后发生。

[dvnews_page=VFP 中级教程:第七课 更多方法]

隐藏方法(Hide)

将表单、表单集或工具栏隐藏起来,也就是将它们的visible属性设为.f.。

语法:

对象.Hide

备注:

_SCREEN也有hide方法,_SCREEN代表VFP主窗口,即用此方法可将主窗口隐藏。

98

移动方法(Move)

移动一个对象。

语法:

对象.Move (左边界[,上边界[,宽度[,高度]]])

参数:

左边界:指定对象新位置的左边界位置。

上边界:指定对象新位置的上边界位置。

宽度:指定对象新的宽度。

高度:指定对象新的高度。

备注:

左边界必须有,其它参数可以省略。但如果您指定了某个参数这个参数前面的所有参数都必须指定,比如,如果有宽度参数,那么就必须有上边界参数。

[dvnews_page=VFP 中级教程:第七课 更多方法]

显示方法(Print)

在表单上显示字符。

语法:

表单.Print[(文本字符)]

参数:

文本字符:将要在表单上显示的字符。假如省略,将显示一个空行。

备注:

文本将从表单的左上角开始显示,下一次会接着上一次显示,到右边界会自动换行

置点方法(PSet)

在表单上画一个点。

语法:

表单.PSet([横座标,纵座标])

99

参数:

横座标:相对于表单左边的距离。

纵座标:相对于表单上边的距离。

备注:

点的大小取决于表单drawwidth属性的设置,而点的显示方式取决于drawmode和drawstyle的属性设置。

[dvnews_page=VFP 中级教程:第七课 更多方法]

重置时间方法(Reset)

将计时器的计时时间重置为0,也就是说让计时器重新开始计时。

语法:

计时器.reset

显出方法(Show)

使表单显示出来,并可指定它的windowtype。

实际上就是将表单的visible属性设为.T.。

语法:

表单.show([表单模式])

参数:

表单模式:指定windowtype属性,如果省略,则以表单自己的windowtype属性为准。

全部设置方法(SetAll)

将一个容器对象(如表单)中的所有控件设置同一个属性。

语法:

容器.SetAll(属性名,值[,类])

参数:

容器:指定哪个容器中的控件要做设置。

属性名:指定控件的哪个属性要做设置。

值:指定属性要设为何值。

100

类:指定一个类名,以便对所有基于此类的控件做设置,不是基于此类的就不设置。

备注:

我们前面讲过可对dynamicbackcolor做设置,以便当记录做了删除标记时以灰色背景显示,但必须为表格每一列的该属性做设置,如果这个表格有几十列那可就麻烦了,但将以下代码写入表格所在表单的init事件中,便可轻松解决此问题: this.grid1.setall;

("dynamicbackcolor","iif(delete(),rgb(192,192,192),rgb(255,255,255))","column")

VFP 中级教程:第八课 优化菜单

第八课 优化菜单

一、跳过

我们在使用WINxx软件时,经常可以看到它们的某些菜单项在某种情况下被屏蔽掉(字成为灰色),这时这些菜单项就不可用,这也叫跳过。

这是一个非常有用的功能,比如当一个数据表没有打开时,就不能执行查询的功能,这时如能将“查询”菜单项屏蔽岂不很酷,那么VFP能否做到这一点呢?能!而且很容易,方法是:

1. 进入菜单设计器;

2. 找到需要屏蔽的菜单项;

3. 按该项后的选项按钮,参见图

1;

4. 按后出现图2,在“跳过”文本框中输入一个表达式,其值以后会是逻辑值,当此值为“真”时该菜单项就被屏蔽,

比如图3;

101

5. 输入完后,确定,会看到选项按钮中有一个钩,表示选项中有内容,如图

4;

6. 将所有菜单项的跳过逻辑表达式设好,重新生成新的菜单程序,关闭菜单设计器就行了。

当程序运行时,在启动菜单之前必须先为所有的跳过变量赋值,否则菜单启动后会发生变量找不到的错误,因为菜单程序会根据这些变量值的“真”、“假”来确定这些菜单项是否可用。

如果您希望菜单一启动某个菜单项就不可用,那么在菜单启动前就给相应的变量赋值“.T.”,否则为“.F.”。 在程序中什么时候要想让这个菜单项不可用,只需要将变量设为“真”即可,又想让它可用,再设为“假”。

比如我们可以给前面的人事档案程序的查询等菜单项设一跳过表达式:

.not. dkda

这里为什么要加个“.not.”呢?因为在菜单的初始化代码中,该变量是设为“假”的,因为这时档案还没有打开,菜单一启动,查询功能应该不可以用,我们前面说过,当跳过表达式为“真”时不可用,这时dkda变量为“假”,我们又想“查询”不可用,于是就取其反面,加上一个“.not.”,整个表达式的值就为真了,反之,当dkda为“真”时,即档案已打开,这时表达式就为假,因此“查询”就可以用了。

初学者看上面这段话,可能会有点晕,多看两遍。

102

二、给菜单加说明

在许多的软件中我们都会看,当鼠标指到某个菜单项时,在下面的状态栏上可看到该菜单项的详细说明,这个功能是怎么实现的呢,方法如下:

1. 进入菜单设计器;

2. 找到要加说明的菜单项;

3. 按选项按钮;

4. 在“信息”一栏输入相应的说明,注意,字符要加引号,如图

5;

5. 确定;

6. 重新生成。

三、给菜单加分隔线

为了使菜单易于查看和调用,常常需要在菜单中加入分隔线,如图

6,下面讲做分隔线的方法:

1. 进入菜单设计器;

103

2. 找到要做分隔线的地方,在菜单名称中输入“-”,其它都不用变,如图7。如果需要做分隔线的地方已经有菜单

项了,那么就按“插入”插入一空栏再输入;

3. 重新生成即可。

四、增加热键

如果要给某个菜单项增加热键,方法如下:

1. 进入菜单设计器;

2. 找到要加说明的菜单项;

3. 按选项按钮;

4. 用鼠标点一下“快捷方式”的“键标签”;

5. 按下您所需要的键,比如“F2”或者“Ctrl+A”,按了之后出现如图

8;

6. 这时在键说明中也会出现与键标签中一样的内容,键说明是用于显示在菜单上以提示菜单项的热键是什么,如图9。

当然您也可以不要隐含的这个说明,而输入自己的说明,比如“^A”

7. 确定及重新生成,再去看看菜单有什么变化,是不是更酷了。

五、插入栏

很多朋友一定想在自己的软件中做出象VFP中的菜单中的一些功能,比如剪切、复制、粘贴等,这些功能如果自己编可就太麻烦,如能直接调用VFP的相应功能那就太好了,完全可以做到,方法是:

104

1. 进入菜单设计器;

2. 进入一个子菜单,注意这些功能是不能加在主菜单上的;

3. 将光标放在适当位置,按“插入栏”按钮,如图

10;

4. 这时会弹出一个插入栏选择框,如图

11,找到您所要插入的功能,然后按“插入”按钮;

5. 插入后的效果如图

12;

6. 剩下的事情不用我再说了吧。

这样您的菜单也就有了VFP的相应功能了,而且还可以使用它的快捷键,如ctrl+x、ctrl+c等等。 105

不能在您的菜单中禁止系统菜单,也就是不能用这个命令:

set sysmenu off

注 意 否则系统菜单中的功能就不能用了。如要不出现系统菜单应使用:

set sysmenu to

菜单设计器会自动在生成的程序中加入这一句,一般您不去动生成的程序(.mpr)就不会有问题。

六、增加快捷键

这里所说的快捷键与上面所说的热键有什么不同呢?说起来要一大堆话,相信您看了图13就明白了,制作的方法为:

1. 进入菜单设计器(老生常谈);

2. 找到所要的菜单项,在菜单名称后加一个括号,括号中加一个您想要的字母或数字,在该字符前面加上“<”,如

图14,如果是英文菜单,直接在菜单名称中找一个字符就行了。

3. 好了。 按同样办法可以给一些控件加快捷键,比如要给一个按钮加上快捷键,用同样方法设置它的caption属性就

行了,设好后如图15,运行时按“Alt+Q”即可调用“退出”按钮。

多学一招

七、制作快捷菜单(弹出式菜单)

106

我们都知道在WIN95/98中可以按右键弹出一个快捷菜单,那么在VFP中怎样制作快捷菜单呢?方法如下:

1. 在项目管理器的“其它”中选择“菜单”,按“新建”;

2. 出现如图

16,按“快捷菜单”;

3. 出现与制作顶部下拉菜单类似的菜单设计器,这时可按制作下拉菜单同样的方法输入所需要的菜单项等内容;

4. 制作完后同样需要“保存”、输入文件名、“生成”;

5. 调用的方法也与下拉菜单一样,即在需要的地方输入命令:do 快捷菜单名.mpr 。比如您可以在一表单或某个控件

的“rightclick”事件输入这个命令,那么当您在表单或这个控件上按鼠标右键时就会弹出这个菜单。 注 意 控件的rightclick事件优先于表单的

rightclick事件,也就是说您把命令放在了表单的rightclick事件中,

而没有放在控件中时,那么在这个控件上按右键是不会有作用的。

八、给菜单项作标记

如果想给菜单的某个选项打勾(作标记),如图17,怎么办呢?用如下命令:

set mark of bar 1 of '系统' to .t.

意思是给主菜单上的“系统”下的子菜单中的第 1 个菜单项作上标记,如要去除标记,则:

set mark of bar 1 of '系统' to .f.

要给整个子菜单的所有选项作标记:

set mark of popup '系统' to .t.

想要知道某个菜单项是否作了标记,用如下函数:

MRKBAR('系统',1)

如该函数返回 .T.,则代表“系统”主菜单下的第 1 个子菜单项作了标记,如是 .F.,则表明没有作。

注意:

107

如果菜单里面加了分隔线,因为分隔线也要算一个菜单项,因此分隔线下的菜单项的序号要增加,比如图17中的“退出”

要加标记,就应该是:

set mark of bar 5 of '系统' to .t.

而不是:

set mark of bar 4 of '系统' to .t.

另外,如果主菜单加了快捷键,那么它的名称会改变,比如“系统”可能变成“a系统s”,这要打开菜单的源代码才能看到,可用如下命令打开来看:

modify command 菜单文件名.mpr

您会看到其中有类似下面这样的程序:

ON PAD _rxz17gfg2 OF _MSYSMENU ACTIVATE POPUP a系统s

ON PAD _rxz17gfgm OF _MSYSMENU ACTIVATE POPUP ab进货j

ON PAD _rxz17gfgo OF _MSYSMENU ACTIVATE POPUP 销售x

ON PAD _rxz17gfgp OF _MSYSMENU ACTIVATE POPUP 代销d

ON PAD _rxz17gfgr OF _MSYSMENU ACTIVATE POPUP 库存k

ON PAD _rxz17gfgt OF _MSYSMENU ACTIVATE POPUP 维护w

POPUP 后面所跟的就是各子菜单的真实名称,在作标记是必须用这个名称。

VFP 中级教程:第九课 报表及标签

第九课 报表及标签

一、分类及总计报表的设计

1. 将要分类的字段索引,比如您的报表要按“部门”分类,那么相应表中“部门”这个字段必须索引;

2. 在项目管理器中选中“报表”;

3. 按“新建”;

4. 按“报表向导”;

5. 选择“分组/总计报表向导”;

6. 选择所需要的字段,比如选择“编号”、“姓名”、“部门”、“职务”、“基本工资”,下一步;

7. 选择分组依据,即按哪个字段分组,最多可以选择三个分组字段,必须选了第一个才能先第二个,这里我们以“部

门”做为第一分组依据,以“职务”做为第二个分组依据,即在同一部门中又按职务分小组,另外可以选择是否需 108

要“总计”或“小计”,总计是指第一分组的合计,小计是指第二及第三分组的合计,选择好后,如图1,下一步;

这里还可以对分组依据的字段进行一些修改,按依据字段后面的“修改”按钮,会弹出一个对话窗:

1. 如果是分组字段是字符型的,可选择“整个字段”或“字段的前几个”字符做分组依据,一

般情况下当然都是整个字段,那也就不用修改了,在某些情况下可能需要按前几个字符来分组,比如

商品分类,可能会有A-1、A-2、B-1...,如果我们希望按分类分组,而又不希望分得太细,即A-1、

窍门

2. A-2等都算A类,分为一组,那么就可选起始第1个字符做为分组依据,否则A-1和A-2就会分在不同的两个组。 如果是数值型的字段,可按一定数量级间隔分组,比如按工资分组,如果不修改这个分组依

据就会110是一组、120也是一组...,如此可能不知会有多少组,只要数值不一样就是一组,这比不

分组可能还糟,为此我们可以选“100s”做为分组依据,即每隔100为一组,200和299都属一组。

3. 如果是日期型,可选择按“星期”、“月”、“年”来分组。

8. 选择在组中排序的字段,比如选择“编号”,最多可选三个字段,下一步;

9. 选择报表样式,可选择“帐务式”,下一步;

10. 输入报表标题,比如“部门工资统计表”,按“完成”;

11. 输入报表文件名,比如“bmgztj”,当然还要选好适当的目录,然后“确定”

109

在项目管理器中预览报表可看到类似图2

情况。 注意 在VFP6.0中分组/总计报表和一般报表已合并了,您选择分组依据就是分组/总计报表,不选择就是一般报表。

另外,不光可以求合计,还可以求平均值、最大值、最小值等等。

二、报表的调整

有时用报表向导做出的报表不能完全满足要求,需要手工对做好的报表进行调整。

首先需要在项目管理器中找到所要的报表,选择它,然后再按“修改”,进入报表设计器,如图3,在这里我们一般可以看到四个带区:

110

1. 标题:如图3中的“人员花名册”,该带区的内容显示在报表第一页的开头。

2. 页标头:该带区的内容显示在每页的开头。

3. 细节:为报表的主要内容,紧接在页标头下打印,虽然我们在这里看到只有一行,但打印时却不一定只有一行,而

是有多少条记录符合打印的条件就会有多少行,这里的一般都是变量名或字段名,也叫“域控件”(很难理解的名称),它们都有个方框框住,打印时会换为相应的内容。

4. 页注脚:打印在每页的底部,一般用于打印页号等内容,在这里一般会看见一个域控件:

“页”+alltrim(str(_pageno))

这就是由报表向导自动产生的页号。

在这里我们可以做如下的调整:

1. 调整带区宽度。将鼠标放在带区上,鼠标光标会变为一个上下的箭头,这时按住鼠标左键可上下调整该带区的宽度。

比如,一般向导所做的报表,其标题、页标头和页注脚都很窄,这样打出来的报表就会上、下将纸顶得很满,因此应把这些带区调高些,如图

4。

2. 移动控件。报表上的所有东西(标签、域控件、线条、矩形等)都是可以移动,如果您觉得位置不满意,可按您自

己的喜好把它们移来移去,就象设计表单一样。比如上面所说的为了不让报表把纸顶的太满,可将带区调高些,对页注脚调高就行了,但对标题和页标头,带区调高后还应将里面的内容往下移,才不会使上面顶的太满。另外页号在左边,这不符合我们的习惯,可将其移到右边。 除了将控件在带区中移动,还可以从一个带区移到另一个带区。西方的习惯也许是只在第一页打印报表的总

窍门 标题,也就是那个标题带区,但我们中国人一般喜欢在每页上都有相同的标题(可能老外比较小气,那么一

点纸也要节约),也就是说我们不需要标题,而只要页标头就行了,为此我们可以将页标头区加宽,将标题

中的内容移到页标头中,不需要的线条可以删掉,标题区的内容全部去掉后,将其高度调到最窄。

3. 修改控件。首先调出报表工具栏,在菜单的“显示/报表控件工具栏”,如要修改标签,比如标题,按下工具栏上

的标签工具,再到要修改的标签上按一下,就可以对其进行修改了。

如要修改域控件,双击所要修改的域,会出现一个窗口,如图5,在表达式中作需要的修改,例如,向导生成的页号也不符合我们习惯,什么“页1”,这简直的就是老外说中国话,那么您就可以在表达式框中将其改为: alltrim(str(_pageno))+"页"

"第"+alltrim(str(_pageno))+"页"

再要不,什么都不要,就是一个

alltrim(str(_pageno))

111

反正您爱怎么改就怎么改,您就是把页号删了人家也管不着。

三、标签的制作

什么是标签呢?比如我们要给人事档案中的每个人打一个小纸条,上面标明他的部门、姓名、职务等,然后贴在他的办公桌上,这就是标签,应用VFP的标签的打印功能就能制作出来。标签可以说是一种简易的报表,其制作的方法与报表非常类似,下面我们就在人事档案软件中制作上面所说的标签。

1. 首先将所要用来做标签的数据表所在的数据库打开,可在项目管理器中选择“数据/数据库/rsgl/打开”,如果不

是数据库表,而是自由表则不需要做这一步;

2. 在项目管理器选择“文档”;

3. 在文档中选择“标签”;

4. 按“新建”,出现如图

6;

112

5. 按“标签向导”,出现如图

7;

6. 在“数据库和表”选择框中选择rsgl数据库,再在下面的列表框中选择rsgl表。如果是自由表,就按那个带三个

点(...)的按钮,选择所要用来做标签的表;

7. 按下一步,出现如图8,选择“公制”符合国人习惯,然后选择符合您需要的标签,这里我们选择“Avery L7160

38.10mm x 63.50mm 3列”,3

列的意思是在每一行上可以并排打三个标签;

8. 按下一步,出现如图9,选择所要的字段然后按按钮,如要换行,按按钮,还可按“.”、“,”等

按钮在其中插入相应的符号和空格等,如果您想在标签中放入一些自己的文字,如公司名等,可在文本中输入,然后按,如要取消一个字段,按(自己想想吧),如要取消一个空行,双击该行即可,设置好后如图10,另外在设置的过程中可在左上角的小窗口中看见效果;

113

114

9. 按下一步,出现如图

11,在这可以选择标签打印时的先后顺序;

10. 按下一步,出现如图12,这时可用预览看一下效果,如图13,如有问题,可回到前面修改,不过这里不要对美观

要求太高,这里要想做得很美观比较难,我们可以在做好保存后再把它调出来修改,这样会比较方便些。如果没有问题,按完成,输入标签文件名将做好的标签保存起来。

115

做好后可在项目管理器中看到所做的标签文件,按预览便可看到所做好的标签,按预览工具栏上的打印机便可将标签打印出来。

也可使用如下命令在命令窗口和程序中将标签打印出来:

label form 标签文件名

另外可用项目管理器上的“修改”按钮,将标签打开来修改,修改的方法与报表基本一样,比如可将上面做好的标签修改成如图14,预览的效果如图15。

116

VFP 中级教程:第十课 查询与视图

第十课 查询与视图

一、查询

这里所说的查询与用locate命令查询记录不太一样,这里的查询是一个名词,它可以说是一个表的子表(或叫子集),即将一些符合某些条件的记录筛选出来形成一个子表,而且此子表可象一个表一样保存起来以供随时调用。

一定会有人要问了:这样把子表保存起来,要是主表的内容改变了,那么子表不是与主表不符了,这时再看子表不是就是错的?

其实它并不是保存的子表,而保存的是形成子表的命令,换句话说,一个查询就是一个程序,打开查询就是运行这个程序,该程序将主表中的数据按一定条件筛选出来。那么您说上面的问题还存不存在?

好了,下面我们就来看看怎样做一个查询,我们仍以人事管理软件为例,做一个专门显示电脑部门人员的查询: 117

1. 首先打开查询所需要用到数据库,注意不是表,表可以先不打开;

2. 在项目管理器中选择“数据/查询”,按“新建”,并选择“新建查询”(等您学会手工方式建立查询后,您应该

会使用查询向导了);

3. 出现图1,在其中选择所需的数据库及表,选好后按添加,即将需要的表添加到查询的数据环境中,可添加任意多

个表,如果是自由表,按“其它”选择,添加后可在查询的数据环境中看见该表,如图2,添加完后按“关闭”,得到图

3;

118

119

4. 在“字段”页中选择查询所需要的字段,按“添加”,选择好后如图

4;

5. 在“筛选”页中选择筛选的条件,选择好后如图

5;

6. 这样一个简单的查询就做好了,在查询上按右键,选择“运行查询”,便可看到查询的结果;

7. 如没有问题,“保存”、“取文件名”,然后“关闭”。

8. 在项目管理器中的“查询”下面可看到刚做好的查询名称,按“运行”即可看到查询的结果。我们前面讲过,查询

实际上是一个程序,它被存在“xxx.qpr”文件中,实际上与“xxx.prg”文件是一样的格式,可在程序中用“do xxx.qpr”来调用它。 ?

?

? 排序:您可以选择查询的结果按那个或那些字段排序,只需在“排序依据”中选择相应字段即可。 无重复记录:如果希望在查询结果中没有重复的记录,可在“杂项”中选择“无重复记录”。 显示排在前面的若干记录:一般情况下我们会要显示符合筛选条件的所有记录,但有时我们可能会只

需要看排在前面的10个记录,比如找出分数最高的前10名,就可以按分数先排序,然后在“杂项”

中将“全部”的钩去掉,在“记录个数”输入所要10。还可以显示查询结果中50%的记录,知道怎么

做吗?记住:如要选择前若干记录,必须有排序。

高级 ? 分组:如果要把一个字段中相同的内容做为了一个组,即显示一条记录,可以在分组中选择所需要的

字段,但如果只是简单的分组,就和无重复记录差不多了,一般要结合统计函数使用,即在字段中输

入“函数和表达式”,比如您可以选择“部门”字段,另外再输入一个表达式“sum(rsda.部门)”,

然后在分组中按“部门”分组,猜猜会出现什么结果。其它可用的函数还有average(), min(), max(),

count()等,不过这些函数只能在这儿用,不能在别的地方用,为什么?以后讲SQL语言时再讲。另外

还可以按条件显示分组的信息。

120

查询结果的去向 一般情况查询结果是以一个临时表的方式来存放,并用browse显示出来。不过您还可以选择其它方

式来存放结果,单击右键,选择“输出设置”,看到了吧。

二、多表查询

如果希望从多个表中获取数据来形成查询结果,可建立多表查询,比如人事管理的职务库(zwzd)中有个职务工资字段,而rsda中没有,我们就可以做个多表查询来看每个人的职务工资:

1. 将所需要的表添加到数据环境。当您添加新表时,系统会让您建立与新表与原有表的联接,添加后如图6,可以看

到两个表的职务字段有一根线连在一起;

可在需要的时候将一个表的字段拖到另一个表的字段上形成联接。也可以双击那根线或在“联接”页中修改

联接。 另外

2. 选择各表中所需要的字段;

3. 其它的就和前面讲的差不多。 连接的方式有以下几种,各自的含义如下:

进一步 ? ?

?

? 内部连接:只返回完全满足连接条件的记录; 左外部连接:返回前一个表中的所有记录以及后一个表中匹配的记录; 右外部连接:返回后一个表中的所有记录以及前一个表中匹配的记录; 全外部连接:返回两个表中的匹配和不匹配的所有记录。

三、视图

“视图”,又是一个难以理解的名词,这都是老外创造出来的名词(view),我们的翻译也就照着字面直译。其实视图和查询差不多的,不同在哪呢?我们知道查询的数据来源于一个或几个表,这些表称之为基表,“查询”就是对基表进行“查看”,您是不能通过它来修改基表的,而视图就是这一点不同,您可以在视图上修改数据,并将修改后的数据传回给基表。

既然视图和查询是基本一样的,我们就不多讲怎样来创建视图了,创建的方法也是差不多的,主要就来讲讲它们不同的地方。 121

进入视图设计器,我们可以看到与查询设计器不同的就是多了一个“更新条件”页面(那是当然的,因为视图可更新,而查询不可更新嘛),如图

7,在这里我们要做以下一些设置:

1. 设置“发送SQL更新”,这样才使基表可更新,至于什么是SQL,以后再讲;

2. 设置每个表的“关键字段”,关键字段是用来使视图中的修改与基表中原始记录相匹配,必须设置了关键字段,该

表才可修改,设置的方法为:在作为关键字段的字段名前面的钥匙处打上钩。每个表只能有一个关键字段;

3. 设置“可修改字段”,在需要修改的字段名前面的铅笔处打上钩,一般说来关键字段应是不可修改的,但您一定要

修改也可以;

4. “SQL WHERE 子句包括”又是什么意思呢?在多用户环境中,当要修改基表时,可能会有冲突,也就是您要改的时

候,人家也要改,那么是不能两个人同时改一个数据的,当别人正在改一个数据时,您就不能改,怎样知道别人是否改了某个字段呢?这里就要为系统指定检测的方法,可以只检测“关键字段”,一般设置为“关键字段和已修改字段”。有人会问了,“关键字段和可更新字段”什么时候用呢?虽然它是可更新字段,但在实际中不是已修改字段,既然没修改就不用检测嘛。有时候会有这种情况,当一条记录中的某个字段改了,就不允许再改其它字段,这时候就有用了。

5. “使用更新”也就是更新方式,一般用“SQL UPDATE”,用“SQL DELETE 然后 INSERT”会使修改的记录跑到最后。 好了,这样一个视图就做好了。使用视图就和使用表一样,可以用USE将其打开,也可以使用其它命令象操作表一样来操作视图。 当您在使用中打开视图并作了修改时,所修改的内容在您离开当前记录时自动地进入基表(这叫记录缓冲,

关于缓冲我们以后再讲),如果您想在没有离开该记录时就更新基表,可使用如下命令(函数):

tableupdate()

重要一点 如果您想放弃对当前记录的修改,使视图回复原状,在没离开当前记录前,可使用如下命令(函数):

tablerevert()

如果已离开了当前记录,那就不行了。

VFP 中级教程:第十一课 OLE控件

第十一课 OLE控件

OLE控件又是个什么洋玩艺,先来解释一下OLE,它是英文“Object Link and Enbed”的缩写,意思是对象的链接和嵌入。 122

这里所说的对象,与我们以前课程中所讲的对象又有所不同,不是指表单、文本框之类的东西,而是指VFP以外的东西,比如一幅图片、一段声音、一个EXCEL图表、一个WORD文件等等,我知道您这时一定很兴奋:难道在VFP的程序中也能放入这些东西吗?我的软件不就有了多媒体的功能,那岂不是帅呆了。

是的,的确是可以,下面我们就告诉您怎样做。

一、通用字段

VFP的数据表有一个通用型字段,可以放各种各样其它格式的文件,比如图片、声音等,在这里就是以OLE的方式放入的。 将其它文件放入通用字段的命令是:

APPEND GENERAL 通用字段名

[FROM 文件名]

[LINK]

比如我们可以给人事档案数据表(rsda.dbf)加一个字段“照片”,将一个人的照片用扫描仪扫好,以文件名“庄稼.bmp”存放在当前目录中,打开rsda数据表,将记录指针走到“庄稼”这条记录上,然后执行如下命令就可以将庄稼的照片放在相应的记录中了:

append general 照片 from 庄稼.bmp

具体到程序中可以在增添和修改人员的表单中增加一个文本框,在其中输入照片文件名,在该文本框的lostfocus事件中加入如下代码: zpwjm=alltrim(this.value) &&将文本框的内容放入一个变量

if file(zpwjm) &&如果该文件存在

append general 照片 from &zpwjm &&将文件放入照片字段

else &&否则

messagebox("文件名不存在!",64,"注意") &&给出提示

endif

如用删除通用字段中的内容,可用如下语句:

append general 通用字段名 另外还可以交互式地将文件放入通用字段:

多学一招 1. 在浏览状态,将光标放在通用字段上,按“ctrl+pagedown”键,或者双击该字段,出现一表单,如图

1;

123

2. 调用菜单上的“编辑/插入对象”,如图2;

3. 在出现的对话框中选择“从文件创建”;

4. 输入文件名并确定,即可在显示通用字段的表单中看到插入的文件;

5. 关闭该表单。

如要修改通用字段中的内容,可在第2步双击所看到的对象。

如要删除通用字段中的内容,可在第2步调用“编辑/清除”。

在第3步,您也可以选择“新建”,然后选择对象类型,比如WORD,然后您就可以打入一篇文章,之后存放在

这个通用字段中。

通用字段中如果有了内容,在浏览时可看到字段中显示“Gen”,字母以大写开头,否则是以小写开头。

二、链接和嵌入

链接和嵌入是两个概念,也就是说我们将VFP之外的对象放入VFP时,既可采用链接的方式,也可采用嵌入的方式,它们有什么不同呢?

124

嵌入:是指将对象真正放入了VFP,或者说是复制了一份放进来。好处是如果它的数据源丢失了,它仍然还在;缺点是数据源发生了变化,它不会随之而变化,比如照片换了,数据表中的照片不会自动更换,除非调用命令重新加入才行。另一个缺点是会使表变得很庞大。

链接:没有将对象真正放入VFP,而只是放了个地址进来,每次要看时,就到这个地址去取。显然,其优缺点与嵌入正好相反。

在第一点中我们采用的都是嵌入,如要链接,append general跟“link”子句;交互式时,如果是从文件创建则在对话框中选择“链接”,如果是新建,因为直接是在通用字段中建立这个文件,不存在什么数据源,所以肯定是嵌入。

三、OLE绑定型控件

所谓“绑定”,就是说该控件是与通用字段绑在一起的(好象看不出通用字段的意思嘛),也就是说它的数据源是一个通用字段的内容,我们可以将一个OLE绑定型控件放在表单以显示通用字段的内容,方法为:

1. 进入表单设计器;

2. 调OLE绑定型控件工具;

3. 在表单上适当地方画出控件,就象设置一个image控件一样,如图

3;

4. 设置其controlsource属性为一通用字段的字段名,比如“rsda.照片”,如图

4。

当运行表单后,我们就能看到所要的效果了,并且当表中记录指针移动时,表单中的照片也会随之变化。

四、OLE容器型控件

125

类似于在通用字段中放入对象,当调用工具栏上的OLE

容器型控件工具,在表单上拉出该控件后,会出现一个与在通用字段中插入对象类似的对话框,插入的方法也与在通用字段中一样,也就是直接将对象插入到表单中,而不象绑定型控件到通用字段中去取。

与在通用字段中插入对象时的对话框有一点不同,就是多了一个“插入控件”,如图5,这是用于插入ActiveX控件的,关于ActiveX控件要到高级教程再讲了,这一课就此Bye-Bye。

五、重要方法程序

OLE控件有一个重要的方法程序,就是doverb,该方法程序可以调用插入对象的宿主程序(如一般在电脑中BMP格式文件的宿主程序是画笔),对对象进行编辑、运行等。

语法:

Object.DoVerb[动作参数]

动作参数

指示OLE对象作怎样的动作。如果省略参数,将执行宿主程序并调用该对象,比如一个BMP图像,就调用画笔程序并将该图像打开,类似在表单上双击该OLE对象。

以下为标准的动作参数: 参数值

-1

-2

-3

-4

-5

-6 动作 执行隐含动作。 打开对象供编辑。如果程序支持在现场激活,则对象可在表单中打开,如WODR、EXCEL等都可以。 在另一个窗口中打开对象。 如果是嵌入的对象,启动对象,并将宿主程序隐藏。这非常有用,比如表单上嵌入的是一个声音文件,可以用此参数启动声音播放器(如录音机),将声音播放出来,但录音机并不显示出来。 假如对象支持现场激活,那么对象将在现场激活,并显示用户界面。如果对象不支持现场激活,将产生错误。假如将焦点放在了OLE控件上,将打开一个窗口使对象可编辑。如果对象不支持单击激活,则将产生错误。 如果对象已进入编辑状态,可用此参数放弃所做的修改。

下面举例说明怎么使用这个方法:

您可以在表单上放入一个绑定型OLE控件,数据源为表中的一个通用字段,字段的内容为声音,比如可以是人事档案中人员自我介绍的讲话录音,加在表单上放入一按钮,按钮中写入如下语句:

thisoform.oleboundcontrol1.doverb(-3)

126

这样按下此按钮就可以将当前记录中的声音放出来,并且不会显示出播放声音的软件。 VFP 中级教程:第十三课 网络版软件初步

第十三课 网络版软件初步

一、原则

网络软件的最重要原则就是不能两个人同时修改一个数据。否则就会造成冲突,不知以谁的修改为准。

二、普通的解决办法

最普通的办法是在要修改数据时将数据表以独占方式打开,所谓“独占”就是使得数据只能自己使用,而别人不能使用,既不能修改,也不能查看。独占打开表的命令是在 use 命令中加 exclusive 子句,比如:

use rsda exclusive

这样就不会出现两个人同时修改一个数据的情况了。

三、共享数据

如果您只是查看数据,而不进行修改,那么您应该以共享方式打开表,否则别人也就没办法查看数据了,如果大家都是查看,不会有任何的冲突发生,即使查看的是同一个数据。

共享方式打开表的命令是 use ... share ,比如:

use rsda share

对于数据库也是一样的处理:

open database ... share

独占打开则是:

open database ... exclusive

四、友好的提示

如果您以独占方式打开了表,别人无论用独占或共享方式打开表都会出错,错误是“不能存取文件”,错误代码是1705;另外如果有人以共享方式打开了表,其它人试图以独占方式再打开,也会出现同样的错误。

如果在程序中出现这样的错误,可能使操作者莫名其秒,如果能给出比较友好的提示就好了,方法是用 on error 语句捕捉错误,一旦捕捉到错误即给出一个友好的提示,比如在需要共享打开数据时:

127

on error err=error()

*上面这条就是捕捉错误的陷井,如在下面任何地方出现错误就将错误代码存入err变量,并不显示错误。

use rsda share &&如果这时出现错误,错误代码就会存入err,并且表不会打开,也不会出现错误提示。

if err=1705 &&如果错误代码是1705

*给出自己的提示

messagebox('数据正在被其它人修改,您暂时不能使用,请稍后再试。',16,'注意')

endif on error &&取消捕捉错误

或者在需要独占打开数据时:

on error err=error()

use rsda exclusive &&以独占方式打开表,如果有人已打开表则会出错,不论是独占还是共享。

if err=1705

messagebox('数据正在被其它人使用,您暂时不能进行修改,请稍后再试。',16,'注意')

*因为操作人员可能并不懂什么叫独占,而此时以独占方式打开表,肯定是要修改,故告诉他不能修改。

endif

on error

五、提高网络效率

学了以上的知识,您就可以编一个简单的网络软件了,但是这种软件的网络效率很低,为什么这么说呢?因为一旦有人独占数据,其它人就都不能看了,假如有几十个人要看这个数据,而该数据又被某人独占,可想而知其工作效率的低下。另外一个缺点就是可能始终都有人在以共享方式打开表,您根本没有机会对数据进行修改。

为了解决效率问题,因此引进“锁”的概念,锁的意思就是将数据锁上后,别人不能修改但可以查看,而且在别人查看时也可以对数据进行加锁修改,这样就解决了上面所说的问题。

给整个表加锁的命令为一个函数:

FLOCK([工作区|别名])

即给指定工作区或别名的表加锁,如果省略工作区或别名,则给当前工作区的表加锁。

该函数的返回值为逻辑值,如果返回“真”,代表加锁成功,如果“假”,则不成功。假如加锁不成功,表明有其它人已经锁住了该文件,目前您不能对其进行加锁,也就是不能修改其中的内容,但您可以查看。

讲到加锁就不能不讲一下这个命令:

SET REPROCESS TO 次数 [SECOND] | TO AUTOMATIC

这个命令的意思是设置尝试加锁的次数或时间。如果 set reporcess to 1,那么当执行 flock() 时,如果不成功立即就返回 .f. ,如果设置为5,那么系统会连续尝试锁5次,如果5次都不成功加锁函数才返回 .f. ,如果跟上 SECOND 子句,则会连续加锁5秒钟。

隐含设置为 set reporcess to 0,系统将会无限次加锁,至到成功为止,并返回 .t. ,期间可按 ESC 键中止尝试,则返回 .f. ,set reporcess to automatic 也是同样的意思。

如果设置 set reporcess to -1 ,那么不但是无限次加锁尝试,而且不能按 ESC 中止,这一般都是不会用到的。 128

在程序中我们一般只需要试1次就行了,故一般在程序开头设置 set reporcess to 1。

一个典型的加锁程序如下: select rsda

jscg=flock()

if jscg

*进入数据的修改处理

else

messagebox('其它人正在修改数据,您不能修改,请稍后再试。',16,'注意')

endif

数据修改完后应尽快将锁解除,以便他人能够进行数据修改,解锁的命令为:

UNLOCK 那么是不是说有了锁,独占打开表的方式就没有用了?不是的,有些操作必须要独占整个表,比如这些命令:

注意 INSERT, INSERT BLANK, MODIFY STRUCTURE, PACK, REINDEX, and ZAP 。因为它们会使表中的所有记录发生改

变或者使表的结构发生改变,那么此时其它人是不能查看数据的。当您的表不是以独占方式打开,系统将会拒绝

执行这些命令。

六、进一步提高效率

如果为了改一条记录,而去锁住整个表,这样的效率也不怎么样。为此有了记录锁 rlock() ,该函数的格式为: RLOCK([工作区|别名]

| [记录号列表, 工作区|别名])

比如:

rlock('1,3,5','rsda')

可将记录号为1、3、5的记录锁住。如果省略记录号则锁住指定工作区(或别名)的当前记录,如省略工作区(或别名),则锁定当前工作区的当前记录。如使用了记录号列表,必须有工作区(或别名),如只指定一个记录(不是列表),可没有工作区(或别名),且用数值指定,比如:

rlock(5) 注意 如果要锁住多条记录,必须设置 set multilock on,如果是 set multilock off,则只有最后一条加锁的记录

被锁住。

有了这个函数就可以锁住一条或多条记录,这样就不至于为了改一条记录,而使得其它所有记录都不能被别人修改。在许多网络软件中,都有多人同时向一个表里输入数据的情况,这就要求软件一定不能经常出现由一个人将整个表锁住的情况。 另外有一个函数 lock() ,它与 rlock() 的功能是完全一样的。

VFP 中级教程:第十五课 更多编程命令

129

BLANK

FLUSH

ON KEY LABEL

SET DEFAULT

BLANK 命令

如果发出该命令时不带任何参数,则清除当前记录中所有字段的数据。

语法

BLANK

[FIELDS 字段名列表]

[范围]

[FOR 条件]

[WHILE 条件]

[NOOPTIMIZE]

说明

如果使用了字段名列表,则清除指定字段中的数据,另外也可以在字段名前面加上别名,以清除其它工作区的表中数据,但要注意一点,如果当前工作区的记录指针指在了表的末尾,那么它也不能清除您所指定的其它工作区中的字段,即使其它工作区的记录指针没有指向末尾。

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

CALCULATE 命令

对字段或带有字段的表达式进行统计计算。

语法

CALCULATE 表达式列表 CALCULATE FOR...ENDFOR PACK DATABASE SET SKIP TO COPY FILE ON ERROR SCAN...ENDSCAN SET SKIP OF COPY STRUCTURE EXTENDEDON ESCAPE SET CONFIRM VALIDATE DATABASE

[范围][FOR 条件][WHILE 条件]

[TO 变量列表| TO ARRAY 数组名]

[NOOPTIMIZE]

参数描述

表达式列表

这里所用到的表达式与其它命令中所用到的表达式不一样,这里必须是规定的几个函数表达式,如下:

函数

AVG(表达式)

含义 计算表达式的平均值。 130

CNT()

MAX(表达式)

MIN(表达式) 返回表中记录的个数。 返回表达式中的最大值。 返回表达式中的最小值。

计算一个固定周期利率下,一系列现金流的净现值。表达式1为十进制表示

利率;表达式2代表一系列现金流的字段、字段表达式或数值表达式,每个

NPV(表达式1,表达式2,[表达式3]) 现金流可正可负,当它是字段时,每个记录的字段值都认为是一个现金流;

表达式3指定可选的初始投资,如果不包括初始投资,则假定初始投资发生

在第一阶段末,这个初始投资就是第一个记录,而且是负的,代表现金流出。

STD(表达式)

SUM(表达式)

VAR(表达式) 计算表达式的标准偏差。 对表达式的值求和。 计算表达式的均方差。

注意:这里的函数是不能单独使用的,它们与单独同名的函数也是不一样的,比如,CALCULATE MIN() 与 MIN() 是不同的,前者计算某一字段中各记录的最小值,函数只需写一个字段名,后者则是计算一系列表达式中的最小值,所有的表达式都必须写入函数中。

TO 变量列表

将计算结果存入指定的变量。注意变量列表中的变量数必须与表达式列表中的表达式个数相同。

TO ARRAY 数组名

将计算结果存入指定的数组。数组可存在,系统会自动创建,数组中的元素也不一定与表达式列表中的个数一样,如果不够,系统会自动增加,如果多了,则多出的元素保持不变。

示例

CALCULATE SUM(工资),AVG(工资),MIN(工资),MAX(工资) TO HJ,PJ,ZX,ZD

计算出工资合计、平均工资、最少工资、最多工资,并分别存入各变量。

从这个例子可以看出,用这个命令比分别用 SUM、AVERAGE 等命令方便,且执行的速度快,因为它只需要将表扫描一次就能将各值计算出来了,而分别用命令就要扫描几次,对于一个很大表这可不能等闲视之。

COPY FILE 命令

复制文件。

语法

COPY FILE 文件名1 TO 文件名2

131

说明

文件名中可以用“*”和“?”,如 *.prg。

要注意的一点是“文件名2”必须有,比如:

copy file *.prg to c:temp*.prg

不象 DOS 的 COPY 命令可以没有目的文件名,而且命令中的“FILE”也一定要有。

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

COPY STRUCTURE EXTENDED 命令

将当前工作区的表结构信息复制到另一表中。

语法

COPY STRUCTURE EXTENDED TO 表文件名 [FIELDS 字段名表]

参数说明

FileName :指定结构信息所存放的表文件名。

FIELDS 字段名表 :指定需要复制表结构中哪些字段的信息,如省略,则复制所有字段的信息。

说明

该命令将一个表的各字段信息复制到一个新表中,每个字段为一个记录,这是一个专门的表,其字段是固定,这些字段分别记录各字段的字段名、长度、数据类型等等,各字段的字段名及其意义如下: 字段名

FIELD_NAME

FIELD_TYPE

FIELD_LEN

FIELD_DEC

FIELD_NULL

FIELD_NOCP

FIELD_DEFA

FIELD_RULE

FIELD_ERR

TABLE_RULE

TABLE_ERR

TABLE_NAME

INS_TRIG

UPD_TRIG

DEL_TRIG

数据类型 字符 字符 数值 数值 逻辑 逻辑 备注 备注 备注 备注 备注 字符 备注 备注 备注 意义 字段名。 数据类型。C=字符型,Y=货币型,N=数值型,F=浮点型,I=整数型,B=双精度型,D=日期型,T=日期时间型,L=逻辑型,M=备注型,G=通用型 字段长度。 小数位数。 字段是否可用 null 值。 字段是否允许代码页翻译。 字段的隐含值。 字段验证规则。 字段验证规则出错时的提示信息。 表验证规则。 表验证规则出错时的提示信息。 长表名。 插入触发器代码。 更新触发器代码。 删除触发器代码。 132

TABLE_CMT 备注 表说明。

如图1,即为用 COPY STRUCTURE EXTENDED 命令生成的 rsda 表的结构信息表,在这个表中就可以看到 rsda.dbf 表的各字段情况。

那么用这个命令产生的结构信息表有什么用呢?这可以让我们在编程时改变一个表的结构,因为信息结构表也是个表,我们可以象操作一般表一样去修改它的内容,当然修改时要遵从表结构的有关规定,比如字段名不能超过10个字符,字段类型必须是上表中的字符之一,字段名不能重名等等。

当然我们修改了这个表后不会对原表马上产生作用,因为它们已经是分别独立的两个表,在物理上没有任何联系,但我们可以首先使用以下命令:

CREATE 表文件名 FROM 结构信息表文件名

根据结构信息表创建一个新表,再将原表的数据加到这个新表中,删除原表,将新表改为原表名,这样就修改了表的结构。比如,我们要将 rsda 表部门字段的长度改为20,程序如下:

select rsda &&选择 rsda 表。

copy structure to rsdajg extended &&将 rsda 表的结构信息复制到 rsdajg 表。

use rsdajg &&打开 rsdajg 表,同时关闭 rsda 表。

replace field_len with 20 for trim(field_name)= ='部门' &&将部门字段的字段长度修改为20。

*实际上上一句的意思是将 rsdajg 表的 field_name 为“部门”的记录的 field_len 字段值改为20。

use &&关闭 rsdajg 表,要从这个表创建新表,必须将其关闭。

create rsda2 from rsdajg &&从 rsdajg 中提取信息创建 rsda2 表。

append form rsda &&从 rsda 表中获取数据。

erase rsda.dbf &&删除 rsda.dbf 表。

rename rsda2.dbf to rsda.dbf &&将 rsda2.dbf 改名为 rsda.dbf。

FLUSH 命令

将表和索引中的数据存入磁盘。

语法

FLUSH

说明

当我们修改一个表时,修改完后,我们可能并不马上将表关闭,那么这时所做的修改可能只是在内存中,而没有真正存到盘,如果这时死机或停电,那么......,所以我们可以在程序适当的地方用此命令确保数据存盘。

比如,我们可以在菜单中加个“保存”命令,该命令就调用 FLUSH 语句,供操作者在适当的时候调用此命令来保存数据。 还可以用一个表单,在其中放一个计时器,在计时器的 timer 事件中用此命令,这样就做成一个具有自动保存数据的功能,甚至可以在菜单中加一个设置计时器时间的命令,以供操作者设置自动保存数据的间隔时间。

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

FOR...ENDFOR 命令

133

按指定的次数循环执行一组命令。

语法

FOR 变量=初始值 TO 结束值 STEP 步长值

命令组

[EXIT]

[LOOP]

ENDFOR | NEXT

参数描述

变量:指定一个变量作为计数器,该变量可以不预先存在,FOR 命令会自动创建。

初始值 TO 结束值:即计数器的初始值和结束值,也就是指定循环的次数。

STEP 步长值:设定计数器每次增加或减少的量,如果省略此子句,则每次增加1,比如 for jsq=1 to 10,那么将会循环10次,如果是 for jsq=1 to 10 step 2,那么循环将会是5次,因为每循环1次计数器增加2,从1到10只需增加5次就行了。

可能有人会问了,既然循环5次,那为什么不 for jsq=1 to 5 呢?这往往是为了在某些情况下使程序编起来方便和易于理解,比如要对数据表中的记录进行某项操作,要求每隔一条记录做一次,起始和结束的记录是根据具体情况变化的,也就是初始值和结束值都是变量,那么我们就可以使用步长子句,并将步长设为2,这样就不用具体去计算到底需要多少次循环了,如果再加上步长也是变量,即有时隔一条记录,有时会隔多条记录,则步长子句就更必要了。 注意 步长的变化是指在不同次的完整循环中步长会变,一般在一次完整的循环中不要更改初始值、结束值和步长。另外步长可以是负的,这又有什么必要?当我们需要根据一个数值不断减小来进行某项操作时就需要了,比如我们需要对记录进行某项操作,这个操作要求先处理第10条记录,再第9条(因为在处理第9条时需要根据第10条的情况),依次到第1条,那么我们就可以: for jsq=10 tp 1 step -1

* 操作命令组

go jsq &&设上面的操作移动了记录指针,此时已不指在第10条记录上,则不能用 skip -1 将指针移到第9条 endfor

很明显,虽然可以用其它方法,但这样编出的程序最简洁,一目了然。

EXIT 和 LOOP:与 DO WHILE 中的意义一样。

说明

计数器只有在大于结束值时才结束循环,即当计数器等于结束值时仍要循环,也就是说:

for jsq=1 to 10

是循环10次,而不是9次,也就是当 jsq 的值为10时,还要循环一次,当它为11时,则退出循环,执行 endfor 之后的程序。 注意

DO WHILE...ENDDO 与 FOR...ENDFOR 都是循环,它们有什么区别呢?在需要使用计数器的情况下,由于 FOR...ENDFOR 不需要专门的计数语句,所以程序执行的速度快,因此能用 FOR...ENDFOR 时尽量用。 134

ON ERROR 命令

设置一命令,当系统错误发生时,该命令执行。

这也就是程序员们常说的错误捕获陷井。所谓错误捕获陷井的意思就是在系统中启动一个监控程序,一旦错误发生,这个监控程序就将错误捕获,并不让错误显示出来,可保证程序的继续运行,然后用一个命令去对出现的错误进行处理,比如显示一个错误提示等。如果错误不发生,这个命令则始终不执行。

语法

ON ERROR [命令]

参数说明

命令:此即为指定的在捕获错误后所要执行的程序。一旦程序执行到某个语句发生错误,该命令即被执行,执行完后,接着执行发生错误的下一条语句。 有时为了对错误进行处理,一条命令可能是不够的,则可用该命令调用一个子程序,那么需要在子程序执行完

后才接着执行发生错误的下一条语句。

提示 如果子程序中有 RETRY 命令,该命令将使子程序返回,并重新执行发生错误的语句,这个命令一般用在这种情

况下,当子程序对错误进行了处理,使得再执行该命令时不会发生错误了,这样就可以使程序按照正常情况运

行下去。

多学一招 当处理错误子程序执行时,您还可以用 ERROR(), MESSAGE(), LINE(), PROGRAM() 等函数返回出错的编号、信

息、出错的语句所在行号以及出错的程序等,这些可能对处理错误有帮助。

如省略命令参数,则取消错误捕获陷井。

说明

ON ERROR 命令不能嵌套,也就是说在 ON ERROR 所执行的子程序中不能再有 ON ERROR 命令,否则等于取消错误捕获。 示例 当 DBF 文件的结构化复合索引损坏时,如果您打开 DBF 文件就会出错,下面的例子就捕获错误并自动修复索引: *主程序

on error do xfsy &&设置错误陷井

use rsda &&打开表,如果索引损坏,则会产生错误,xfsy 程序将会执行

on error &&取消错误陷井

...

cancel

*修复子程序

procedure xfsy &&修复子程序的过程名

erase rsda.cdx &&删除索引文件

135

use rsda &&打开表

index on 编号 tag of rsda.cdx &&重建索引“编号”

index on 姓名 tag of rsda.cdx &&重建索引“姓名”

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

ON ESCAPE 命令

设置一命令,当按了ESCAPE时,该命令执行。

语法

ON ESCAPE

[命令]

参数说明

命令:该命令即为指定的按下 ESC 键后所要执行的命令。

假设当程序执行到第10条语句时您按下了 ESC,那么在命令执行完后,将接着执行第11条语句,如果命令是调用一个子程序,而子程序中有 RETRY 语句,则返回重新执行第10句。

如果省略此参数,则

说明

假如 ON KEY LABEL 命令也同时指定了 ESC 键,ON ESCAPE 所指定的命令优先执行。

如 SET ESCAPE OFF 则该命令也不起用。 这个命令常常用来在一个循环中退出循环,比如:

on escape exit

do while .t.

*循环体中的命令组

enddo

on escape

这样在循环中执行命令时,只要您随时动用您的玉指按下 ESC 键,即可令程序退出循环。

注意 但别高兴得太早,上面这个程序在执行时常常出错,但也不是总出错,为什么呢?因为 exit 命令必须在循环体

中,假如程序正执行到 do while 或 enddo,这时您动了玉指,程序就会出错,怎么解决这个问题呢?这样:on escape tc=.t.

tc=.f.

do while .t.

*循环体中的命令组

if tc

exit

endif

136

enddo

on escape

思考题:上面这个程序为什么不会出错?

ON KEY LABEL 命令

指定一个命令,当键盘上某个键(也可以是组合键或鼠标)被按下后,该命令执行。 语法

ON KEY LABEL 键名 [命令]

参数描述

各键的键名如下: 键

HOME

END

PAGE UP

PAGE DOWN

DEL

BACKSPACE

SPACEBAR

INS

TAB

SHIFT+TAB

ENTER

F1 to F12

CTRL+F1 to CTRL+F12

SHIFT+F1 to SHIFT+F12

ALT+F1 to ALT+F12

ALT+0 to ALT+9

ALT+A to ALT+Z

CTRL+LEFT ARROW

CTRL+RIGHT ARROW 键名 LEFTARROW RIGHTARROW UPARROW DNARROW HOME END PGUP PGDN DEL BACKSPACE SPACEBAR INS TAB BACKTAB ENTER F1, F2, F3 ... CTRL+F1, CTRL+F2 ... SHIFT+F1, SHIFT+F2 ... ALT+F1, ALT+F2, ALT+F3 ... ALT+0, ALT+1, ALT+2 ... ALT+A, ALT+B, ALT+C ... CTRL+LEFTARROW CTRL+RIGHTARROW

137

CTRL+HOME

CTRL+END CTRL+PAGE UP

CTRL+PAGE DOWN

CTRL+A TO CTRL+Z

CTRL+0

RIGHT MOUSE BUTTON

LEFT MOUSE BUTTON

MOUSE BUTTON

ESC CTRL+HOME CTRL+END CTRL+PGUP CTRL+PGDN CTRL+A, CTRL+B, CTRL+C ... CTRL+0 RIGHTMOUSE LEFTMOUSE MOUSE ESC

ON KEY LABEL 键名 后面不跟命令则解除该键所要执行的命令(即解除所设的程序陷井)。 说明

该命令的用法与 ON ESCAPE 的用法基本上是一样的。

该命令对于在系统菜单及系统对话框上的按键不起作用。

注意

对于 on mouse 等命令,在鼠标点击件时不起作用。

示例

on key label ctrl+e tc=.t.

tc=.f.

do while .t.

*循环程序组

if tc

exit

endif

enddo

on key label ctrl+e

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

PACK DATABASE 命令

将数据库所有表中作了删除标记的记录物理删除,即真正从磁盘上删除。

语法

PACK DATABASE

说明

执行该命令时,数据库必须以独占方式打开。

138

该命令可用于在程序中定期清理数据库各表中的删除记录,因为软件可能经常要作删除,对大的表会影响运行速度,所以可以在软件中设置 set delete on,即隐去作了删除标记的记录,但这些记录并没有真正删除,但这些垃圾记录太多了也对系统不利,故可设一功能由操作人员在需要时运行此命令来清理整个数据库。

SCAN...ENDSCAN 命令

将记录指针由头到尾扫描一遍,每移动一次记录指针可执行一组命令。

类似于循环 DO...ENDDO 或 FOR...ENDFOR,只是这是专门针对表的循环。

语法

SCAN [范围] [FOR 条件1] [WHILE 条件2]

[命令组]

[LOOP]

[EXIT]

ENDSCAN

参数描述

范围:在指定范围内扫描,即不从开头扫描到最后。

FOR 条件1 及 WHILE 条件2:只扫描符合条件的记录。

如果不带范围和条件子句,则隐含扫描表中的全部记录。

示例

库存日记帐表(字段有日期、入库数、出库数,库存数),每记录一笔出入库,则计算相应的库存数并放入表中,有时意外情况可能导致库存数出错,因此需要有一功能将库存数重新计算一遍,下面这个程序就是完成这个功能: go top

dqkc=库存数 &&记下第一条记录的库存数

skip &&跳到第2条记录

scan rest &&只需扫描从第2条记录起的所有记录

replace 库存数 with dqkc+入库数-出库数

dqkc=库存数 &&记下当前记录的库存数

endscan

上面这段程序与下面这段程序是等价的: go top

dqkc=库存数 &&记下第一条记录的库存数

skip &&跳到第2条记录

do while .not. eof()

replace 库存数 with dqkc+入库数-出库数

dqkc=库存数 &&记下当前记录的库存数

skip

endscan

139

所不同的就是不用每次都 skip,系统自动移动记录指针,这样程序运行的速度快,尤其对于按条件扫描的情况就更快了,因为用 do 的话每次都要用 locate 或 continue 进行查询,速度很受影响。

与这段程序也是等价的: go top

dqkc=库存数 &&记下第一条记录的库存数

skip &&跳到第2条记录

for jsq=2 to reccount()

replace 库存数 with dqkc+入库数-出库数

dqkc=库存数 &&记下当前记录的库存数

skip

endfor

您喜欢用哪个呢? 上面第三段程序中有个小问题,在 for 语句中用了 reccount() 函数,而函数都要经过运算才能得到结果,比起直接从一个变量中获取值要慢,假如只执行1次,两者相差无几,但上面是用在循环中,for 这一句可能会执行几十万次,那么就会使速度降低很多,为此可改成下面这样:

注意 ...

jls=reccount()

for jsq=2 to jls

...

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

SET CONFIRM 命令

当输入的内容填满输入区域时是否需要按回车键跳出输入区域。

语法 SET CONFIRM ON | OFF

参数描述

ON 需要按回车键。也可以按 TAB 或其它箭头键等。

对表单上的文本框也起作用。当对于需要慎重输入的地方,可将其设置为 ON,以便让操作者在离开输入之前可再提示 看一下所输入的内容有没有问题,如果不设置为 ON,当输入内容输入满时,光标会自动离开文本框,不利于发现

错误。

OFF 不需要按回车键(隐含)。

SET DEFAULT 命令

设置隐含的驱动器及目录。

140

设置了隐含的路径,我们可以在程序运行打开每个隐含路径下的文件时不用再指定路径了。

语法

SET DEFAULT TO [路径]

参数说明

路径:可以是符合操作系统要求的任何路径,比如:

set default to d:softrsgl

set default to

set default to .. 如果路径中带有空格,必须用引号将路径括起来,否则会出错。比如:

注意

set default to "d:softrsgl 佳帆"

示例

当我们的程序在运行时,一般我们都需要知道该程序运行所在的目录,因为往往许多数据也是放在这个目录下,我们不能指定一个绝对的路径,因为程序编好后可能拿到另一台电脑上用,其路径可能发生变化,一旦变化便找不到所需要的数据文件了,因此我们必须有一种方法获取其当前所在的路径,有一种办法是这样:

cxlj=sys(5)+sys(2003)+''

这在程序编译成独立 EXE 文件时运行是对的,但在 VFP 系统中运行 PRG 程序却不对,它返回的是 VFP 所在的目录,为了解决这个问题,可采用如下程序: CXLJ=SYS(16) &&获取当前运行的程序名及其所在路径

FOR JSQ=1 TO LEN(CXLJ) &&用一个循环找出最右边一个反斜线,将其后的程序名去掉,只剩路径

CXZF=LEFT(RIGHT(CXLJ,JSQ),1) &&从 CXLJ 的右边依次取出每个字符

IF CXZF='' &&查看这个字符是不是反斜线

CXLJ=STUFF(CXLJ,LEN(CXLJ)-JSQ+1,JSQ,'') &&如果是,就将反斜线之后的字符全删掉并退出循环

EXIT

ENDIF

ENDFOR

SET DEFAULT TO "&CXLJ" &&用宏替换设置隐含路径,用引号是为了带空格的路径也能正确设置

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

SET SKIP TO 命令

在表之间建立一对多关系。

语法

141

SET SKIP TO [表1 [, 表2] ...]

参数描述

表1 [, 表2] ... 为一对多表中的子表的别名。可以有多个子表,它们之间用逗号隔开。

如省略此参数,则取消一对多关系,但一对一关系仍存在。

说明

我们用 SET RELATION 命令可创建表之间的一对一关系,也就是当我们将父表的记录指针移到某一个记录上时,子表的记录指针也会移到相应的关联记录上,比如我们有一个人事档案(rsda,有字段编号、姓名等),另有一个参加项目表(cjxm,有字段编号和项目,项目中记录该人员所参加过的项目,编号建立了索引),然后用如下程序: close all

use rsda

select 0

use cjxm

set order to 编号

select rsda

set relation to 姓名 into cjxm

那么当我们将 rsda 的记录指针移到一个记录上时,cjxm 也会移到相同编号的记录上,但由于是一对一关系,只有 cjxm 中的相同编号的第1个记录会与 rsda 中的记录对应,其它的记录不会与之对应。

但我们知道一个人可能参加了多个项目,由于一对一的关系,我们没法从父表中知道子表中有多少个记录与之对应,比如这时您在父表中键入如下命令:

display 编号,姓名,cjxm,项目 for 编号='10'

那么只会有一条记录出现,因为 rsda 中只有一个编号为 10 的记录,虽然可能他参加了多个项目,但这时不会显示出来,要想显示出来就必须建立一对多的关系,比如我们在 rsda 所在工作区键入如下命令:

set skip to cjxm

然后再运行上面的 display 命令,子表中所有对应的记录都会显示出来,即可看到 10 号人员所参加的所有项目,而编号和姓名都是一样的。

怎么样,很不错吧!

这时如果您用 browse 看 rsda 表,假如 cjxm 中有多条编号为 10 的记录,您会发现 10 号记录下面会多出几条记录来,它们的内容全部是“*”,且不可修改,这就是系统根据一对多关系增加的假记录,依靠这些记录,上面的 display 命令才能带来我们想要的结果。 注意 如果这时删除子表中的记录,且做了 pack,那么关联将被解除,包括一对多和一对一,如仍需要一对多关系,必

须重新用 set relation 和 set skip to 建立一对多关系。

思考题

以下是前两天一位网友在技术论坛提的问题,大家思考下用 set skip to 是否能很好地解决他的问题。

142

想请教一个编程的问题,在中文版VFP6.0中,有一些数据分布在三个数据库中,如A库中有各个企业的基本情况,在B库中有各个企业的董事会组成成员情况,在C库中有各个企业的分支机构情况,三者可以通过企业编号进行索引,其相互关系是:A库中一个记录即一个企业,对应于B库中的至少三个记录(即不同的组成成员),再对应于C库中的至少三个记录(即不同的分支机构),现在的问题是,我如何能在一张页面上,同时打印A库中的一条记录,B库和C库中的三条以上的记录。请不吝赐教!谢谢。

SET SKIP OF 命令

设置菜单或菜单项是否跳过,即是否可用。

语法

SET SKIP OF MENU 主菜单名 逻辑表达式

SET SKIP OF PAD 主菜单项名 OF 主菜单名 逻辑表达式

SET SKIP OF POPUP 子菜单名 逻辑表达式

SET SKIP OF BAR 子菜单项 OF 子菜单名 逻辑表达式

参数描述

MENU 主菜单名 逻辑表达式

设置主菜单是否跳过,逻辑表达式为“真”时跳过,即整个菜单不可用,反之则可用。

例如,可以使用以下命令禁止 VFP 的系统菜单 _MSYSMENU:

set skip of menu _msysmenu .t.

使用以下命令则启用它:

set skip of menu _msysmenu .t.

PAD 主菜单项名 OF 主菜单名 逻辑表达式

设置主菜单中某个菜单项是否跳过,如果设置为跳过,假如该项下有子菜单,则子菜单不能拉下来。

可用如下命令禁止 VFP 主菜单上的“编辑”菜单:

set skip of pad _msm_edit of _msysmenu .t.

POPUP 子菜单名 逻辑表达式

设置某个子菜单是否跳过,如设置为跳过,该子菜单可以拉下,但其中的菜单项都不可用。

143

可用如下命令禁止 VFP 的“编辑”子菜单:

set skip of popup _medit .t.

BAR 子菜单项 OF 子菜单名 逻辑表达式

设置某个子菜单中的某个菜单项是否跳过。

可以用下面的命令设置 VFP “文件”子菜单中的“新建”不可用:

set skip of bar _mfi_new of _mfile .t.

说明

那么对于菜单设计器设计的我们自己的菜单怎样进行设置呢?其实当我们用菜单设计器设计好一个菜单并完成生成后,就会生成一个扩展名为 mpr 的菜单程序,它与 prg 程序是一样的,我们可以用如下命令将其打开来看:

modify command xxx.mpr

打开后我们就可以看到其中的主菜单名及其相应的主菜单项、子菜单、子菜单项等。下面便是一个 mpr 文件: *菜单的说明信息略

SET SYSMENU TO

SET SYSMENU AUTOMATIC

DEFINE PAD _s950ndhs4 OF _MSYSMENU PROMPT "系统录入及查询" COLOR SCHEME 3

DEFINE PAD _s950ndhs6 OF _MSYSMENU PROMPT "系统维护" COLOR SCHEME 3

DEFINE PAD _s950ndhs7 OF _MSYSMENU PROMPT "统计" COLOR SCHEME 3

DEFINE PAD _s950ndhs8 OF _MSYSMENU PROMPT "备份数据" COLOR SCHEME 3

ON PAD _s950ndhs4 OF _MSYSMENU ACTIVATE POPUP 系统录入及

ON PAD _s950ndhs6 OF _MSYSMENU ACTIVATE POPUP 系统维护

ON PAD _s950ndhs7 OF _MSYSMENU ACTIVATE POPUP 统计

ON PAD _s950ndhs8 OF _MSYSMENU ACTIVATE POPUP 备份数据

*主菜单即为 _MSYSMENU

*主菜单中的菜单项分别为 _s950ndhs4, _s950ndhs6 等

*_s950ndhs4 下的子菜单是“系统录入及”

*_s950ndhs6 下的子菜单是“系统维护”

*...

DEFINE POPUP 系统录入及 MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF 系统录入及 PROMPT "数据录入" ;

KEY ALT+L, "ALT+L" ;

MESSAGE '录入房地产广告信息'

DEFINE BAR 2 OF 系统录入及 PROMPT "数据查询" ;

KEY ALT+C, "ALT+C" ;

MESSAGE '查询和修改录入的房地产广告信息'

DEFINE BAR 3 OF 系统录入及 PROMPT "数据整理" ;

144

KEY ALT+Z, "ALT+Z" ;

MESSAGE '如发现数据有问题,可调用此命令进行整理,如整理后仍有问题,请与软件供应商联系'

DEFINE BAR 4 OF 系统录入及 PROMPT "-"

DEFINE BAR 5 OF 系统录入及 PROMPT "退出系统" ;

KEY ALT+E, "ALT+E"

ON SELECTION BAR 1 OF 系统录入及 do sjlr

ON SELECTION BAR 2 OF 系统录入及 do lrsjcx

ON SELECTION BAR 3 OF 系统录入及 do sjzl

ON SELECTION BAR 5 OF 系统录入及 clear event

*“系统录入及”子菜单中的各菜单项为 1、2、3 等

*以下略

比如可以在程序中使“系统录入及查询”下的“数据整理”不可用,即“系统录入及”子菜单中的第3个菜单项,命令为: set skip of bar 3 of 系统录入及 .t.

我们知道在菜单设计器可为每个菜单项设置跳过表达式,那么这个命令还有什么用吗?有时用它还是比较方便,比如我们要让一个子菜单中的所有菜单项不可用,我们可以设置该子菜单不可用,如用跳过要一个个菜单项去设,或者每个菜单项都设一个比较复杂的表达式,再要不就设调这个子菜单的主菜单项不可用,那样又看不到子菜单中有什么内容了(有时虽然不可用但也想看看),总之是比较麻烦,那么用这个命令就好一些。

不好的地方是要把 mpr 程序打开来记住那些菜单名、菜单项,你们也看到上面了,不是那么好记的。反正这个世界没有十全十美的东西,我们只好将就点了。

[dvnews_page=VFP 中级教程:第十五课 更多编程命令]

VALIDATE DATABASE 命令

保证当前库中表和索引位置的正确性。

语法

VALIDATE DATABASE

[RECOVER]

[NOCONSOLE]

[TO PRINTER [PROMPT] | TO FILE 文件名]

参数描述

RECOVER:显示一个对话框,该对话框允许您定位表和索引,这些表和索引不在被检查的数据库中。必须在命令窗口中发出 VALIDATE DATABASE RECOVER 命令,在程序中发布该命令会产生错误信息。

NOCONSOLE:如发现数据库中有错误,不向屏幕输出错误信息。

TO PRINTER [PROMPT]:打印错误信息。

TO 文件名:将错误信息输出到一个文件。

说明

该命令确保数据库包含的表和索引处于正确位置,确保数据库中的表包含正确的字段,以及确定数据库中索引标识是否存在。 145

该命令对当前数据起作用,而且要求数据库必须是以独占方式打开。

虽然该命令一般不用在程序中,但由于一些初学者是第一次接触数据库(不是表),故这个命令还是有必要讲一下。当我们发现数据库有不正常的情况时,就可用这个命令检测和恢复数据库。 示例

下面的示例打开 testdata 数据库,并使用 VALIDATE DATABASE 命令,以确保表和索引的位置在数据库中是正确的。

close databases

set path to (home(2)+'data') &&设置数据库路径

open database testdata exclusive &&以独占方式打开数据库

validate database

VFP 中级教程:第十六课 更多编程函数

第十六课 更多编程函数

ALLTRIM() CHRTRAN() FIELD() LEFT() MESSAGEBOX() STRCONV()

BETWEEN() CTOT() FILTER() LEN() RAND() STRTRAN()

CAPSLOCK() DIRECTORY() GETFILE() LIKE() RAT() STUFF()

CHR() EMPTY() IIF() LOOKUP() REPLICATE() SUBSTR()

以上讲的是一些编程时常用到的函数,并不是 VFP 函数的全部,还有很多其它函数,大家可在帮助中找到,另外可在主页附录2的“VISUAL FOXPRO 6.0中文版语言参考手册”中查到,其中有一大堆的 SYS() 函数,有些在编程时也会用到的,在此就不一一讲了。 ALLTRIM() 函数

删除指定字符表达式的前后空格符,并返回删除空格后的字符串。 语法

ALLTRIM(字符表达式) 返值类型 字符型 参数描述

字符表达式:即为需要删除前后空格的字符串。

说明

这个函数在程序中经常需要用到,比如我们有时需要检查某个数据在表中是否有重复的,我们可能在一个文本框里输入要查询的内容,而该文本框的 controlsource 是一个变量,比如 content,可在文本框的 valid 事件中放入查询语句,如:

146

locate for 型号=content

如果找到就代表有重复值。然而这个命令却不能满足我们查询重复值的要求,因为它不是精确匹配的,也就是说 content 中的内容与型号的前几个字符相等就算查到了,比如型号是“PINTIUM II”,content 的内容是“PINTIUM”,那么就算它们相等了,但其实它们并不是一个型号。为此把命令改为如下形式:

locate for 型号= =content

这样“PINTIUM II”与“PINTIUM”就不会认为是相等了,但这样却会有一个问题,即使有重复值可能也查不到,为什么呢?因为我们知道字段中的内容一般都小于字段的长度,不够的部分以空格填满,也就是说型号中的内容往往是:

“PINTIUM II ”

如果 content 中的内容是“PINTIUM II”,则在精确匹配的情况下电脑会认为它们不相等,即认为没有重复值,但其实它们是重复的(电脑有时也真笨)。为了解决这个问题就把命令改为:

locate for alltrim(型号)= =content

这样上面所说的问题就没有了,即使型号中的内容是“ PINTIUM II ”也不怕(您在往表中录入数据时可能不小心在前面多打了一个空格),alltrim() 函数会把前后的空格都去掉,再与 content 比较,于是会认为它们是重复的。 不过还有个问题可能您疏忽了,假如您在 content 中录入查询内容时不小心在前面或后面多打了空格,即 content 中的内容可能是“ PINTIUM II ”,电脑又会认为它与表中的“PINTIUM II”不同,怎么办呢?改成这样就行了:

locate for alltrim(型号)= =alltrim(content)

这样不论是型号还是 content,不论是前面还是后面,也不论是有多少个空格,只要它们的实质内容是一样的,电脑就会认为是重复的,这正好满足了我们的需要,看来电脑在我们的教导下也变得聪明了:)

一个典型的检测重复值的 valid 事件往往是这样: select xhb &&选择相应的表,比如:xhb。

locate for alltrim(型号)= =alltrim(content)

if found()

messagebox('该型号在表中已存在,请重新输入!',48,'注意') &&给出提示。

return 0 &&焦点不离开当前文本框,以便重新输入。

else

return 1 &&如果没有找到重复的值,焦点跳到下一个控件。

endif

另外还有两个与这个函数有关的函数,一个是删除字符串后面的空格 TRIM();另一个是删除字符串前面的空格 LTRIM()。 LTRIM() 有个特殊有途,当我们把一个数值转换为字符时,隐含是按8位数转换的,不够8位以空格填满,也就是说 str(5)返回的值是:

5

前面会有7个空格,而这往往是我们不需要的,因此我们可以用 ltrim(str(5)) 来返回我们需要的值:

5

当然聪明的您一定会想到用 alltrim(str(5)) 也是一样的。

147

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

BETWEEN() 函数

确定一个值是否介于另两值之间。

语法

BETWEEN(测试值,低值,高值)

返值类型

逻辑型或 null 值

参数描述

如果测试值介于低值与高值之间,包括等于低值或高值,函数返回 .t.,如:

?between(5,2,8) 或 ?between(2,2,8)

否则返回 .f.,如:

?between(9,2,8)

如果低值或高值中有一个是 null 值,那么将返回 null 值。

示例

我们在编程时常常有这种情况,需要处理某个时间段的数据,比如某个月的数据,我们往往用 set filter 将所需数据过滤出来,比如: ...

date1=date() &&设置时间段低值的隐含值,取系统日期

date2=date() &&设置时间段高值的隐含值,也取系统日期

do form srrq name srrq &&在这个表单中用两个文本框输入 date1 和 date2

*一般在表单关闭前可以检测一下 date2 是否大于 date1,否则叫他重输入。

*选择需要的表所在工作区,然后再执行下面这句,除非已在所要的工作区。

set filter to between(日期,date1,date2) &&“日期”为表中的一个字段名

...

当然,如果您高兴,这最后一句也可以是:

set filter to 日期>=date1 .and. 日期<=date2

是不是麻烦点儿?

BETWEEN() 函数

确定一个值是否介于另两值之间。

语法

148

BETWEEN(测试值,低值,高值)

返值类型

逻辑型或 null 值

参数描述

如果测试值介于低值与高值之间,包括等于低值或高值,函数返回 .t.,如:

?between(5,2,8) 或 ?between(2,2,8)

否则返回 .f.,如:

?between(9,2,8)

如果低值或高值中有一个是 null 值,那么将返回 null 值。

示例

我们在编程时常常有这种情况,需要处理某个时间段的数据,比如某个月的数据,我们往往用 set filter 将所需数据过滤出来,比如: ...

date1=date() &&设置时间段低值的隐含值,取系统日期

date2=date() &&设置时间段高值的隐含值,也取系统日期

do form srrq name srrq &&在这个表单中用两个文本框输入 date1 和 date2

*一般在表单关闭前可以检测一下 date2 是否大于 date1,否则叫他重输入。

*选择需要的表所在工作区,然后再执行下面这句,除非已在所要的工作区。

set filter to between(日期,date1,date2) &&“日期”为表中的一个字段名

...

当然,如果您高兴,这最后一句也可以是:

set filter to 日期>=date1 .and. 日期<=date2

是不是麻烦点儿?

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

CHR() 函数

返回 ASC 码所代表的字符。

语法

CHR(ASC 码表达式)

返值类型

字符型

参数描述

149

ASC 码表达式:该函数即返回该码所代表的字符,对西文字符其值在 0-255 之间,如:

?chr(65)

返回“A”。

而中文在 33088-65279 之间,如:

?chr(55215)

返回“庄”

但要注意在 33088-65279 中间有很多是空字符,还有很多会使chr()函数出错,下面有一个可下载的“字符表”文件,是 DBF 文件,记录了可显示的汉字,chr 字段中是“e”的表示该码会出错。

说明

我们可以和这个函数显示一些打不出来的特殊字符,如嗽叭发声的 ASC 码是 7,用如下函数就能让嗽叭发声: ??chr(7) 用如下命令可以设置一个声音文件(wav 格式文件)作为 chr(7) 所发出的声音:

多学一招

set bell to wav文件,1

13 是回车符的 ASC 码,用如下命令可将等待提示信息显示成两行:

wait window '注意!'+chr(13)+'您输入了错误数据。' 再学一招 另有一个函数 ASC(),是 CHR() 函数的逆运算,即返回一个字符的 ASC 代码,如:asc('A') 就返回65。 示例

运行如下程序试试:

CLEAR

FOR nCOUNT = 65 TO 75

? nCount

?? ' ' + CHR(nCount)

ENDFOR

CHRTRAN() 函数

将一字符串中的某些字符替换为另一些字符。

语法

CHRTRAN(字符表达式1,字符表达式2,字符表达式3)

150

返值类型

字符型

参数描述

字符表达式1:字符串,其中的某些字符将被替换。

字符表达式2:确定字符表达式1中的哪些字符将被替换。

字符表达式3:用于替换的字符。

看了上面的内容后可能还是有点稀里糊涂,那就用一个例子来说明吧:

? CHRTRAN('AABCDEF', 'ACE', 'XYZ')

显示结果是:XXBYDZF

即用“X”替换“AABCDEF”中的“A”,“Y”替换“C”,“Z”替换“E”。这下明白了吧:) 如果“字符表达式3”中的字符少于“字符表达式2”中的字符数,那么“字符表达式1”与“表达式2”中多余的字符相匹配的字符将被删除,怎么讲起来总象绕口令,还是看例子:

注意 ? CHRTRAN('AABCDEF', 'ACE', 'XY')

显示结果是:XXBYDF,“E”被删掉,或者说“XY”中第3个字符是空字符,“E”被这个空字符替换了。

说明

另有一个函数 CHRTRANC(),主要用于双字节的字符,如字符串中有中文,最好使用这个函数。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

CTOT() 函数

该函数将日期时间格式的字符型数据转换成为日期时间型数据。

这个函数类似于 CTOD(),即将字符转换为日期。

语法

CTOT(字符表达式)

返值类型

日期时间型

参数描述

字符表达式:用于转换的日期时间格式的字符型数据。

备注

151

这里强调字符型数据必须符合日期时间型数据的格式才能转换成功,比如:

02/16/95 2:00:00pm

02/16/95 2:00pm

02/16/95 14:00

02/16/95

14:00

不是说 ctot('庄稼') 也能转换成一个日期时间型数据。

而我们知道,日期的格式与 set date 的设置有关,比如您如果在隐含状态下用如下函数就不会得到您想要的: ?ctot('1999.11.26 14:00:00')

如果您作了下面的设置,就可以得到正确结果了:

set date ansi

也许有的聪明的网友会想到,时间的格式有12小时和24小时之分,它们由 set hour to 命令确定,即 set houre to 24 为24小时显示格式,那么这个函数会否受这个设置限制呢,即在 set hour to 12 的情况下是否“1999.11.26 14:00:00”会转换不成功?

不会的,这个设置只影响转换后结果的显示方式,即在 set hour to 12 的情况下,这个命令:

?ctot('1999.11.26 14:00:00')

执行的结果为:

1999.11.26 2:00:00 PM

同理,set century 的设置也只影响结果的显示,而不影响函数的执行,也就是说字符可以是“99.11.26 14:00:00”,也可以是“1999.11.26 14:00:00”,当然如果没有世纪位数,系统会自动为其加上“19”,那么您要当心千年虫了 :-P 如果省略时间,系统会自动加上 12:00:00。

注意

如果省略日期,系统会自动加上 1899.12.30。

另有一个函数 TTOC(),是 CTOT() 函数的逆运算,即将日期时间型数据转换为字符型,类似于 DTOC() 。

DIRECTORY() 函数

检测某个目录是否存在,假如存在返回 .T.。

语法

DIRECTORY(目录名)

目录名为直接的字符要用引号引起来,比如:

?directory('d:jfz')

152

如果是变量,则不要用引号,比如:

dir='d:jfz'

?directory(dir)

返值类型

逻辑型。

示例

有时为了程序的数据的安全性,每次退出程序时要将所有的数据表在硬盘上复制一份,一般是复制到另一个目录下,那么在复制前就应该先检测一下该目录存不存在,如果不存在就先建这个目录,然后再复制,程序如下: if .not. directory('c:rsdabf')

md c:rsdabf

endif

copy file rsda.dbf to c:rsdabfrsda.dbf

copy file rsda.cdx to c:rsdabfrsda.cdx

可别小看这一小段程序,它对于防止有人恶意删除数据很有用,曾经有人因我的程序中有这个功能而避免了几千条记录的丢失。

说明

另有一个函数 file(),用于检测某个文件是否存在,用法与这个函数类似。

下面举个应用 file() 函数的例子:

软件在使用过程中免不了会出现程序异常中止的情况,比如死机、停电等,对于数据库(表)来说最常见的危害就是可能造成索引损坏。如果能使程序有这样一种功能,当异常中止后,下次启动程序时能够将索引自动修复,那无疑对软件的稳定性及数据的安全性有非常大的好处,您编的软件也会让人觉得酷很多,就好象 WIN98 死机后重新启动会检测硬盘一样,下面我们就用 file() 这个函数来实现这个功能。

1. 在程序结束时,即 cancel 命令之前,加一句如下命令:

save to xtzcgb

这个命令的意思是将系统的内存变量存入 xtzcgb.mem 这个文件,至于真的存了什么变量并不重要,要的就是这个文件名。

2. 系统每次启动时检测硬盘上是否有 xtzcgb.mem 这个文件,如果有,就说明上次系统是正常退出的,然后就将该文

件删除。那么如果系统运行的过程中异常中止了,就不会运行 save to xtzcgb 这个文件,由于 xtacgb.mem 已经删除了,硬盘上就没有这个文件,因此系统启动时如果没有检测到这个文件,就说明上次系统是异常退出的,这时就可以修复索引,以保证索引是正确的。启动时的程序如下: if file('xtzcgb.mem')

erase xtzcgb.mem

else

use rsda

reindex

*如果还有别的带索引的表,可以将它们依次打开重新索引。

endif

153

再举一个应用这个函数的例子:

程序编好后用了一段时间一般会遇到需要修改的情况,有时特别讨厌的是数据表的结构也要改,这时我们程序员往往要跑去修改用户的数据表,我们不能只在自己的电脑上修改,再复制到用户的机器上,因为用户的表中已经有大量的数据了,这里有一种办法让您以后不用跑路了。

思路是这样:

1. 把修改数据表结构的命令编成程序放到整个软件系统中,这里一般要用到 copy structure ... extended 命令;

2. 当然不能让程序启动时就直接进入修改程序,那样岂不是每次系统启动时都要修改,而这种修改是不能进行两次的,

否则会造成错误。因此让系统启动时先检测硬盘上是否有某个特殊的文件名存在,如果存在,就进入修改程序,修改完后将这个文件删除,这样下次启动时就不会再修改了;

3. 当您把修改后的程序给用户时一定要记得把那个特殊文件名的文件一起给用户,至于文件的内容无关紧要,只需要

这个文件名;

4. 特别要注意的是,修改数据表结构之前一定要将表备份,以防在修改中出现意外,造成不可挽回的损失。 以下是一个系统自动升级的例子: *这是为 jck 数据表加一个备注字段的程序,放在主程序的开始部份。

if file('updates.mem')

wait window '正在更新系统,请稍候...' nowait

use jck

copy to jckbf &&将 jck 中的数据复制到 jckbf 做一备份。

copy to jckjg structure extended &&生成 jck 表的结构表。

use jckjg &&打开结构表,同时关闭 jck 表。

append blank &&为结构表添加一新的记录,也就是为了 jck 表添加一个字段。

replace field_name with '备注',;

field_type with 'c',;

field_len with 50,;

field_dec with 0,;

field_null with .f.,;

field_nocp with .f. &&输入“备注”字段的有关信息。

use &&关闭结构表。

create jck from jckjg &&根据新的结构表生成新的 jck 表。

append from jckbf &&从 jckbf 中将数据添加到新的 jck 表中。

erase updates.mem &&将 updates.mem 文件删除,下次系统启动时将不再运行本段程序。

wait window '系统更新完毕。' nowait

endif

是不是很棒 :-)

请参看 copy constructure extended 命令的具体用法。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

154

EMPTY() 函数

确定一个表达式的值是否为空。

语法

EMPTY(表达式)

返值类型

逻辑型

参数描述

表达式:被用于测试是否为空的表达式。

表达式的数据类型可以是字符、数值、日期或者逻辑型数据,各种类型数据当出现以下情况下时,该函数返回 .T.: 数据类型

字符 数值

货币

浮点

整数

双精度

日期

日期时间

逻辑

备注

通用型

图形 值 空字符串,或只有空格、回车符等。 0 0 0 0 0 空日期,比如 empty(ctod(''))就返回 .T.。 空日期时间,比如 empty(ctot())。 假(.F.) 空,即没有内容。 空,即没有 OLE 对象。 空,没有图形。

EMPTY() 不能用于确定一个对象引用变量是否为空,比如一个表单的引用变量,当表单被关闭了,但引用变量中还是包含有 null 值,即用 empty() 函数检测不为空。

下面的例子确定一个对象引用变量中的对象是否存在:

bd1 = CREATEOBJECT('Form')

WAIT WINDOW IIF(TYPE('bd1') = 'O' AND !ISNULL(bd1),;

'bd1 表单存在',;

'bd1表单不存在')

示例

以前我们检测一个字符变量是否为空往往是用如下两种方法:

if trim(bl)=='' &&即将空格消除后精确地等于空字符串。

155

或者

if len(trim(bl))=0 &&即将空格消除后其长度为0。

现在就不需要这么麻烦了,只需要:

if empty(bl)

即可。

FIELD() 函数

返回指定的第几个字段的字段名。

语法

FIELD(字段序号 [, 工作区 | 别名])

返值类型

字符型

参数描述

字段序号:由您指定需要知道第几个字段的字段名。

工作区|别名:指定某工作区或别名的相应字段。如果省略则为当当前工作区。

如果指定工作区没有表打开,则返回空串,如果指定的别名不存在,会出错。

示例

比如我们做一个通讯录的软件,可以允许用户自己添加字段,那么我们的查询程序就不能只是个简单的 locate,因为程序并不知道表有哪些字段,这时就可以用 field() 函数来将表的各字段名提取出来,以供用户选择,然后再根据选择的字段进行查询,示范程序如下: fc=fcount() &&先获取有多少个字段。

dimension fn(fc) &&定义与一个数组用与存放字段名,数组元素与字段数一样多。

for jsq=1 to fc

fn(jsq)=field(jsq) &&将各字段名赋给相应的 fc 数组变量。

endfor

*接着可运行一个选择字段和输入查询内容的表单,

*可把字段名放在一个列表框中供选择,

*选择后的字段名放在 cfn 变量中,查询内容放在 cont 变量中。

locate for &cfn=alltrim(cont) &&根据选择的字段用宏替换进行查询。

if .not. found()

messagebox('抱歉,没有您要查询的内容。',48+0+0,'注意')

endif

156

一个可以任意增减字段的程序,会给人高水平的感觉,至于怎样增减字段,可参看 DIRECTORY() 函数中的那个软件自动升级例子。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

FILTER() 函数

返回数据表用 set filter 命令所设的过滤条件。

比如下面的程序:

use rsda

set filter to 部门='电脑'

? filter()

结果将显示:部门='电脑'

语法

FILTER([工作区 | 别名])

返值类型

字符型

参数描述

工作区:返回指定工作区的过滤条件。如果该工作区没有表打开,则返回空字符串。

别名:返回指定别名表的过滤条件。假如别名不存在会出错。

说明

如果省略工作区或别名,则返回当前工作区表的过滤条件。

示例

我们在程序可能会有这种情况,以某种条件过滤出某种类型的数据以便于查看,在查看的时候可能又需要增添一条记录,这时您可能会用如下程序: append blank

do form sjlr name sjlr &&启动数据录入的表单

这样就错了,因为新增的这条空记录一般不会满足过滤的条件,因此一经增添它便被隐藏了,您无法向其中输入数据,这时应该采用如下的程序: gltj=filter() &&记下当前的过滤条件

set filter to &&取消过滤条件

append blank

do form sjlr name sjlr

set filter to &gltj &&用宏替换将过滤条件设回原来状态

157

要注意的是增添的记录必须符合过滤条件,否则条件设回原来状态后这条记录将看不见。

为了避免输入者不按条件输入,造成数据没有录入的错觉,可让系统自动为其输入符合条件的数据,比如当前设定的是:部门='电脑',那么在 append blank 命令之后可加一句如下命令:

replace 部门 with '电脑'

然后在数据录入的表单中将部门设为指读,使录入者不能修改其内容。

GETFILE() 函数

显示一个获取文件名的对话框,并返回所选取的文件名。

语法

GETFILE([文件扩展名] [,提示信息] [,确定按钮的标题]

[,按钮状态])

返值类型

字符型

参数描述

文件扩展名:指定在对话框中显示的文件扩展名。比如您只需要选取 bmp 图形文件,那么您就可以用 getfile('bmp'),在对话框的文件选取区(搜索)就只显示扩展名为 bmp 的文件和目录,文件类型区则显示 bmp,表示现在可供选择的文件只有 bmp,不过您也可以在这将文件类型改为全部文件。

如果是空字符串或省略,则所有文件都显示出来供选择。

扩展名中也可以用通配符 * 和 ?。

还可以加扩展名说明,比如:getfile('位图文件:bmp'),那么在文件类型处就会显示“位图文件”以代替“bmp”。 也可以有多个扩展名,其中有分号(;)隔开,比如:

getfile('dbf;idx;cdx')

这样在文件类型中就可以有多种文件类型供选择。

提示信息:您可以在对话框中输入文件的地方放上您自己的提示信息,比如:

getfile('bmp','输入文件名')

不过要注意的是提示信息只能有10个字符,也就是5个汉字。

确定按钮的标题:在对话框中有一个确定按钮,按下此按钮后,该函数就返回所选定的文件名,包括路径,您可以在该按钮上放上您喜欢的字符,比如:

getfile('bmp','输入文件名','OK')

158

按钮状态:该参数可以设为0、1、2,0与省略是一样的。设为1,对话框中将会有一个“新建”按钮,按下此按钮,该函数将会返回“untitle”文件名,扩展名则根据文件类型而定。设为2,会有一个“无”按钮,按手册上说按下这个按钮将会返回空字符串,但实际上与设置为1时是一样的,不知是怎么回事。

说明

如果在对话框中按了 Esc 键、按了“取消”按钮或者按了右上角的关闭按钮,那么函数将返回空字符串。

另有一个函数 getdir() 用于获取目录名,用法与此函数相似。

还有 getpict(),用于获取图片文件,其实就相当于 getfile('bmp;ico')。

getprinter() 用于获取打印机。

示例

我们在用其它的软件时经常会碰到要求输入文件名的情况,这时往往在输入文件名的文本框边上有一个“浏览”窗口,按下此按钮就弹出文件选择对话框,可在其中选择文件名,按确定按钮后可将文件名自动填到文件输入的文本框中,有了 getfile() 函数,您也可以做出这样的程序来了。

比如我们有一个存放图片文件的表,需要将硬盘上的一些图片文件放入表中,那么就可以用 getfile() 获取图片文件名,再将它用 append general 命令放数据表的通用字段。

方法就是设一输入文件名的文本框,假设是 text1,为其设一个 controlsource 变量,比如fname,在文本框旁边放一个按钮,其 click 事件中写入如下语句: fname=getfile('bmp')

thisform.text1.refresh

然后可用另一个“存盘”按钮将该文件存入表中。 有时操作者可能不用这个浏览按钮,而直接输入文件名,但这样就可能输错文件名,如果根据错误的文件名存

入表中,就会造成程序出错,为了避免这种情况可在 append general 语句前用 file() 函数检查一下这个文

件是否存在,如果不存在则提示操作者重新输入文件名,比如“存盘”按钮中的程序可以是这样:

if file(fname)

append general 图片 from &fname

注意 else

message(fname+'文件不存在,请重新输入!',48+0+0,'注意')

thisform.text1.setfocus

endif

在有些软件中为了减少操作出错,干脆将输入文件名的文本框设为只读,甚至将 enabled 设为 .f.,只能通过

按钮来选择文件名。

这样您编的软件又会与操作者友好很多了。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

IIF() 函数

159

根据条件返回指定的值。(光看这句话肯定不明白:-(,接着往下看吧)

语法

IIF(逻辑表达式, 表达式1, 表达式2)

(能明白点了吗?还不明白就再往下看)

返值类型

表达式1、2是什么类型就是什么类型。(越看越糊涂 &:-(

参数描述

逻辑表达式:如果此表达式为真,该函数就返回表达式1,否则返回表达式2。

比如:

? iif(a=1,'正确','错误')

如果 a 等于1,就显示“正确”,否则就显示“错误”。

(这下明白了吧 ^o^)

聪明的读者一定会想到,用下面这段程序也可以实现上面的功能: if a=1

? '正确'

else

? '错误'

endif

但很显然用 iif() 函数简单得多,而且程序运行的速度也快。另外在有些地方它是不可替代的,请看下面的示例。 示例

我们在前面人事档案的那个软件的 rsda 表中是以逻辑值来表示性别的,那么在 grid 中显示出来时也是以 T 或 F 来表示性别的,这显然不是个友好的界面,用这个函数就能解决问题。

(先别看下面,自己想想看能否知道怎么做)

另外在报表、标签中也可以用这个方法,将域控件的表达式设为上面那样来打印性别。

在这种地方就没有办法用 if 语句来代替了。

LEFT() 函数

160

返回指定字符串左边的某几个字符。

语法

LEFT(字符表达式,数值表达式)

返值类型

字符型

参数说明

返回字符表达式中左边开始的若干字符。

数值表达式:指定返回的字符个数。

如果指定的数值大于字符表达式的字符个数,则全部返回;如果为0,则返回空字符串。

说明

另有一个函数 right(),则返回右边开始的若干字符。

还有一个函数 substr(字符表达式,数值表达式1,[数值表达式2]),用于返回字符表达式中从第几个(数值表达式1指定)字符开始的若干字符(数值表达式2指定),如省略数值表达式2或大于剩余的字符数,则返回其后的所有字符。 left('abcde',3) 实际上就相当于 substr('abcde',1,3),返回值都是 abc。

另外还有一个 leftc() 函数,如果字符中有中文,并想返回完整的中文字,最好用这个,比如:

? left('中华人民共和国',1)

那么什么都没有显示,因为只返回半个“中”字,如果:

? leftc('中华人民共和国',1)

就会返回完整的“中”字。

当然,right 和 substr() 也都有 rightc() 和 substrc() 与之对应。

示例

下面是一个应用 left() 和 right() 函数的示例,用于在程序中设置 EXE 所在目录为隐含目录: *设置隐含路径

CXLJ=SYS(16) &&获取当前运行程序的目录及文件名

*很显然下面需要将 CXLJ 中的文件名去掉,那么就剩下所要的路径了

FOR JSQ=1 TO LEN(CXLJ) &依次从右至左检测每一个字符直到找到右边第一个反斜线“”

*最后一个(也就是右边第一个)反斜线后面的内容就是文件名了

CXZF=LEFT(RIGHT(CXLJ,JSQ),1) &&将右边的一部分字符取出,看其第一个字符是否“”

*在循环中随着 JSQ 的增大,取的右边部分逐渐增多

IF CXZF='' &&一旦找到反斜线

161

CXLJ=STUFF(CXLJ,LEN(CXLJ)-JSQ+1,JSQ,'') &&用 stuff() 将反斜线之后的字符删除

EXIT

ENDIF

ENDFOR

SET DEFAULT TO "&CXLJ" &将 CXLJ 中的路径设为隐含路径

*用引号的目的是为了路径中有空格也能正确执行。

要注意的是这个程序只能放在 prg 程序中使用,不能放在诸如表单的 init 事件中来使用,因为在表单中 sys(16) 返回的不是路径和表单文件名。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

LEN() 函数

返回字符串的长度。

语法

LEN(字符表达式)

返值类型

数值型

参数描述

该函数即返回字符表达式的字符数,一个汉字算两个字符,比如:

len('庄稼abc')

返回7。

说明

另有一个函数 lenc(),它将汉字看成是一个字符,还是上面那个例子:

lenc('庄稼abc')

则返回5。

示例

在程序中我们可能常常需要用 wait window 来给出提示信息,有时提示的信息会比较长,我们可以将其分成多行显示,方法是在字符中间加 chr(13),即回车符。为了在程序中调用方便,我们可以编一个子程序,由程序自动去判断显示的信息是否要分行,如果要则自动分行,程序如下:

*程序名:wprom.prg

parameters cha &&接收上级程序传过来的字符串

if len(cha)>20 &&如果字符数大于20则分行

clen=len(cha)

162

lines=ceiling(clen/20) &&计算所需要的行数

dimension chas(10) &&定义所要的字符串变量,一行一个

*由于有半角字符的原因,不能定有多少行,但一般不会超过10行

lcha='' &&最后用于显示的字符变量,由 chas 数组元素加 chr(13) 组合而成 counter=1

do while len(cha)>0 &&利用循环,如果分行后剩余的字符仍然大于20将继续分行 chas(counter)=leftc(cha,10)

*考虑到提示大多数中文因此以汉字来处理,这样可避免半个汉字的出现 clen=len(chas(counter)) &&取出当前行的字符数

*因为可能会有半角字符的情况,所以取出的不一定是20的长度

cha=stuff(cha,1,clen,'') &&用 stuff 函数将已取的字符从 cha 中删除

lcha=lcha+chas(counter) &&将取出的当前行加到 lcha 中

if len(cha)>0 &&如果不是最后一行就加上回车符

lcha=lcha+chr(13)

endif

counter=counter+1

enddo

wait window lcha nowait

else

wait window cha nowait

endif

调用程序用类似如下语句:

do wprom with 提示的字符串

LIKE() 函数

确定两个表达式是否相似。

用等号不是也可以实现相同功能吗?

那可不一样,这个函数可以用通配符,比如:

? like('??????爱好者*','FOXPRO爱好者的天堂')

将返回 .T.,怎么样?不错吧。

语法

LIKE(字符表达式1, 字符表达式2)

返值类型

逻辑型

如果两个表达式相似就返回“真”,否则返回“假”。

参数描述

163

字符表达式1:可以有通配符。

字符表达式2:不可以有通配符。

说明

set compatible 确定两个表达式结尾空格的比较方式。如果是:

set compatible on 或 set compatible db4

则将结尾空格删除再比较。

如果设置为:

set compatible off 或 set compatible foxplus

结尾的空格也将参与比较。

另有一个函数 likec(),它将汉字当成一个字来比较,比如:

like('中?','中国')

返回 .f.,而

likec('中?','中国')

返回 .t.。

示例:

如果您想查一个单位叫“佳帆网络科技信息有限公司”的人,但记不清楚名称了,只记得中间有“网络”和“信息”,那么您就可以用如语句来查:

locate for like('*网络*信息*','单位')

当然您也可以用如下语句:

locate for '网络'$单位

locate for '信息'$单位

但这样可能会找出许多相同的,使得进一步查找困难,而用 like() 虽然不能排除相同的,但命中率会高很多。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

LOOKUP() 函数

在当前表的指定字段中查找信息。

有点类似 LOCATE 命令。

语法

164

LOOKUP(返回值所在的字段,被查找的内容,查找的字段[,索引名称])

比如:LOOKUP(部门,'庄稼',姓名)

如果表中有“庄稼”这个人,所在记录的部门字段为“电脑部”,那么该函数就会返回“电脑部”。

返值类型

可以是任何类型

参数描述

返回值所在的字段:当查到所需要的记录后该函数返回指定字段中的内容。如果查找不成功,即没找到,那么将返回与该字段长度相等的空数据,比如字符型就返回空格,空格数与字段长度相等。

被查找的内容:不用说了吧。

查找的字段:也不用说了吧。如果该字段有同名索引是打开的,系统将自动使用索引。使用索引会使查找快很多(废话)。 索引名称:如果索引的名称与字段名不同,可用这个参数指定,一般可以省略。必须是复合索引。

说明

该函数不能由 RUSHMORE 优化。不过您只要为查找的字段建立了索引,实际上也就相当于优化了。

另有一个函数:

SEEK(被查找的字符表达式 [, 工作区 | 别名 [, 索引序号 | 索引文件名 | 索引标识名]])

用法与此类似。

示例

我们在编软件的时候常常会有这种情况,在一个文本框中输入一个产品的代码,输入完后一回车,在另一个文本框中会显示出其产品名称,用这个函数来实现就可以实现这样的功能,当然这首先必须有一个代码、名称的对照表。

比如我们想在 text1 输入代码,在文本框 text2 中显示名称,那么我们可以在 text1 的 lostfocus 事件中写入: select dmb &&选择代码、名称对照表

*下一句将查到的名称所对应的代码赋给text2

thisform.text2.value=lookup(名称,alltrim(this.value),代码)

thisform.text2.refresh

当然您也可以用如下程序: select dmb &&选择代码、名称对照表

locate for 代码=alltrim(this.value)

if found()

thisform.text2.value=名称

endif

thisform.text2.refresh

165

很显然,用 lookup() 的程序简洁一些。

但是,如果是下面这种情况就不能完全照搬第一种形式: select dmb &&选择代码、名称对照表

locate for 代码=alltrim(this.value)

if found()

thisform.text2.value=名称

thisform.text3.value=型号

endif

thisform.text2.refresh thisform.text3.refresh

为什么呢?因为如果照搬,程序就要写成如下这样:

select dmb &&选择代码、名称对照表

thisform.text2.value=lookup(名称,alltrim(this.value),代码)

thisform.text3.value=lookup(型号,alltrim(this.value),代码)

thisform.text2.refresh thisform.text3.refresh

这样电脑就将执行两次查询,很不经济,如果是一个有很多记录的表,这会使程序运行变慢,要是再增加几个项目,比如颜色、规格等,就更不堪设想了。

折衷的办法:

select dmb &&选择代码、名称对照表

thisform.text2.value=lookup(名称,alltrim(this.value),代码)

thisform.text3.value=型号

thisform.text2.refresh

thisform.text3.refresh

为什么这样可以呢?因为 lookup() 找到记录后同样会把记录指针定位到找到的记录上,这一点同 locate 是一样的,找不到也会定位在表结尾。

MESSAGEBOX() 函数

显示一个用可自定义的对话框。

常用作提示之用,也可以作一些简单的选择,比如“确定”、“取消”等,程序中经常用到的一个函数。

语法

MESSAGEBOX(提示信息[,对话框的属型[,对话框窗口标题]])

返值类型

数值型

166

参数描述

提示信息:对话框中所用到的提示文字。

对话框的属型:用于确定对话框的按钮、图标等属性,这是一个数值型的参数。这可是个复杂的东东,请听我慢慢道来: 设置按钮属性 值

1

2

3

4 5 对话框按钮属性 仅有一个“确定”按钮。 有“确定”和“取消”按钮。 有三个按钮,分别是“终止”、“重试”、“忽略”。 “是”、“否”和“取消”按钮。 “是”和“否”。 “重试”和“取消”。

比如:

messagebox('是否真的要退出系统?',4)

如果省略这个参数就相当于是0。

设置图标(照表格中的命令试一下就知道了:)

16

32

48 64 图标 messagebox('你不能再这么干下去了!',16) messagebox('有什么问题吗?',32) messagebox('可得小心了!',48) messagebox('跟您说点事。',64)

如果又想要图标,又想要“是”、“否”两个按钮,怎么办呢?这么办:

messagebox('是否真的要退出系统?',4+32)

上一句您也可以写成:

提个醒 messagebox('是否真的要退出系统?',36)

(4+32=36谁还不知道,废话),不过为了程序易读,最好还是写成“4+32”。

设置隐含按钮

256

512

隐含按钮 第一个按钮。 第二个按钮。 第三个按钮。 167

比如:

messagebox('是否真的要退出系统?',4+32)

这一句,您希望显示对话框时,隐含的按钮为“否”,也就是按下“回车”键即代表“否”,那么就写成如下形式: messagebox('是否真的要退出系统?',4+32+256)

对话框窗口标题:显示在对话框窗口上部,那个蓝色区域内的信息。比如:

messagebox('是否真的要退出系统?',4+32+256,'注意')

如果设的数大于按钮的数,比如设为512,但只有两个按钮,则隐含还是第一个按钮。

说明

在对话框中按了不同的键,该函数将返回不同的值,键、值对应如下: 值

1 2

3

4

5

6

7 键 确定 取消 终止 重试 忽略 是 否

这样我们就以根据不同的返回值作不同的处理了。

该函数缩写就为 MESSAGEB(),而不是通常的4个字母。

对话框弹出时还会有提示声。

示例

请看下面的程序:

tc=messagebox('是否真的要退出系统?',4+32+256,'注意')

if tc=6

close tables all

clear events

*还可以加上其它各种退出时的清理工作

cancel

endif

知道是怎么回事吧。该程序一般可作成一个子程序,由菜单的“退出”选项来调用。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

RAND() 函数

168

返回一个0到1之间的随机数。

语法

RAND([种子值])

返值类型

数值型

参数描述

种子值:用于设定随机数出现的序列(一定没看明白),也就是说您第一次调用此函数代入一个种子值,那么其后对不代值的调用此函数,其返回值是按照一定序列出现,比如您运行如下命令:

? rand(1)

返回值将会是:0.03

其后您再调用:

? rand()

返回值一定是:0.25

之后一定是按照:0.71, 0.48, 0.02, ...这样的序列出现。 疑问 那这是不是说该函数返回的并不是真正的随机数呢?不能这样说,当您第一次使用某个种子值时,谁也不知道它

会以怎样的序列出现,虽然说这个序列是已经固定了的,而且下一个数与上一个数之间没有任何规律可寻。

所以您现在再去运行我们以前编过的那个幸运7游戏,您会发现只要是启动软件后第一次出现的数一定是8、5、9,第二次一定是8、2、7,也就是说第二次您一定赢,因为系统隐含是以100001作为种子值。

那么怎样才能避免这种情况呢,以一个负数作为种子值,多少都没有关系,系统将会自动以系统当前的时间为种子值,由于这个值是精确到秒数,所以谁也无法预料会出现什么值,比如您在幸运7程序中将 rand() 全部改成 rand(-1),那就不能保证第二次一定赢了,其实您不需要每个都改为 rand(-1),只需要在进入系统时,比如菜单的设置代码或表单的 init 事件中加上如下代码:

rand(-1)

其它程序中仍用 rand(),出来的数一样是难以预料的,这就是种子的意义,只要播下的第一个种子不知道是怎样的,那就谁也不知道会结出什么样的果来。

示例

下面有一个供下载的程序,是用于按电话号码抽奖的,基本思路是:将一些电话号码放入数据表,产生一个从1到记录总数之间的随机,那么随机数是几,第几个号码就被抽中。

抽中的记录做个记号,下次如再抽到不算,重抽,直到抽中未抽过记录的。

如果全部记录都抽过了,则将所有记录都重新设为未抽,以便可以再次重抽。

169

产生随机数的程序为: zjjl=round(rand()*reccount(),0) &&产生随机数,随机数的最大值与记录数相等

if zjjl=0 &&如果随机数为0

zjjl=1 &&就将设为1

*这好象有点不太合理,因为这样第1个记录的抽中概率就要大一些,想想有没有什么办法改进

endif

RAT() 函数

返回一个字符串中某个字符最后一次出现所在的位置。

这句话一下不容易看明白,举个例:

? rat('d','abcde fdcba')

即返回 'abcde fdcba' 中 'd' 字符最后一次出现所在的位置,也就是 f 和 c 之间的那个 d(不是 c 和 e 之间的那个 d),而它在字符中所处的位置是第8个字符(从左边数),故该函数返回 8(中间的空格也算一个字符)。

语法

RAT(被搜索的字符, 搜索字符所在的字符串 [, 到数出现的第几个字符])

返值类型

数值型

参数描述

前面两个参数不用讲了吧,讲也讲不清楚,看了上面那个例子我想大家都能明白了。

要讲的是最后那个参数,即到数出现的第几个字符,先看一下这个例子:

? rat('d','abcde fdcba',2)

返回值是 4,即到数第二个 d 所在的位置,如果省略这个参数,就默认到数第1个。

因此在开头我们对这个函数所下的定义是不准确的,可准确的定义怎么下呢?我想即使能说出来,谁也看不懂,不信您试试。 说明

如果在字符串中没有找到所要的字符,那么该函数将返回0,比如:

? rat('d','abcde fdcba',3)

就返回0,因为没有第3个 d。

该函数在查找时是区分大小写的。

170

另有一个函数 AT(),它查找某个字符第一次出现所在的位置,比如:

? at('d','abcde fdcba')

返回4。当然您也可以指定第二次出现所在的位置,比如:

? rat('d','abcde fdcba',2)

返回8。

对应的还有两个函数 ATC() 和 RATC(),这两个函数在查找时不区分大小写,注意它们与不带 C 的并不是中文和英文的区别,和前面我们讲过的一些函数不同,比如:

?at('r','FOXPRO爱好者的天堂')

返回 0。

?atc('r','FOXPRO爱好者的天堂')

返回5。

示例

我们在讲 left() 函数时举过一个设置隐含路径的例子,程序如下: *设置隐含路径

CXLJ=SYS(16) &&获取当前运行程序的目录及文件名

*很显然下面需要将 CXLJ 中的文件名去掉,那么就剩下所要的路径了

FOR JSQ=1 TO LEN(CXLJ) &依次从右至左检测每一个字符直到找到右边第一个反斜线“”

*最后一个(也就是右边第一个)反斜线后面的内容就是文件名了

CXZF=LEFT(RIGHT(CXLJ,JSQ),1) &&将右边的一部分字符取出,看其第一个字符是否“”

*在循环中随着 JSQ 的增大,取的右边部分逐渐增多

IF CXZF='' &&一旦找到反斜线

CXLJ=STUFF(CXLJ,LEN(CXLJ)-JSQ+1,JSQ,'') &&用 stuff() 将反斜线之后的字符删除

EXIT

ENDIF

ENDFOR

SET DEFAULT TO "&CXLJ" &将 CXLJ 中的路径设为隐含路径 *用引号的目的是为了路径中有空格也能正确执行。

如果用 rat() 函数,可将这段程序优化,如下:

*设置隐含路径

CXLJ=SYS(16)

fnp=rat('',cxlj)

cxlj=left(cxlj,fnp)

SET DEFAULT TO "&CXLJ"

171

除了程序精练了很多外,由于没有了循环,一次就能找到最右边的反斜线,程序执行的速度也会快很多。虽然现在的电脑的速度都很快,这点提高也许不明显,但我们应养成良好的编程习惯,即让代码运行的效率尽量高,一个地方加快一点也许并不起眼,但很多地方加在一起可能效果就不同了,有时即使一个地方的代码由于编的方法不同,可能导致运行所需的时间相差很远。

所以当我们编一段代码时,不光要考虑它是否能达到我们的目的,而且要考虑它的执行效率。要想效率高,就要多从不同的角度来考虑问题,再就是要尽量多的掌握各种命令和函数,并能灵活运用它们,而且要多看高手们的程序,从中学习各种技巧,开拓自己的思路。

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

REPLICATE() 函数

将一个字符串重复多次后返回。

比如:

? replicate('庄稼',3)

显示为:庄稼庄稼庄稼

语法

REPLICATE(字符串,重复的次数)

返值类型

字符

参数描述

好象不需要说了嘛。

说明

返回字符的长度仅受内存的限制,也就是说只要您的内存足够大,就可以任意长的字符串,您可以将一个字符重复1万次都行。

另有一个函数:

SPACE(次数)

用于返回一定数量的空格,等同于:

REPLICATE(' ',重复的次数)

示例

当我们在文本框中需要限制输入字符的长度时,我们就在其 INPUTMASK 属性中输入所需要长度的“X”,比如只准输入4个字符,就输入 XXXX (不需要加引号)。

如果我们要限制的长度是40,那要打40个“X”可就太麻烦,尤其在一个程序中可能很多地方可能都要做这样的设制,于是我们就可以用到这个函数,在其中输入:

172

=replicate('X',40)

注意由于这是一个表达式,故前面要加等号,否则就会乱套。

STRCONV() 函数

用于双字节字符(全角)与单字节字符(半角)的转换。

语法

STRCONV(字符表达式, 转换方式)

返值类型

字符型

参数描述

字符串:需要转换的字符表达式。

转换方式:指定是从单字节转换到双字节,还是从双字节转换到单字节,1为单到又,2为双到单。

示例

对于使用中文版的软件往往有这么一个问题,就是该输入半角字符的时候输入了全角字符,比如地址中的门牌号,在录入时如果不小心录入了全角,那么查询时用半角就查不到,或者录入时虽然是半角,但在查找时却用全角来查询,同样也查不到,现在有了这个函数就能很容易地解决这个问题了,比如录入的地址数据存在 addr 这个变量中,那么用如下语句来存数据表就可避免录入了全角:

replace 地址 with strconv(dz,2)

strconv() 函数会将其中的全角数字、字母等转换为半角,而汉字任保持不变,棒吧!

查询时,要查询的信息放在 seekcont 变量中,用如下语句,即使查询信息中有全角字符也可以查到:

locate for 地址=strconv(seekcont,2)

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

STRTRAN() 函数

将一字符串中的某个字符串用另一个字符串替换,也可以替换备注型字段中的字符串。

语法

STRTRAN(主字符串, 被替换的子字符串 [, 用于替换的字符串]

[, 开始替换的位置] [, 替换的次数])

返值类型

字符型

173

参数描述

主字符串:其中的字符将被替换。

被替换的子字符串:指定要被替换的字符。

用于替换的字符串:被替换的字符串将被替换成该字符串。

开始替换的位置:指定从第几个被替换的字符串处开始替换,即在此之前的,虽然符合被替换的子字符串也不被替换。可以省略,如省略,则从第一个开始。

替换的次数:指定替换的次数,即替换次数够了,其后虽然符合被替换的子字符串也不被替换。可以省略,如省略,则从开始处到最后的所有符合的都将被替换。

示例

a='上有天堂,下有天堂,真正的天堂在 FOXPRO 爱好者的天堂'

? strtran(a,'天堂','美景')

显示:上有美景,下有美景,真正的美景在 FOXPRO 爱好者的美景

? strtran(a,'天堂','美景',2,2)

显示:上有天堂,下有美景,真正的美景在 FOXPRO 爱好者的天堂。即从第2个“天堂”开始,只替换2个“天堂”。 说明 该函数并不能直接替换备注型字段中的内容,比如“简历”为备注型字段:

strtran(简历,'大学','大专')

注意 只是将当前记录的简历中的内容提取出来,将其中的“大学”全部替换为“大专”,也就是说该函数的值是替换

后的内容,但该值并没有自动传回给字段,字段中仍是原来的内容,想要替换字段中的内容怎么办呢?很简单,只须如此这般:

replace 简历 with strtran(简历,'大学','大专')

前面我们还讲过一个 CHRTRAN() 函数,与此有点类似,区别是 CHRTRAN() 用于单个字符的替换,但一次可以替换多个不同的字符;而 STRTRAN() 可替换一个字符串,但一次只能替换其中的某个字符串,如要替换几个不同的字符串,则要进行多次替换。

STUFF() 函数

将一字符串中的某一部分用另一个字符串替换掉。

语法

STUFF(原字符串, 开始替换的位置, 被替换的字符数, 用于替换的字符串)

返值类型

174

字符型

参数描述

原字符串:其中的某一部分将被替换。(解释似乎比被解释的更难懂)

开始替换的位置:指定从原字符串的第几个字符开始替换。

被替换的字符数:指定从开始处连续有几个字符要被替换掉。 绝招 如果此数为0,那么“用于替换的字符串”将插入到开始处。

用于替换的字符串:该字符串将代替被替换掉的字符串放到原字符串当中。(不知是语言的悲哀还是我的悲哀) 绝招 如果此为空字符串,被替换的字符将被删除。

示例

STORE 'abcdefghijklm' TO gcString1

STORE '12345' TO gcString2

CLEAR

? STUFF(gcString1, 4, 0, gcString2) && 插入

? STUFF(gcString1, 4, 5, gcString2) && 替换

? STUFF(gcString1, 4, 6, '') && 删除

? STUFF(gcString1, 4, 3, gcString2) && 替换3个,其它的就插入

? STUFF(gcString1, 4, 7, gcString2) && 替换5个,还有两个没东西替换的就删掉

? STUFF(gcString1, 4, LEN(gcString1), gcString2) && 替换5个,其余全删掉

这最后一个可能要动下脑筋想想为什么。

想看看运行的结果是怎样的吗?那就自己试试吧,您可以在命令窗口中一句一句的试。

说明

仔细对比一下 STUFF() 与 CHRTRAN()、STRTRAN() 的区别。

另有一个 STUFFC() 函数,它把汉字当一个字符看待,这样不会出现半个字符的现象,而 STUFF() 则把汉字当两个字符来处理,您可以比较一下如下两句:

?stuff('FOXPRO 爱好者的天堂',9,1,'a')

?stuffc('FOXPRO 爱好者的天堂',9,1,'a')

[dvnews_page=VFP 中级教程:第十六课 更多编程函数]

SUBSTR() 函数

从一个字符串或备注型字段中截取一部分返回。

SUBSTR(字符表达式, 开始截取的位置 [, 截取的长度])

返值类型

175

字符型

参数描述

字符表达式:将从这个字符表达式(或字符串)中截取。

开始截取的位置:指定从第几个字符开始截取。

如果开始位置大于字符的总长度,在 set talk on 的情况下将返回错误信息,如 set talk off,则返回空字符串。 截取的长度:指定截取多少个字符,如省略,则从开始到最后。

说明

在调试窗口中,该函数不会直接对备注型字段起作用,如要使用备注型字段,需将备注型字段名放入 ALLTRIM() 函数中,再将 ALLTRIM() 放入 SUBSTR()。

比如:substr(alltrim(简历),1,20)

另有一个 SUBSTRC() 函数,这个函数将汉字当一个字符处理,可避免截取到半个汉字的情况。

示例

? substr('FOXPRO 爱好者的天堂',8,6)

显示:爱好者

VFP 中级教程:第十七课 数组在应用

第十七课 数组在应用

数组实际上就是一组变量,它们有同样的名称,但有不同的下标,也可以叫编号,比如:a(1)、a(2)、a(3)……,这些变量叫做数组的元素。

这些变量可以同一般变量一样使用,比如:

a(1)='庄稼'

replace 姓名 with a(1)

那么用数组有什么好处呢?当我们要对一组变量进行处理时,就会体现出它的好处了,比如我们想将某个数据表中连续的若干记录中某个字段的内容放入变量,如果不用数组程序就会是这样 a1=姓名

skip

a2=姓名

skip

a3=姓名

skip

...

176

显然这是很麻烦的,尤其需要的变量很多时,而最主要的是,如果变量的个数是不定的,那就几乎难以实现。

而用数组就可以解决问题,假设需要100个变量,程序就可以是这样: dimension a(100) &&定义数组的个数

for counter=1 to 100

a(counter)=姓名

skip

endfor

*counter 根据循环从1到100,即完成 a(1)=姓名、a(2)=姓名、……、a(100)=姓名。

*并且每循环一次记录向下跳一个,也就将连续100个记录的“姓名”字段的内容赋给了100个变量。

而代表数组个数的100也可以用变量,即:

dimension a(ac) &&定义数组的个数

for counter=1 to ac

a(counter)=姓名

skip

endfor

这样只要在每次定义数组个数前给 ac 不同的值就可以实现数组个数是可变的了。 从上面的程序中可以看出,数组在使用之前必须定义,且要说明数组中元素的个数。定义的命令是:

注意

DIMENSION 数组名1(元素个数),数组名2(元素个数),...

二维数组

我们上面讲到的是一维数组,即一个元素由一个下标来确定,那么二维数组就是由两个下标来确定一个元素,比如:a(1,1)、a(1,2)、a(1,2)、a(2,1)、a(2,2)……。

二维数组有什么好处呢?比如还是上面的例子,但我们需要把“姓名”和“电话”都放到变量中,我们就可以这样来做: dimension a(100,2) &&定义数组的个数

for counter=1 to 100

a(counter,1)=姓名

a(counter,2)=电话

skip

endfor

如果不用二维数组而用一维数组,那做起来会很麻烦,不信您自己试试看,另外后面使用起来也很不方便。

实际上上面这个二维数组就相当于一个具有2列、100行的表,a(1,1) 是第一个人的姓名、a(1,2) 是第一个人的电话、a(2,1) 是第二个人的姓名、……,使用起来非常直观。

二维数组也可以当一维数组来用,对于一个2列、100行的二维数组,即:

多学一招

a(1,1) 可看作是 a(1),a(1,2)=a(2)、a(2,1)=a(3)、a(2,2)=a(4)、a(3,1)=a(5)、……

177

数组相关命令 数组相关函数

178

爱华网 www.aIhUaU.com欢迎您转载

  

爱华网本文地址 » http://www.aihuau.com/a/390251/249680372200.html

更多阅读

Tomcat安装及配置教程 tomcat怎么下载安装

Tomcat安装及配置教程——简介用来进行web开发的工具有很多,Tomcat是其中一个开源的且免费的java Web服务器,是Apache软件基金会的项目。电脑上安装配置Tomcat的方法和java有些相同,不过首先需要配置好java的环境才行。Tomcat安装及配

声明:《vfp编程实例及提高 vfp教程 VFP编程入门到精通教程 vfp教程》为网友诗化丶相逢分享!如侵犯到您的合法权益请联系我们删除