本章说明 本章共分十一节。阐述了与VFP工作环境有关的细节问题,其中一至七节为一般描述适用于一般使用者,第八、九两节适用于中、高级开发者,第十节是四个具体的实例,可用于任何应用程序,第十一节是"选项"对话框的说明性表格,是手册性质的资料。 第一节 概述 工作环境规定了 VFP 工作时的某些行为。配置工作环境意味着您可以通过某些方法使 VFP 的行为满足自己的需要 - 必须的或者是某些个人偏好的。 与任何完善的开发工具一样,VFP 也是一个复杂的系统,复杂的一个方面就表现在它具有极大的灵活性 - 同一种工作,由于"环境"的不同,可能会产生不同的效果。 工作环境是个整体的概念,我们使用"环境参数"一词来描述构成工作环境的个体。那么就可以说,工作环境就是由一组环境参数来决定的,配置工作环境就是设置这组环境参数,以满足不同的需求。这显然有两方面的任务,第一、您要知道有那些环境参数,第二、明确怎样配置这些参数。 工作环境涉及到很多方面,例如输入输出、数据处理、开发、调试等等。可以通过分类对所有环境参数进行描述。所有的环境参数都有一个原始的默认值,一般的环境配置工作,事实上只需要修改少量的环境参数即可。 有一个问题需要明确,"VFP 的工作环境"是指 Visual FoxPro 工具的开发环境。另外一种情况,使用 VFP 开发的应用程序,在编译成执行文件后,工作在 VFP 运行时的工作环境下。这两者是有区别的,显而易见的区别是,运行时环境涉及的问题比开发环境少得多,例如开发、调试方面的问题。一般来讲,运行时的环境是开发环境的一个子集,但严格说来这是不对的,二者仍存在一些差别,不仅在环境参数的类别与数量方面,也存在于配置方法上。本文的叙述主要针对开发环境而进行,对二者存在的差别也将被提及。 [dvnews_page=VFP 高级教程:VFP 开发技巧] 第二节 默认的工作环境 VFP 安装完成后,其"内部"保存着对所有环境参数的设置,这些参数可以称做是原始设置。您永远也不能够去修改这些原始设置。但 VFP 提供一种机制允许您每次启动时用自己的设置去覆盖这组原始设置。方法是是使用 Windows 注册表及配置文件。 启动时,VFP 首先会检查 Windows 注册表中的有关设置,然后据此修改环境参数。然后还会查找一个配置文件,该文件默认的名字为 config.fpw,其中也存放着一些环境配置信息。VFP 将读取这些信息,并再次修改环境参数。如果 VFP 在Windows 注册表中没有找到配置信息或者没有找到配置文件,这没有关系,VFP 将以原始配置进行工作。您甚至可以主动让 VFP 在启动时放弃读取注册表及配置文件。 VFP 不会主动把环境参数写进 Windows 注册表中,但它提供了一种方法使您方面地将环境参数写进注册表,写入不仅针对修改过的参数,而是所有参数,您没有去修改的参数值将以原始值写入。配置文件则不同,它只保存您写入的参数,某种意义上,它相当是一份注册表中的环境参数表的勘误。 保存在 Windows 注册表中的环境参数以及配置文件中的设置称做"默认工作环境" 第三节 配置文件 先读取注册表再查找配置文件的方法似乎有点复杂,但有些时候需要这样做,例如工作在运行时环境的应用程序,启动时不去读取注册表,这使得配置文件成为必须。另外,配置文件创建于 FoxPro 的早期版本,兼容性使它必须被保留。VFP6 以前的版本在安装时将自动建立一个这样的文件(在安装目录下),而到了 VFP6,系统不再自动建立该文件,需要时由用户自行建立。该文件是一个文本文件,可以使用任何文本编辑器来建立它。 VFP 启动时将按照下列路径查找该文件: 当前工作目录 安装 Visual FoxPro 的目录 DOS 路径中列出的目录 配置文件有默认的名字(config.fpw),"默认"意味着你可以去改变它。这有利于您同时拥有多个配置文件,以便满足不同的需要。指定另外不同于config.fpw名字的配置文件的方法是在启动 VFP 时增加命令行开关项,例如 VFP6.EXE -Cc:\myprg\myconfig.fpw 配置文件中不仅仅存放环境配置信息,例如可以在配置文件中设置 VFP 启动后立即去执行一个程序文件。这使得您可以在配置环境方面使用一种技巧,即将环境设置命令保存在一个程序文件中,然后让 VFP 启动时去执行它。 启动过程也可以通过使用命令行参数来定制,通过定制启动过程,可以得到不同的环境默认设置,以下是对环境配置有影响的一些情况:
[dvnews_page=VFP 高级教程:VFP 开发技巧]
第四节 更改默认设置 VFP 启动完成后,工作环境被设置成默认状态。您可以即时地更改环境参数,这有几种方法,各种方法可能适合于不同的情况。 交互式修改 在命令窗口输入环境设置命令,执行后该修改会立即生效。绝大部分的环境参数是通过 SET 命令设置的,另外还有一些通过系统变量存储以及极少数的函数设置。在早期的XBASE 工具中,例如dBASEII中,仅有 20 几条 SET 命令,而今天,VFP 中已经有 120 多个 SET 命令了。交互式的修改方法是枯燥的,因此只用于临时性的修改,例如调试等。 程序化执行修改 可以将一组环境设置命令组织成一个程序文件,运行该程序,就相当于在命令窗口一一输入执行了这组命令,有些情况下是需要这样做的,例如在开发环境下同时调试多于一个项目的时候。可以将该环境设置程序存为一个过程或直接写在项目的主文件中。事实上,对于应用程序,因为最终要脱离开发环境运行,所以大部分都是这样做的。 用命令方式读取Windows 注册表中的设置 让 Visual FoxPro 再次读取自己的注册表设置,并且使用当前的注册表设置更新自己。 语法为: SYS(3056 [,1]) 说明:包含1选项可以只根据注册表设置进行更新,不包括 SET 命令和文件位置。Visual FoxPro 在 Windows 注册表的选项对话框中保存设置。当Visual FoxPro 启动时,读取这些设置,并利用这些设置进行配置。SYS(3056)可更新包含有文件名和路径的系统变量,而系统变量由SET命令和注册表设置。当您选择设置为默认值时,选项对话框指定的设置写入注册表。其中一些影响Visual FoxPro环境的设置含有SET 命令,例如SET BELL,SET LOCK等。注意 SYS(3056)不更新来自Config.fpw(即 Visual FoxPro 配置文件)的设置。 有关环境设置的命令 有很多环境设置的命令,在本文的附表中,以"选项"对话框中的分类列出了相应的环境设置命令,还有一些命令没有包含在"选项"对话框中,但它们也是关于环境的设置命令,本文仅将它们列出来,具体的意义及语法可参见随机文档。
无论是交互式还是程序化执行命令,其修改都是临时的,只对当前工作有效,VFP 系统关闭后,这种修改不能被保存,下一次运行,如果需要同样的设置,您不得不再一次输入该命令或执行该程序。要想使修改成为永久,必须将修改保存在 Windows 注册表中或构建配置文件。 修改注册表 有两种方法:直接修改 Windows 注册表或使用 VFP 开发环境提供的"选项卡"对话框。对于直接修改 Windwos 注册表的方法,没有人会建议您这样做,除非您首先对 Windwos 注册表的结构非常熟悉,其次对 VFP 在 Windows 注册表中的各种参数的意义比较了解,第三有某些特殊需要以至于不能使用常规的方法完成。常规的方法,是使用 VFP 的"选项卡"对话框来完成环境配置。 直接修改注册表 VFP 环境设置信息在 Windows 注册表的目录如下: HKEY_CURRENT_USER\Software\Microsoft\VisualFoxPro\6.0\Options 主键名称在本文附表中有详细介绍。 使用"选项"对话框 使用"选项卡"对话框来进行系统的配置比直接修改 Windows 注册表的方法至少有两个好处,第一,很多配置参数在"选项卡"上是以一种"易于理解的方式"表达的,您只需在某些"问题"前打勾或者在很多答案中选择一个,这就象您填一份表格一样来得轻松。第二,在"选项卡"上进行的配置,一旦完成后,立即生效,而直接修改 Windows 注册表的方法却不是这样,只能是在下一次运行 VFP 时生效。 在开发环境下,执行"工具"菜单中的"选项"菜单项,会出现"选项"对话框。对话框上有12个卡页,分12个方面对环境进行配置。本文附表中详细描述了其中的每一项设置。事实上,真正需要修改的并不多,很多项设置,原始值应该能满足要求。现在的问题是,当你修改完成后,可有两种选择,按"确定"按钮退出对话框,您的修改只会保存在当前环境中,随着系统的关闭而失效;按"设置为默认值"按钮退出对话框,当前的设置将会被写进 Windows 注册表从而成为新的默认值。 第六节 ON命令 ON ERROR 命令 设计得再好的程序,在运行时也不可避免地会发生错误,这些错误可能是程序自身的错误,也可能是系统环境引起的或是用户错误地操作(如错误地移动/删除文件等)引起的等。 因此,程序员有责任编写出可以捕捉错误的程序并尽可能地处理这些错误。要捕捉程序中发生的错误使用 ON ERROR 命令。你可以在 ON ERROR 命令后跟随一个错误处理程序的名字:ON ERROR DO ERRORHANDLER,或者在 ON ERROR 命令后跟随一条赋值语句:ON ERROR glError = .T.。 注意,在程序中全程使用类似于ON ERROR glError = .T.的命令是极不负责任的和令人憎恶的,这有可能会使用程序陷入死循环而使用用户不得不强行退出系统(强行关断电源等),这样做极有可能破坏用户的数据文件。 ON ESCAPE 命令 指定在程序或命令运行过程中,按下 ESC 键时所执行的命令。 语法为:ON ESCAPE [Command] ON SHUTDOWN 命令 指定当试图退出 Visual FoxPro 时所要执行的命令。 语法为:ON SHUTDOWN [Command] 第七节 一些重要的设置 以下是几乎每一个应用程序都要进行的设置: _VFP 系统变量 指向当前运行的Visual FoxPro应用程序对象。 语法 参数描述 eValue 指定属性的值。Method指定应用程序对象的方法。 说明 通过_VFP,您可以访问对象集合(Objects collection)。 可设置的属性有:
SET SAFETY 决定改写已有文件之前是否显示对话框,或者决定当用表设计器或用 ALTER TABLE 命令对表结构进行修改后,是否重新计算表或字段规则、默认值以及错误信息。 语法为:SET SAFETY ON | OFF SET PROCEDURE TO 设置运行时的命令文件 SET CLASSLIB TO 设置运行时的类库文件 SET MEMOWIDTH TO 指定备注字段和字符表达式的显示宽度。 语法为:SET MEMOWIDTH TO nColumns SET MULTILOCKS ON 决定能否使用 LOCK ()或 RLOCK () 锁定多个记录。 语法为:SET MULTILOCKS ON | OFF 若要在程序中使用表缓存必须设置SET MULTILOCKS 为 ON。 SET HELP TO 激活或废止 Visual FoxPro 联机帮助或指定的帮助文件。 语法为 SET HELP ON | OFF - 或者 - SET HELP TO [FileName] SET DELETED 指定 Visual FoxPro 是否处理标有删除标记的记录,以及其他命令是否可以操作它们。 语法为:SET DELETED ON | OFF SET EXCLUSIVE 指定 Visual FoxPro 在网络上以独占方式还是共享方式打开表文件。 语法为:SET EXCLUSIVE ON | OFF SET NOTIFY 确定是否显示某种系统信息。 语法为:SET NOTIFY ON | OFF SET BELL 关掉或打开计算机铃声,并设置铃声属性。 语法为: SET BELL ON | OFF - 或者 - SET BELL TO [nFrequency, nDuration | cWAVFileName, nDuration] SET NEAR FIND 或 SEEK 查找记录不成功时,确定记录指针停留的位置。 语法为:SET NEAR ON | OFF SET EXACT 指定比较不同长度两个字符串时,Visual FoxPro 使用的规则。 语法为:SET EXACT ON | OFF SET CONFIRM 指定是否可以用在文本框中键入最后一个字符的方法退出文本框。 SET ESCAPE 决定是否可以通过按 ESC 键中断程序和命令的运行。 语法为:SET ESCAPE ON | OFF SET DATE 设置日期格式 语法为:SET DATE [TO] AMERICAN | ANSI | BRITISH | FRENCH | GERMAN | ITALIAN | JAPAN | USA | MDY | DMY | YMD SET CENTURY 设置是否在日期的年中显示世纪值 语法为:SET CENTURY ON | OFF ON SHUTDOWN DO 指定当试图退出 Visual FoxPro 时所要执行的命令。 语法为:ON SHUTDOWN [Command] 第八节 进一步订制VFP的向导和生成器 作为最好的数据库管理系统,VFP 给我们提供了高度的可自定义的交互式的开发环境(interactive development environment IDE)。你可以修改菜单,安装新的生成器和向导,实现开发工具条,修改项目管理器的行为,以及很多其它事来使你的IDE 更高效。这些定制甚至比我们所知道的和喜欢的VFP语言更好,连相对不熟炼的VFP 开发者都可以按他们自己的方法定制开发环境。 本节将配合实际程序示例,向你展示如何建立简单的工具增加你和你的开发组的效率。我们将着眼于修改VFP 的向导和生成器来提供额外的或自定义功能,并使用VFP 6中的BuilderD 技术创建你自己的生成器。 修改VFP 的向导和生成器 如果你象我一样,你可能不常用VFP 的生成器和向导,因为它们不完全能满足你的需要。可能它们不具备足够的灵活性或可能你只是不喜欢他们的工作方式。直到VFP 6以前,没有办法改变生成器和向导的行为,因为Microsoft 没有提供源代码。但是,现在我们不但获得了生成器和向导的源代码,也获得了类浏览器,组件管理器的源代码。 生成器和向导的源代码可以在VFP主目录下的TOOLS\XSOURCE目录中的XSOURCE.ZIP 文件中找到。当你解压该文件时,它建立一个VFPSOURCE 目录,其中包含了所有的源代码。向导的源代码 可以在WIZARDS 目录下找到,生成器的在BUILDERS目录下 (虽然一些WIZARDS目录中的公共文件也被生成器使用)。 因此,现在我们有了源代码,我们可个按我们的需要来修改生成器和向导,对吗? 好了,一个较好的办法是建立新的生成器和向导时,使用源代码中的大多数类和程序,但以派生子类并复制和修改PRGs 或建立封装PRGs的方法来忽略他们原有的行为。本文将详细说明如何对各生成器和向导这样做。 一但你建立了你自己的生成器和向导,怎样告诉VFP 使用你的而不是原来的吗? 生成器是注册在BUILDER.DBF 且向导是在WIZARD.DBF中,两个表都在VFP主目录下的WIZARDS 子目录中。这些表具有相同的结构,如下表所示。
表 1. BUILDER.DBF 和 WIZARD.DBF的结构 当你调用一个生成器时,VFP 调用_BUILDER 系统变量中指定的程序 (默认是BUILDER.APP)。BUILDER.APP 查看它所在的环境 (例如,生成器是被那一个对象调用),在BUILDER.DBF 表中查找与环境匹配的记录 (例如,在TYPE 字段中查找对象的基类),并调用注册后的生成器。除系统变量_WIZARD外,向导也是以相同的方法处理,_WIZARD是用于指向可在WIZARD.DBF找到的WIZARD.APP。 要告诉VFP 使用一个不同的生成器或向导,插入一条说明如何运行你的生成器或向导的新记录到BUILDER.DBF或 WIZARD.DBF表中。如果你想在运行新的向导或生成器的同时也可以选择运行原有的生成器或向导,在WIZARD.DBF表中保留原有的生成器或向导记录。如果你想替换原有的,简单的修改它的TYPE值为另外的值(我使用一个"X"前缀,如 "XGRID"),而不是删除该记录; 使用该方法,你可以简单的以恢复TYPE 的值来恢复使用原来的生成器或向导。 我们将考查创建一个Grid 生成器的替代物,参照完整性生成器,Upsizing 向导,和WWW 搜索页向导。 创建更有用的Grid 生成器 VFP Grid 生成器提供了一种快速方法来整合grid 的列并建立你所希望的视觉外观。但是,关于该生成器,有我所不喜欢的一些东西:
要建立一个VFP Grid 生成器的替代物,首先建立了SFGRIDBLDR 项目(它在你解压该文档所附的示例程序时建立的GRID 子目录中) 并添加以下文件: BUILDER.VCX (在VFP 向导源目录中的BUILDERS\BUILDERS 子目录中),GRIDBLDR.VCX (在 BUILDERS\GRIDBLDR 目录中),THERM.VCX,WIZCTRL.VCX,WIZMOVER.VCX (所有都在WIZARDS\WZCOMMON目录中),DUMMY.PRG,和 WBGRID.PRG (在BUILDERS目录中)。我如何知道要添加那些文件到项目中呢? 这很简单:只需要查看GRIDBLDR 项目的内容。 其次,派生GridBuilder 类到SFGridBuilder (在 SFGRIDBLDR.VCX类库中) 并复盖ResetColumns 方法,该方法用于调整选定列的宽度到适当的大小 (我只实现了该想法,未实现上述列表中的其它两个)。该方法的代码在下面列出。这里要注意两个有趣的东西。首先,我希望写很多复杂的代码,根据字段宽度、字体和字号来计算出列的宽度,等等。有趣的是,它放弃了生成器中已存在的任务的方法 (SetColWidth),而只是传递字段宽度到该方法中,生成器传给它一个不同的值。其它事情是分配到loColumn。Width 的计算宽度被注释了。理由是我还没有调试好,改变列宽后,当生成器关闭时,会造成问题,所以我把列宽设置为,列宽是保存在wbaCols中。(如果你需要,wbaCols 是一个公共数组。我没有创建生成器,因此不要责怪我采用这种方式) 因此,效果是当一个字段添加网格中时,它没有立即获得适当的列宽,但是一但你执行了一些其它操作 (添加另一个字段,转到生成器的另一个页面中,关闭生成器等等。)它就会取得适当的列宽。
最后我为我的生成器建立了GRIDMAIN.PRG (使用了BUILDERS\GRIDBLDR 中的GRIDMAIN.PRG 的办法) 并使它成为项目的主文件。该程序用SET CLASSLIB命令添加SFGRIDBLDR.VCX 到打开的类库中,这样它可以找到我们的SFGridBuilder 类。GRIDMAIN 也自动注册它自己到VFP BUILDER 表中(如果是直接运行该APP 文件)。 要查看该生成器的行为,建立并运行SFGRIDBLDR.APP 来作为grids生成器注册它。如果你在运行该APP后,观察观查BUILDER.DBF,你会发现它是"未注册的" ,原始的grid 生成器的 TYPE 字段由"GRID" 改成了"XGRID" 并在它后面为它自己添加了一条TYPE 字段为"GRID"的新记录。下一步,新建一个表单,拖动grid 到表单中,并调用生成器。新的生成器看起来与原来的没有任何区别,但是当你添加字段到表格时,你会发现字段宽度自动调整到了适当大小。 创建一个更好的参照完整性生成器 我有一小点关于VFP自身的参照完整性(RI)生成器的问题:
修正这些项相对容易些,因为我们有RI 生成器的源代码。我建立了SFRIBUILDR.APP,一个 VFP RIBUILDR.APP的替换物。我没有实现附加的规则 (上面提及的最后一项) 但是我处理了其它的所有项。我首先建立了SFRIBUILDR 项目(它在解压后的程序的主目录下的RI 目录中) 并添加了VFP的RIBUILDR.VCX(在VFP向导源程序所在目录下的BUILDERS\RIBUILDR 目录中)。 接着,我派生VFP RI生成器类到 SFRIBUILDR.VCX 类库中的SFRIBuildr。我忽视(overrode)了Load 方法以便在数据库中有被删除的记录时不会发生错误 (你可以参见我注释的已存在的代码)。我忽视了确认按钮的Click 方法,以便不出现确认对话框和不复制当前的储存过程到RISP.OLD中,并修复了生成的代码中的错误。同时,如果它检查到NEWRI.PRG 存在于你的系统中 (与SFRIBUILR.APP在同一目录),它会把该代码放入数据库的储存代码中而不会生成其它代码。这个修改需要一个新的方法 RIMakeNewTr用一个不同的名字建立触发器(__RI_Handler) 而不是原来的RI 生成器的生成的(__RI_<action>_<table>,如__RI_Delete_Customer)。因为这些方法中有大量的代码,在这里就不写出来了; 但是,如果你查看提供的源代码,你会看见我确实只注释了很少的代码行,并添加了很少的代码。 最后,我从BUILDERS\RIBUILDR目录中复制RIMAIN.PRG 到我的生成器所在的目录,添加它到项目中,并使它面为主程序。我修改了该程序所指向的类库,使它能找到我的VFP RI 生成器类的子类所在的类库 (SFRIBUILDR.VCX) 并自动注册该生成器到VFP BUILDER 表中(如果是直接运行该APP)。 生成并运行SFRIBUILDR.APP 导致该文件作为RI生成器注册取代了通常的RIBUILDR.APP。要看这一点,按以下步骤进行:
创建一个更好的升迁(Upsizing) 向导 Jim Falino 在使用升迁向导时失败了,因此他创建了一个具有他所希望的行为的版本。
Jim 的修改是让用户指定他们的要求; 你可以使用它们如果你的需要与之相同,或将他们作为模板使用以便你自己修改。 创建一个更好用的WWW 搜索页向导 在 "Visual FoxPro 6 企业开发" (Prima Tech,ISBN 0-7615-1381-7)一书中,Rod Paddock 和 John Petersen 描述了如何修改WWW 搜索页向导来取消只能输出5个字段到一个网页的局限。胜于修改向导,你会希望派生它们。要这样做,用我派生生成器类的相似的方法。WWW 搜索页向导的代码在VFP 向导源目录下的WIZARDS\WZINTNET 目录中。 第九节 创建你自己的向导 Rod Paddock 和John Petersen 也在他们的书中也提供了一些使用VFP 向导类来建立你自己的向导的细节。这样做的优点是,有人已经建立了所有的"引擎"来管理向导处理,且你的向导将与其它的VFP向导具有相同的感观。 用BuilderD 创建生成器 在查看VFP6中的FoxPro基本类时(FFC),我注意到一件事是它们都具有叫做Builder和BuilderX的自定义属性,并且各个类的BuilderX 是设置为= HOME() + "Wizards\BuilderD,BuilderDForm"。我知道这些属性的作用(他们告诉VFP,这个类的生成器名),但为什么每一个类都要指定相同的生成器呢?更有趣的是,各个类以不同的选项调用类似的生成器表单(图1显示_HyperLinkLabel 类的生成器)。当使用相同的类时将会发生什么情况呢?
首先介绍一点背境。可能正象你可能意识到的,VFP 生成器可能以多种方式调用,但最常见的是可能是在对象上右击鼠标并从出现的菜单上选择生成器。这会使BUILDER.APP 被执行。BUILDER.APP 检查是否选定的对象(我们称为"目标对象") 具有 BuilderX 属性,如果有,运行该属性指定的程序或实例化该该属性指定的类(如果生成器是一个类,将指定一个类库,逗号及类名)。如果它没有 BuilderX 属性但有一个 Builder 属性,生成器运行该属性指定的程序或实例化该该属性指定的类(我们将看到为什么会同时用两个属性指定生成器)。如果两个属性都不存在,将使用该对象基类的默认生成器。 你可以在你的类中建立自定义的 Builder 和 BuilderX 属性(甚至在你的基类中) 然后为每一个类填写适当的生成器名字。要使用两个属性的理由是 BuilderX 为该类指定一个自定义生成器 而Builder为一组公共的类(如comboboxe 或 grid)指定一个所需的生成器。正如我们稍后会看到的一样,我们可以在 BuilderX 属性中指定的生成器中,单击一个按钮来调出在 Builder 属性中指定的生成器。 因此,我们可以很容易地在 Builder 和 BuilderX 属性中指定生成器。而不是花很多时间来建立你自己的生成器,特别对于不常使用的类。 BuilderD 你可能意识到了 BuilderB 技术 用来建立生成器是容易的和快速的。BuilderB 是一组你可以派生来建立你自己的生成器的类。你可以为你想用生成器维护的目标对象的各属性添加一个控件到生成器子类。虽然BuilderB 使得建立生成器更容易,但你必须为每一个你想用生成器管理的类建立一个新的生成器子类。对于我们这样的懒人来说,幸运的是,Ken 以数据驱动的方式增强了BuilderB。这种新技术叫做BuilderD ,"D" 的意思是"动态" (Dynamic),可以在Ken 的网站(www.classx.com)找到,也包含在VFP 6 中(在VFP 主目录下的WIZARDS 目录中的BUILDERD.VCX)。 BuilderD 由一系列的类组成,但主要的一个是BuilderDForm; 这是一个数据驱动生成器表单 (注意这是VFP6.0的基本类中指定的BuilderX类)。正如你在图2中看到,该表单中的文本框用于输入目标类的名字和类名,按钮提供的功能可以调出类浏览器和显示帮助,一个页框、两个页面,但没有用于管理属性值的控件。
以下是该表单在实例化时,如何组合适当的控件:
这些步骤的结果是,一个生成器可以管理目标对象的一个或更多的属性。一个指定的生成器可以事实上做得比这些还要多,例如放置代码到目标对象的方法或对象的容器中,介是目前我们只使用最简单的方法。 BuilderD 表 让我们进一步了解生成器定义表BUILDERD.DBF,因为理解它的结构是建立你自己的生成器的关键。表2 显示了BUILDERD.DBF表的结构; 在该表中,"property control" 意思是生成器中的、维护一个目标对象的指定属性的控件。
表 1. BUILDERD.DBF的结构 表2 和 3 显示了两个生成器的记录,VFP6.0的基本类_HyperLinkBase 和_HyperLinkLabel 类。我没有显示BUILDERD.DBF 表中的所有字段,出于空间的考虑; 仅列出与讨论有关的字段。
表 2. _HyperLinkBase 和 _HyperLinkLabel 类的生成器记录
表 3. _HyperLinkBase 和 _HyperLinkLabel 生成器管理的属性定义记录 在表2 中的 _HyperLinkBase记录中,我们看见CLASSNAME 和 CLASSLIB 指定了该生成器使用的类(注意CLASSLIB 包含了一个在运行时求值的表达式而不是一个硬编码值),在LINKS 中列了出该生成器可管理的类的属性。显示在表3中的cTarget PROPERTY 记录指明该属性将被BuilderComboBox 控件管理,且该BuilderComboBox的RowSource 包含一个 Microsoft VFP 网站的URL (目前是msdn.microsoft.com/vfoxpro)。cFrame 将被 BuilderTextBox 对象管理(因为它是一个字符串属性且该类没有定义),lNewWindow 将有一个BuilderCheckBox 对象(因为它是一个逻辑属性)。 看起来是如此简单,对吧? 好了,LINKS 字段事实上可以更加复杂。首先,假如在LINKS字段中指定的类记录的ID 没有匹配记录,该ID 就被假设为属性名。 这样,你可以简单的在CLASS记录的LINKS字段中指定它管理的属性来实际建立一个生成器。当然,你必须保持属性的 captions 和属性的名字相同,没有状态条文本、默认的类和属性大小,但这些对于一个快速的和dirty 生成器来说都不算太坏。 第二个复杂之处是,CLASS记录的LINKS字段中指定的ID 可以指向另一个CLASS 记录而不是一个PROPERTY 记录。在这种情况下,该类 "继承"所有指定类的连接。你可以在表2中的_HyperLinkLabel 记录中看到这一点; 它的一个连接是_HyperLinkBase,因此它不仅用该类的生成器来管理Caption 属性(specifically 在它的 LINKS 字段中列出),同时也管理cTarget、cFrame和lNewWindow 属性,因为这些在_HyperLinkBase 的LINKS 字段中指定。 第三,PROPERTY 记录可以连接到另一个PROPERTY 记录。在这种情况下,记录"继承"连接记录的所有非空字段。最后,如果你用@<caption> 格式指定它,LINKS 可以包含生成器中的页框的标题 (例如,@Properties 使用"Properties" 作为页的标题)。 创建一个生成器 让我们在TEST.VCX类库中创建一个名为TestCheck的 VFP CheckBox 的子类作为开始。在该类中添加一个叫做BuilderX 的自定义属性并设置它的值为= HOME() + "Wizards\BuilderD,BuilderDForm"。然后在该类上右击鼠标并从快捷菜单中选择生成器。我们得到一条"没有注册该类型的生成器"的错误。这也说得通,因为我们还没有定义它 (尽管这令人不愉快,在稍后它将自动为我们建立一个)。按以下步骤做:
酷不酷,嗯? 完全是你自己的生成器,仅在一分钟左右就建立了! 去掉Enabled 的选择并输入一个不同的Caption,并可看到这些属性已被修改。 注意不要被为属性建立记录所迷惑; Enabled 和 Caption 记录已存在于 BUILDERD.DBF表中,因此我们只需重用它们,而且由于不存在 AutoSize 记录,BuilderD 只假设我们想管理那个属性。 让我们再建立一个并看看以多小的输入可以得到一个可工作的生成器。在TEST.VCX类库中创建一个名为TestText的 VFP TextBox 的子类,向它添加一个属性 BuilderX 并设置它的值为 = HOME() + "Wizards\BuilderD,BuilderDForm"。在BUILDERD.DBF 中添加一条记录并只指定 TYPE ("CLASS"),ID ("TestText"),LINKS ("ReadOnly")和 CLASSNAME ("TestText";我们可以跳过 CLASSLIB 但 CLASSNAME 是必须的)。关闭该表,然后调出该类的生成器。 这也是一个可以工作的生成器。单击生成器中的生成器按钮; 它调出另一个生成器(如果Builder属性存在并非空,在Builder属性中指定的生成器,否则就是该类的基类的默认生成器,在此例中是 VFP TextBox 生成器)。这样,即使我们可以为一个类指定生成器,只要愿意,我们仍然可以访问更多的普通生成器。 预置-内建的生成器 我最近用BuilderD 建立了两个生成器,一个为我的SFGrid 类另一个为SFPageFrame。SFGrid 生成器维护DeleteMark 和RecordMark 属性(你可以很容易地添加其它你可能经常修改的属性) 但由于它们容易在属性窗口中修改,所以这不是我要建立该生成器的真正的理由。真正的理由是因为我喜欢使用grid 的columns 中的SFGridTextBox 对象(SFTextBox 的一个子类,具有一小部分不同的属性设置,以提供它们在 grid中出现的外观),并尽量用SFGridTextBox对象替换一般的TextBox 对象。 因此我建立了一个按钮(SFBUILDERS.VCX类库中的SFGridTextBoxButton类) 来自动进行该工作。以下是该按钮的 Click 方法代码:
BuilderDForm 有一个引用到目标对象保存在它的 oObject 属性,因此生成器表单上的许多控件可以引用或修改目标对象的东西。在这些代码中,Thisform.oObject.Columns 引用grid 的Columns 集合被生成器影响。 这里有一个副作用:我怎样才能在我的生成器表单中得到该按钮? 我可以为我的生成器在BUILDERD表中建立一个记录,但那会添加按钮到页框中的一个页上,而我确希望该按钮象其它的生成器按钮一样出现。 因此我建立了一个 "button loader" 类 (SFBUILDERS.VCX类库中的SFBuilderButtonLoader类),该类添加一个按钮到生成器表单然后返回 .F. 这样,它实际上没有实例化。SFBuilderButtonLoader 在BUILDERD表的当前记录的备注字段 USER 中查找(使用类实例化的记录) 要添加到表单的按钮的类名和类库(注意下面代码中的 BUILDERD 是以 "BUILDER"别名打开的)。对于SFGridTextBoxButton,例如,我在BUILDERD表中建立一条记录,以 "SFGridTextBoxButton" 作为它的 ID 但 "SFBuilderButtonLoader" 是一个类,并在USER字段中输入 "SFBuilders, SFGridTextBoxButton"。这告诉 SFBuilderButtonLoader 添加一个 SFGridTextBoxButton 到表单中。以下是SFBuilderButtonLoader的 Init 方法代码:
在BUILDERD表中 SFGrid 记录的LINKS字段中有 DeleteMark、RecordMark 和 SFGridTextBox,因此该生成器获得这些控件。 现在我可以为我的SFGrid对象调出我的BuilderD 生成器,使用 VFP Grid 生成器(单击BuilderD表单上的生成器按钮) 来快速建立 grid中的列,然后单击我的添加SFGridTextBox按钮来让这些列使用 SFGridTextBox 对象。 对于 SFPageFrame,我也希望相似的功能:一个添加代码到页框中各页的RightClick方法的按钮; 这允许在页面上右击鼠标明时为页框或整个表单提供快捷菜单。象SFGrid 生成器一样,我在BuilderD表中建立了一个记录来实例化一个 SFBuilderButtonLoader 对象,然后添加一个 SFCodePageButton 对象到生成器表单。以下是添加代码到各页的SFCodePageButton的Click方法:
要添加这些新的生成器到你的系统中,复制 SFBUILDERS.VCX 和 VCT (在本文档的源代码文件中可以找到) 到一个目录,然后打开示例文件提供的NEWBUILDERS 表,并修改 CLASSLIB 字段到包含路径的 SFBUILDERS.VCX文件名。打开 BUILDERD 表,然后 APPEND FROM NEWBUILDERS 表,添加Builder 和BuilderX 属性到你的类中,并在BuilderX中输入 = HOME() + "Wizards\BuilderD,BuilderDForm"。 其它生成器类 我早些时候注意到,SFBUILDERS.VCX 中的BuilderCheckBox 和 BuilderTextBox 类都是派生自同一个BuilderD 类。在附加的右击鼠标行为中,这两个类具有nTop 和 nLeft 属性,当这些属性改变时,用assign 方法设置它们的自定义的lMoved 属性为.T.。这允许我们查觉到什么时候一个属性控件被移动了(在属性控件生成器中的Left 和Top 是绑定至 nLeft 和 nTop 而不是直接绑定到 Left 和 Top)。只有当一个属性控件移动时,我们保存它的its Left 和 Top 值到 BUILDERD 表。 SFBUILDERS.VCX 中的加一个类是 SFPropertyCaption。该控件用于管理属性控件的 caption。 为什么用一个特别的类来做这件事? 为什么不用一个普通的BuilderTextBox 对象? 理由是如果属性控件是一个BuilderCheckBox,它有一个 Caption 属性,因此控件管理该属性。但是,如果属性控件是一个 BuilderTextBox,它没有 Caption 属性但确有一个组合的 label 对象,因此控件必须管理对象的对象的 Caption 属性。由于 BuilderD 类只管理单个对象的属性,SFPropertyCaption 不得不超越(override)少量方法来允许它处理这种情况。 SFBuilderPropertyComboBox 类是BuilderComboBox的一个子类。它提供一个目标对象的所有可写属性的一个列表(用 AMEMBERS() 得到所有属性的列表然后检查各自的PEMSTATUS()来排除只读的)。虽然它的主要目的是修改属性控件的 cProperty 属性(用于检查要管理的属性),它有两个有趣的行为。 首先,如果属性控件是一个BuilderTextBox 但你只修改了它管理的逻辑属性(如 Enabled),它移去BuilderTextBox 及组合的label 对象,并放一个 BuilderCheckBox 在这个地方。如果你改变了一个逻辑属性为另一个类型它会做相反的事。 第二,如果你输入了一个不存在的属性名,你会被提示建立该属性。如果你同意,生成器使用AddProperty 来添加新属性到目标对象。我的确不推荐在一个拖放到表单中的对象上这样做,因为这等于实例化编程; 如果你真的需要一个新属性,你可以考虑用子类来代替。 总之,这是一个经一个步骤在类中建立新属性并用生成器管理它的快速方法。 SFBuilderAddButton 是一个为了属性控件使用 SFBuilderButtonLoader 类而添加到生成器表单的按钮类 (派生自BuilderCommandButton)。SFBuilderAddButton 的Click方简单地调用属性控件所在的生成器表单的 AddPropertyControl 方法来建立一个新的属性控件,然后转换当前生成器表单来管理新属性控件。 这意味着你可以单击添加属性按钮添加一个新属性到生成器,并在生成器中管理它。这是一种添加多个属性控件的快速方法。 试一试 让我们看一看SFBuilderBuilderForm; 它实际上比对它的描述更易于使用。在TEST.VCX中建立一个叫做 MyTestText 的VFP TextBox 类的子类。添加一个 BuilderX 属性并设置它的值为 "SFBuilders,SFBuilderBuilderForm",然后调出该类的生成器。 注意尽管我们没有在BUILDERD 表中建立任何记录,我们仍然得到了一个生成器表单(当然,表单上没有控件,但我们马上会改变这一切)。 这是因为 SFBuilderBuilderForm 在不能找到该类的记录时,自动地为该类在BUILDERD表中建立了一个 CLASS 记录。 单击添加属性控件按钮。你会注意到一个文本框和标签出现在生成器表单上,但随后另一个生成器表单出现在该生成器表单的面上。这个新的表单是我们刚添加的属性控件的生成器。在属性 combobox中,选择 "Tooltiptext" 并修改 Caption 为 "Tool Tip Text" 和 Width 到250。移动该生成器表单在一旁并注意原表单上的属性控件也同样修改了。 单击第二个生成器表单上的添加另一属性按钮并注意另一个属性控件已添加到原生成器表单中,并且该生成器表单现在可以管理它了。为属性选择"Statusbartext" 并修改Caption 为"Status Bar Text" 和Width 到250。添加另一个属性控件,并为该属性选择 "Readonly" 并修改Caption 到"Read-Only"; 这时,注意原生成器属性控制改变了checkbox。关闭新的生成器表单。图3 显示了我们建立的生成器,图4 显示了属性控件生成器。
要修改控件的属性,在控件上右击鼠标并从快捷菜单中选择修改属性修改控件; 你以前看到过的相同的属性控件生成器会出现。要移去属性控件,从菜单中选择移去属性控件。要重置目标对象的该属性的值为默认值,选择重置为默认值。 让我们修改生成器表单的标题为更为合适的东西。单击修改生成器标题按钮并在随后出现的SFBuilderBuilder生成器表单中输入 "我的测试文本框类生成器" 。关闭第二个生成器。 如果你在关闭生成器时没有保存,下一次你再调用 MyTestText 对象的生成器时,你的设置不会起作用。若要保存生成器的定义到 BUILDERD 表中,单击保存生成器按钮。如果你想导出生成器定义到另一个表中,单击导出生成器按钮并在出现的对话框中输入文件名。然后你可以发送该表给一些人,他们可以将其导入他们的 BUILDERD 表并访问你建立的生成器。 说明 因为VFP的开放式IDE,任何人都可以很容易地定制它们的开发环境,因此他们和他们的开发组可以极大地提高编程效率。 第十节 示例 例1:在程序开始时设置系统环境 *-- 设置工作目录 cCurrentProcedure = SYS(16,1) nPathStart = AT(":",cCurrentProcedure) - 1 nLenOfPath = RAT("\", cCurrentProcedure,2) - (nPathStart) SET DEFAULT TO (SUBSTR(cCurrentProcedure, nPathStart, nLenOfPath)) SET PATH TO PROGS,LIBS,MENUS,HELP,INCLUDE,FORMS,BITMAPS *-- 修改程序标题并将屏幕放到最大,在编译成.EXE的文件运行时,这是必须的 MODIFY WINDOW SCREEN TITLE "测试" NOCLOSE ZOOM WINDOW SCREEN MAX *-- 屏蔽系统菜单 SET SYSMENU OFF *--定义用于保存系统设置的全局变量 PUBLIC ; GcOldTalk,; gcOldPath,; gcOldDir,; gcOldClassLib,; gcOldEscape,; gcOldSafety,; gcOldProc,; gcOldStatus,; gnOldMemo,; gcOldMultiLocks,; gcOldHelp,; gcOldDeleted,; gcOldExclusive,; gcOldNotify,; gcOldBell,; gcOldNear,; gcOldExact,; gcOldIntensity,; gcOldConfirm,; gcOldOnShutDown,; gcOldCompatible *--保存原始的系统状态,以便在退出时恢复 GcOldTalk = SET("TALK") gcOldPath = SET("PATH") gcOldDir = FULLPATH(CURDIR()) gcOldClassLib = SET("CLASSLIB") gcOldEscape = SET("ESCAPE") gcOldSafety = SET('SAFETY') gcOldProc = SET('PROCEDURE') gcOldStatus = SET('STATUS BAR') gnOldMemo = SET('MEMOWIDTH') gcOldMultiLocks = SET('MULTILOCKS') gcOldHelp = SET('HELP', 1) gcOldDeleted = SET('DELETED') gcOldExclusive = SET('EXCLUSIVE') gcOldNotify = SET('NOTIFY') gcOldBell = SET('BELL') gcOldNear = SET('NEAR') gcOldExact = SET('EXACT') gcOldIntensity = SET('INTENSITY') gcOldConfirm = SET('CONFIRM') gcOldOnShutDown = ON('SHUTDOWN') gcOldCompatible = SET("COMPATIBLE") gcOldDate = SET("DATE") gcOldCentury = SET("CENTURY") *-- 设置SET 和ON 命令 SET SAFETY OFF SET PROCEDURE TO UTILITY.PRG SET CLASSLIB TO MAIN SET MEMOWIDTH TO 120 SET MULTILOCKS ON SET HELP TO HELP\MAIN.CHM SET DELETED ON SET EXCLUSIVE OFF SET NOTIFY OFF SET BELL OFF SET NEAR OFF SET EXACT OFF SET INTENSITY OFF SET CONFIRM ON SET COMPATIBLE OFF SET DATE ANSI SET CENTURY ON SET ESCAPE OFF ON SHUTDOWN DO OnShutDown 注意:运行上述代码时程序文件UTILITY.PRG,OnShutDown,类库文件MAIN及帮助文件MAIN.CHM必须存在。 例2:在程序结束时恢复系统环境 LOCAL luTemp SET PATH TO (gcOldPath) luTemp = gcOldClassLib SET CLASSLIB TO &luTemp luTemp = gcOldProc SET PROCEDURE TO &luTemp SET MEMOWIDTH TO gnOldMemo luTemp = gcOldMultiLocks SET MULTILOCKS &luTemp luTemp = gcOldSafety SET SAFETY &luTemp luTemp = gcOldStatus SET STATUS BAR &luTemp CD (gcOldDir) luTemp = gcOldDeleted SET DELETED &luTemp luTemp = gcOldExclusive SET EXCLUSIVE &luTemp IF FILE(gcOldHelp) SET HELP TO (gcOldHelp) ENDIF luTemp = gcOldNotify SET NOTIFY &luTemp luTemp = gcOldBell SET BELL &luTemp luTemp = gcOldNear SET NEAR &luTemp luTemp = gcOldExact SET EXACT &luTemp luTemp = gcOldIntensity SET INTENSITY &luTemp luTemp = gcOldConfirm SET CONFIRM &luTemp luTemp = gcOldOnShutdown ON SHUTDOWN &luTemp luTemp = gcOldEscape SET ESCAPE &luTemp luTemp = gcOldCompatible SET COMPATIBLE &luTemp luTemp = gcOldTalk SET TALK &luTemp luTemp = gcOldDate SET DATE &luTemp luTemp = gcOldCentury SET CENTURY &luTemp CLOSE ALL CLEAR DLLS RELEASE ALL EXTENDED CLEAR ALL 例3:程序崩溃时恢复系统环境 在程序设计期间,有时会因程序错误而使系统崩溃。当系统崩溃时,可能会有打开的表没有关闭,数据工作期不能释放,表单在屏幕上不能清除,菜单没有释放等一系列令人心烦的事。当你要手工地恢复系统到初使状态真还要费不少事,但是你可以自己编写一个过程文件来自动完成这些工作。这样,当系统崩溃时,你只需在命令窗口打入 DO XXX 即可。 *:****************************************************************************** *: *: 过程文件C:\HIGHMIS\HF.PRG *: *:****************************************************************************** ON ERROR SET PROCEDURE TO POP KEY ALL SET COLOR TO SET SYSMENU TO DEFA CLOSE ALL RELEAS ALL EXTE CLEAR DLLS CLEAR EVENTS CLEAR FIELDS CLEAR GETS CLEAR MACROS CLEAR MEMORY CLEAR MENUS CLEAR POPUPS CLEAR PROGRAM CLEAR PROMPT CLEAR READ ALL CLEAR TYPEAHEAD CLEAR WINDOWS SET DEVICE TO SCREEN CLEAR SET EXCL ON SET TALK ON SET MESSAGE ON RELEASE WINDOW 常用 SET HELP TO SET HELP ON CANCEL 例4:查看系统设置 *:****************************************************************************** *: *: 过程文件C:\HIGHMIS\PROGS\SYSTEST.PRG *: * 用途:查看当前系统信息 ************************************************************************** PROCEDURE SYSTEST PARAMETERS T__Method, T__File RELEASE ALL EXCEPT T__* PRIVATE ALL LIKE T__* CREATE VIEW it SET TALK OFF CLOSE ALL SET SAFETY OFF SET ALTERNATE TO (IIF(EMPTY(T__File), "SysTest.TXT", T__File)) SET ALTERNATE ON SET SAFETY ON SET EXACT OFF T__IsVisual = .T. T__OutputFile = SET("ALTERNATE", 1) DEFINE WINDOW T__win FROM 0,0 TO SROWS(),SCOLS() TITLE T__OutputFile CLOSE SYSTEM ZOOM ACTIVATE WINDOW T__win ??"*** SysTest Version 2.00","日期",DATE(),"时间",TIME(),"***" T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="SET") WAIT WINDOW NOWAIT "*** SET() ***" ? ? "*** SET() ***" ?PADR("SET(ALTERNATE)",30) + " == ", SET("ALTERNATE") ?PADR("SET(ANSI)",30) + " == ", SET("ANSI") ?PADR("SET(AUTOSAVE)",30) + " == ", SET("AUTOSAVE") ?PADR("SET(BELL)",30) + " == ", SET("BELL") ?PADR("SET(BLINK)",30) + " == ", SET("BLINK") ?PADR("SET(BLOCKSIZE)",30) + " == ", SET("BLOCKSIZE") ?PADR("SET(BRSTATUS)",30) + " == ", SET("BRSTATUS") ?PADR("SET(CARRY)",30) + " == ", SET("CARRY") ?PADR("SET(CENTURY)",30) + " == ", SET("CENTURY") ?PADR("SET(CLASSLIB)",30) + " == ", SET("CLASSLIB") ?PADR("SET(CLEAR)",30) + " == ", SET("CLEAR") ?PADR("SET(CLOCK)",30) + " == ", SET("CLOCK") ?PADR("SET(COLOR)",30) + " == ", SET("COLOR") ?PADR("SET(COMPATIBLE)",30) + " == ", SET("COMPATIBLE") ?PADR("SET(CONFIRM)",30) + " == ", SET("CONFIRM") ?PADR("SET(CONSOLE)",30) + " == ", SET("CONSOLE") ?PADR("SET(CURRENCY)",30) + " == ", SET("CURRENCY") ?PADR("SET(CURSOR)",30) + " == ", SET("CURSOR") ?PADR("SET(DATE)",30) + " == ", SET("DATE") ?PADR("SET(DEBUG)",30) + " == ", SET("DEBUG") ?PADR("SET(DECIMALS)",30) + " == ", SET("DECIMALS") ?PADR("SET(DEFAULT)",30) + " == ", SET("DEFAULT") ?PADR("SET(DELETED)",30) + " == ", SET("DELETED") ?PADR("SET(DELIMITERS)",30) + " == ", SET("DELIMITERS") ?PADR("SET(DEVELOPMENT)",30) + " == ", SET("DEVELOPMENT") ?PADR("SET(DEVICE)",30) + " == ", SET("DEVICE") ?PADR("SET(DISPLAY)",30) + " == ", SET("DISPLAY") ?PADR("SET(DOHISTORY)",30) + " == ", SET("DOHISTORY") ?PADR("SET(ECHO)",30) + " == ", SET("ECHO") ?PADR("SET(ESCAPE)",30) + " == ", SET("ESCAPE") ?PADR("SET(EXACT)",30) + " == ", SET("EXACT") ?PADR("SET(EXCLUSIVE)",30) + " == ", SET("EXCLUSIVE") ?PADR("SET(FIELDS)",30) + " == ", SET("FIELDS") ?PADR("SET(FILTER)",30) + " == ", SET("FILTER") ?PADR("SET(FIXED)",30) + " == ", SET("FIXED") ?PADR("SET(FULLPATH)",30) + " == ", SET("FULLPATH") ?PADR("SET(HEADING)",30) + " == ", SET("HEADING") ?PADR("SET(HELP)",30) + " == ", SET("HELP") ?PADR("SET(HISTORY)",30) + " == ", SET("HISTORY") ?PADR("SET(HOURS)",30) + " == ", SET("HOURS") ?PADR("SET(INTENSITY)",30) + " == ", SET("INTENSITY") ?PADR("SET(KEYCOMP)",30) + " == ", SET("KEYCOMP") ?PADR("SET(LIBRARY)",30) + " == ", SET("LIBRARY") ?PADR("SET(LOCK)",30) + " == ", SET("LOCK") ?PADR("SET(LOGERRORS)",30) + " == ", SET("LOGERRORS") ?PADR("SET(MACKEY)",30) + " == ", SET("MACKEY") ?PADR("SET(MARGIN)",30) + " == ", SET("MARGIN") ?PADR("SET(MARK)",30) + " == ", SET("MARK") ?PADR("SET(MEMOWIDTH)",30) + " == ", SET("MEMOWIDTH") ?PADR("SET(MENU)",30) + " == ", SET("MENU") ?PADR("SET(MESSAGE)",30) + " == ", SET("MESSAGE") ?PADR("SET(MOUSE)",30) + " == ", SET("MOUSE") ?PADR("SET(MULTILOCKS)",30) + " == ", SET("MULTILOCKS") ?PADR("SET(NEAR)",30) + " == ", SET("NEAR") ?PADR("SET(NOTIFY)",30) + " == ", SET("NOTIFY") ?PADR("SET(ODOMETER)",30) + " == ", SET("ODOMETER") ?PADR("SET(OPTIMIZE)",30) + " == ", SET("OPTIMIZE") ?PADR("SET(ORDER)",30) + " == ", SET("ORDER") ?PADR("SET(PALETTE)",30) + " == ", SET("PALETTE") ?PADR("SET(PATH)",30) + " == ", SET("PATH") ?PADR("SET(PDSETUP)",30) + " == ", SET("PDSETUP") ?PADR("SET(POINT)",30) + " == ", SET("POINT") ?PADR("SET(PRINTER)",30) + " == ", SET("PRINTER") ?PADR("SET(PROCEDURE)",30) + " == ", SET("PROCEDURE") ?PADR("SET(REFRESH)",30) + " == ", SET("REFRESH") ?PADR("SET(REPROCESS)",30) + " == ", SET("REPROCESS") ?PADR("SET(RESOURCE)",30) + " == ", SET("RESOURCE") ?PADR("SET(SAFETY)",30) + " == ", SET("SAFETY") ?PADR("SET(SCOREBOARD)",30) + " == ", SET("SCOREBOARD") ?PADR("SET(SEPARATOR)",30) + " == ", SET("SEPARATOR") ?PADR("SET(SHADOWS)",30) + " == ", SET("SHADOWS") ?PADR("SET(SPACE)",30) + " == ", SET("SPACE") ?PADR("SET(STATUS)",30) + " == ", SET("STATUS") ?PADR("SET(STATUS BAR)",30) + " == ", SET("STATUS BAR") ?PADR("SET(STEP)",30) + " == ", SET("STEP") ?PADR("SET(STICKY)",30) + " == ", SET("STICKY") ?PADR("SET(SYSMENU)",30) + " == ", SET("SYSMENU") ?PADR("SET(TALK)",30) + " == ", SET("TALK") ?PADR("SET(TEXTMERGE)",30) + " == ", SET("TEXTMERGE") ?PADR("SET(TOPIC)",30) + " == ", SET("TOPIC") ?PADR("SET(TYPEAHEAD)",30) + " == ", SET("TYPEAHEAD") ?PADR("SET(UDFPARMS)",30) + " == ", SET("UDFPARMS") ?PADR("SET(UNIQUE)",30) + " == ", SET("UNIQUE") ?PADR("SET(ALTERNATE,1)",30) + " == ", SET("ALTERNATE",1) ?PADR("SET(CLOCK,1)",30) + " == ", SET("CLOCK",1) ?PADR("SET(CURRENCY,1)",30) + " == ", SET("CURRENCY",1) ?PADR("SET(DELIMITERS,1)",30) + " == ", SET("DELIMITERS",1) ?PADR("SET(FIELDS,1)",30) + " == ", SET("FIELDS",1) ?PADR("SET(HELP,1)",30) + " == ", SET("HELP",1) ?PADR("SET(MESSAGE,1)",30) + " == ", SET("MESSAGE",1) ?PADR("SET(PRINTER,1)",30) + " == ", SET("PRINTER",1) ?PADR("SET(RESOURCE,1)",30) + " == ", SET("RESOURCE",1) ?PADR("SET(TALK,1)",30) + " == ", SET("TALK",1) ?PADR("SET(TEXTMERGE,1)",30) + " == ", SET("TEXTMERGE",1) ENDIF T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="ON") WAIT WINDOW NOWAIT "*** ON() ***" ? ? "*** ON() ***" ?PADR("ON(ERROR)",30) + " == ", ON("ERROR") ?PADR("ON(ESCAPE)",30) + " == ", ON("ESCAPE") ?PADR("ON(KEY)",30) + " == ", ON("KEY") ?PADR("ON(KEY LABEL)",30) + " == ", ON("KEY LABEL") ?PADR("ON(PAGE)",30) + " == ", ON("PAGE") ?PADR("ON(READERROR)",30) + " == ", ON("READERROR") ?PADR("ON(SHUTDOWN)",30) + " == ", ON("SHUTDOWN") ENDIF T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="MIS") WAIT WINDOW NOWAIT "其他设置" ? ? "其他设置" ?PADR("CAPSLOCK()",30) + " == ", EVALUATE("CAPSLOCK()") ?PADR("CPCURRENT()",30) + " == ", EVALUATE("CPCURRENT()") ?PADR("CPCURRENT(1)",30) + " == ", EVALUATE("CPCURRENT(1)") ?PADR("CPCURRENT(2)",30) + " == ", EVALUATE("CPCURRENT(2)") ?PADR("DISKSPACE()",30) + " == ", EVALUATE("DISKSPACE()") ?PADR("FULLPATH('')",30) + " == ", EVALUATE("FULLPATH('')") ?PADR("GETENV('COMSPEC')",30) + " == ", EVALUATE("GETENV('COMSPEC')") ?PADR("GETENV('FOXPROCFG')",30) + " == ", EVALUATE("GETENV('FOXPROCFG')") ?PADR("GETENV('FOXPROSWX')",30) + " == ", EVALUATE("GETENV('FOXPROSWX')") ?PADR("GETENV('FOXPROX')",30) + " == ", EVALUATE("GETENV('FOXPROX')") ?PADR("GETENV('PATH')",30) + " == ", EVALUATE("GETENV('PATH')") ?PADR("GETENV('TEMP')",30) + " == ", EVALUATE("GETENV('TEMP')") ?PADR("INSMODE()",30) + " == ", EVALUATE("INSMODE()") ?PADR("ISCOLOR()",30) + " == ", EVALUATE("ISCOLOR()") ?PADR("MEMORY()",30) + " == ", EVALUATE("MEMORY()") ?PADR("NUMLOCK()",30) + " == ", EVALUATE("NUMLOCK()") ?PADR("OS()",30) + " == ", EVALUATE("OS()") ?PADR("OS(1)",30) + " == ", EVALUATE("OS(1)") ?PADR("PRINTSTATUS()",30) + " == ", EVALUATE("PRINTSTATUS()") ?PADR("VERSION(1)",30) + " == ", EVALUATE("VERSION(1)") ?PADR("VERSION(2)",30) + " == ", EVALUATE("VERSION(2)") ?PADR("VERSION(3)",30) + " == ", EVALUATE("VERSION(3)") ENDIF T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="VAR") WAIT WINDOW NOWAIT "系统内存变量" ? ? "系统内存变量" ?PADR("_ASCIICOLS",30) + " == ", EVALUATE("_ASCIICOLS") ?PADR("_ASCIIROWS",30) + " == ", EVALUATE("_ASCIIROWS") ?PADR("_BROWSER",30) + " == ", EVALUATE("_BROWSER") ?PADR("_BUILDER",30) + " == ", EVALUATE("_BUILDER") ?PADR("_CONVERTER",30) + " == ", EVALUATE("_CONVERTER") ?PADR("_DBLCLICK",30) + " == ", EVALUATE("_DBLCLICK") ?PADR("_GENGRAPH",30) + " == ", EVALUATE("_GENGRAPH") ?PADR("_GENMENU",30) + " == ", EVALUATE("_GENMENU") ?PADR("_GENPD",30) + " == ", EVALUATE("_GENPD") ?PADR("_GENSCRN",30) + " == ", EVALUATE("_GENSCRN") ?PADR("_GENXTAB",30) + " == ", EVALUATE("_GENXTAB") ?PADR("_PDRIVER",30) + " == ", EVALUATE("_PDRIVER") ?PADR("_PDSETUP",30) + " == ", EVALUATE("_PDSETUP") ?PADR("_SHELL",30) + " == ", EVALUATE("_SHELL") ?PADR("_SPELLCHK",30) + " == ", EVALUATE("_SPELLCHK") ?PADR("_STARTUP",30) + " == ", EVALUATE("_STARTUP") ?PADR("_TRANSPORT",30) + " == ", EVALUATE("_TRANSPORT") ?PADR("_WIZARD",30) + " == ", EVALUATE("_WIZARD") ENDIF T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="FONT") WAIT WINDOW NOWAIT "可用字体" ? ? "可用字体" =AFONT(T__fonts) FOR T__i = 1 TO ALEN(T__fonts) T__string = T__fonts[m.T__i] + ": " IF AFONT(T__sizes, T__fonts[m.T__i]) FOR T__j = 1 TO ALEN(T__sizes) T__string = m.T__string+IIF(m.T__j==1, "", ", ")+; IIF(T__sizes[m.T__j]==-1,"scalable", LTRIM(STR(T__sizes[m.T__j]))) ENDFOR ENDIF ? m.T__string ENDFOR RELEASE T__i, T__j, T__fonts, T__sizes, T__string ENDIF T__IsError = .F. IF (EMPTY(m.T__Method) OR UPPER(m.T__Method)="CONFIG") FOR T__i = 1 TO 3 DO CASE CASE m.T__i==1 T__File = SYS(2019) IF EMPTY(m.T__File) T__File = SYS(2004)+"CONFIG.FPW" ENDIF CASE m.T__i==2 T__File = "C:\CONFIG.SYS" CASE m.T__i==3 T__File = "C:\AUTOEXEC.BAT" ENDCASE WAIT WINDOW NOWAIT "配置文件" ? DO CASE CASE m.T__i==1 T__File = SYS(2019) IF EMPTY(m.T__File) T__File = SYS(2004)+"CONFIG.FPW" ? "*** " + "配置文件" + SYS(2004) + " CONFIG.FPW ***" ELSE ? "*** " + "配置文件" + SYS(2019) + " CONFIG.FPW ***" ENDIF CASE m.T__i==2 T__File = "C:\CONFIG.SYS" ? "*** " + "配置文件" + "C:\CONFIG.SYS ***" CASE m.T__i==3 T__File = "C:\AUTOEXEC.BAT" ? "*** " + "配置文件" + "C:\AUTOEXEC.BAT ***" ENDCASE IF FILE(m.T__File) T__handle = FOPEN(m.T__File) IF m.T__handle>-1 DO WHILE NOT FEOF(m.T__handle) ? FGETS(m.T__handle) ENDDO =FCLOSE(m.T__handle) ELSE ? "不能打开文件." ENDIF ELSE ? "不能找到文件." ENDIF ENDFOR RELEASE T__File, T__handle, T__i ENDIF SET CONSOLE OFF ? "完成" SET CONSOLE ON CLOSE ALL T__handle = FOPEN(T__OutputFile, 2) IF NOT m.T__handle==-1 =FSEEK(m.T__handle, -1, 2) IF FREAD(m.T__handle,1)==CHR(26) =FCHSIZE(m.T__handle, FSEEK(m.T__handle, -1, 2)) ENDIF =FCLOSE(m.T__handle) ENDIF ACTIVATE SCREEN SET TALK OFF SET MESSAGE TO "检查完成,结果已写至" + T__OutputFile WAIT CLEAR ZOOM WINDOW T__win MAX MODIFY COMMAND (T__OutputFile) NOEDIT WINDOW T__win NOMENU RELEASE WINDOW T__win SET VIEW TO it.vue DELETE FILE it.vue SET MESSAGE TO " " RETURN
包含了有关 Visual FoxPro 中声音、程序设计、数据输入的选项以及其它选项。
包含控制表中数据的选项。
远程数据 包含了有关远程 (ODBC) 视图和连接默认值的选项。
文件位置 包含了 Visual FoxPro 使用文件的目录位置和名字。可以直接在每个框中键入路径和文件名,也可以单击对话框按钮激活对话框设置相应的选项。
表单 包含“表单设计器”的选项。
项目 包含了有关“项目管理器”的选项。
控件 包含了为指定“选项”对话框中“表单”选项卡上的模板类而登记可视类库的选项。这些选项也指定“表单控件”工具栏上“查看类”按钮中可以选择的内容,这可以简化向表单中添加用户自定义控件的工作。
区域
调试 通过设置选项来定制 Visual FoxPro 调试器窗口。
语法着色 包含的选项可以指定颜色及字体,用来区分 Visual FoxPro 的程序元素,比如关键字和注释。这些程序元素在命令窗口或所有编辑窗口(除用 MODIFY FILE 或 MODIFY MEMO 命令打开的编辑窗口之外)中使用。
字段映象
|
|