VFP学习、开发漫谈 (八)
今天,与大伙聊一聊有关表单与控件设计方面的使用体会。先说一说“Properties Window(属性窗口)”。
1. 要快速定位到某个属性、事件或方法,可先切换到 ALL 标签,再按下 Cltrl+Alt+属性/事件/方法的首字母,如:要定位到 Name 属性,可按下 Ctrl+Alt+N
2. 要修改枚举型属性,可以不从列表中选择,而直接双击属性名或列表框,系统自动在各个选项间切换。如:要将表单的 ShowTips 属性由 .f.设为 .t.,可直接双击 ShowTips属性名或.f.属性列表值
3. 若修改了某个属性,则其属性值的字体由黑色变成粗体紫色,而添加了代码的事件或方法变为粗体红色
4. 若要将某个属性改回系统默认值,最好不要直接修改属性值,而应该单击右键,选择“Set to Default”。好处有二:一是可减少编译后EXE文件的大小,二是若控件来源于自定义类,可恢复对父类属性的继承。
5. 若要仅显示被修改的属性以及添加了代码的事件、方法,可以右键单击属性窗口上方区域,选择“Non_Default Properties Only(仅显示默认属性)”
为表单窗口命名时,应该遵守一定的规范,以保持窗口名称的唯一性,便于在命令或函数中引用。一般情况下,我习惯在表单文件名前加“frm”作为窗口的名称。如:供应商字典的表单文件名为 Vendor.scx,则表单的 Name 属性可设为 frmVendor。要判断供应商字典窗口是否打开,可检测 WEXIST('frmVendor') 函数的返回值。
顺便说一下,若要修改表单文件名,如:Vendor.scx,最好不要在资源管理器中修改,而应该在项目管理器中右键单击表单名,选择“Rename”,再输入新文件名。这样做的好处是,表单的附属文件(如:Vendor.sct)也自动改名。
表单有一个“DataSession(数据工作期)”属性,默认值是“1-Default Data Session(默认工作期)”,实际应用中,应该将其设置为“2-Private Data Session(私有数据工作期)”。当设置为私有工作期时,每个表单的数据环境和系统状态(如:Set Talk、Set Delete、Set Safety等)都是相互独立的,互不影响。
一般情况下,我们只允许运行表单的一个实例。当窗口不存在时,运行DO FORM命令,窗口存在时,运行ACTIVATE WINDOW命令激活该窗口即可。为此,我编写了一个 Doform()函数来运行表单,内容如下:
程序代码:
* 运行表单-------------------------------------------------------------------------------------------------- FUNCTION DoForm(tcForm,tcVar,tcPara) && tcForm(表单文件名),tcVar(表单变量),tcPara(参数) LOCAL W_Name,lErr,i = WaitWindow('正在启动程序,请稍候……') W_Name = 'frm' + tcForm && 表单窗口名 IF !WEXIST(W_Name) && 窗口不存在时,运行表单 FOR i = 1 TO 5 && 尝试运行表单 5 次,约 3 秒 lErr = .f. TRY IF TYPE('tcVar') = 'C' IF TYPE('tcPara') = 'C' DO FORM (tcForm) NAME (tcVar) LINKED WITH tcPara ELSE DO FORM (tcForm) NAME (tcVar) LINKED ENDIF ELSE IF TYPE('tcPara') = 'C' DO FORM (tcForm) WITH tcPara ELSE DO FORM (tcForm) ENDIF ENDIF CATCH TO oErr WHEN oErr.ErrorNo = 2005 lErr = .t. && 因表被其他用户占用,导致无法打开表单时,再重试 4 次 ENDTRY IF !lErr EXIT && 正常打开时,退出 ENDIF = INKEY(0.5,'H') && 等待 0.5 秒后,再重试 NEXT i IF lErr MESSAGEBOX('数据库中的表正在被其他用户使用,请稍后重试!',16,'提示') ENDIF ELSE ACTIVATE WINDOW (W_Name) TOP && 窗口存在时,激活窗口 IF WMINIMUM(W_Name) && 窗口最小化时,恢复窗口大小 ZOOM WINDOW (W_Name) NORM ENDIF ENDIF WAIT CLEAR ENDFUNC
上述代码中,将DO FORM 放在了一个循环中,并对 2005 号错误进行了捕获。
为什么要捕获错误信息呢?难道DO FORM也会出错?我们先做一个小实验:建立一个自由表 Table1,以共享方式打开该表,然后键入 FLOCK()对表加锁,或者键入 RLOCK('0','Table1')对表头加锁。再启动一个VFP窗口,新建一个表单,将 Table1添加到数据环境,运行表单,你会发现系统进入无限等待状态,至到按下 Esc 键。若在表单数据环境的 BeforeOpenTables 事件中输入 Set Reprocess To 0 Seconds,使打开或加锁失败后不再重试,则运行表单时会立刻返回1881号错误,在编译成EXE文件后,返回2005号错误。
系统进入无限等待状态,是由于 Set Reprocess to Automatic,在其他用户使用表而无法打开表或加锁失败时无限重试。在我编写的程序中,一般设置 Set Reprocess to 0 Seconds,禁止系统重试,而完全由我们自己来处理共享冲突。
上述代码中,共重试了 5 次,每次间隔 0.5 秒,若仍无法运行表单,则给出提示并退出。
我们都不是完人,无论你在程序的开发和调试阶段花费多少精力,程序中都或多或少存在Bug,特别是一些逻辑错误很难发现。当程序发生运行错误时,我一般将错误信息记录到一个表文件中,通过定期查看该文件,来排除错误。上述错误,就是我通过跟踪错误日志文件发现的。错误捕获可通过在主程序中添加如下语句来实现的: ON ERROR DO ErrHandler WITH SYS(16),LINENO(1),MESSAGE(1),ERROR(),MESSAGE(),gcUserName
很多状态设置命令都和数据工作期有关,由于表单多采用私有数据工作期,因此我一般在数据环境的 BeforeOpenTables事件中运行一个自定义函数 SetStat()来初始化系统状态。
程序代码:
* 状态设置--------------------------------------------------------------------------------------------------- FUNCTION StatSet() SET HOURS TO 24 && 时间采用 24 小时制 SET CENTURY ON && 显示日期中的世纪 SET DATE ANSI && 国标日期格式:YYYY.MM.DD SET SAFETY OFF && 关闭安全提示 SET EXCLUSIVE OFF && 多用户方式 SET TALK OFF && 不反馈命令结果 SET DELETED ON && 不对有删除标记的记录进行操作 SET EXACT OFF && 只比较字符串的前面部分 SET AUTOSAVE ON && 自动保存 SET CONFIRM ON && 每个控件都需确认 SET REPROCESS TO 0 SECONDS && 对表锁定失败后不自动重试 SET DECIMALS TO 6 && 默认 6 位小数 SET COLLATE TO "MACHINE" && 按机器码排序 SET NULLDISPLAY TO '' && 不显示空值 SET ESCAPE OFF && 程序运行时禁止按 Esc 键中断 ENDFUNC
对于影响全局的系统设置,我一般放在 Config.fpw 中,内容如下:
程序代码:
SCREEN = OFF SYSMENU = OFF RESOURCE = OFF STATUS = OFF NOTIFY = ON NOTIFY CURSOR = OFF
下面说一说我在使用表单控件中的一些心得体会:
1. 选定多个控件左对齐时,系统会与最左边控件的左边界对齐。若按下 Ctrl键单击布局工具栏上的左对齐按钮,则选定的控件会与最右边控件的左边界对齐。该技巧也适应于其他对齐方式。
2. 若多个控件有相同的属性和属性值,可以先选定这些控件,然后在属性窗口中输入新值,则所有选定控件的该属性都会被修改,这要比逐个修改来得快。
3. 按下Ctrl键移动控件则可以忽略网格设置,将控件“平滑”地移动到任意位置。同理,按下Ctrl键拖动控件四周的“锚点”,则可以“平滑”地缩放控件的大小。
4. 选定控件后,每按一下光标键(→、←、↑、↓),则控件移动一个像素。按下Shift键不放,每按一下光标键,则控件缩放一个像素。这在精确调整控件的大小和位置时特别有用。
5. 当按下Ctrl键不放,再按光标键时,则控件会按照网格的大小“跳跃”式移动。按下Ctrl+Shift不放,再按光标键时,控件会按照网络的大小“跳跃”式缩放。
6. 按下 Ctrl+Shift 不放,单击容器控件,可直接选定容器中的子控件。
7. 当控件有重叠时,可以通过布局工具栏上的“Bring to Front(置前)”或“Send to Back(置后)”来调整控件的前后顺序,以防止前面的控件挡住后面的控件造成后者无法选定。
8. 大部分控件都有一个TabIndex属性。以交互方式设定TabIndex值时,不一定非要单击数字框,单击控件本身也可以。
9. 通过单击方式设置好最后一个控件的 TabIndex后,单击一下表单即可退出,不一定非要单击布局工具栏上的“Set Tab Order”按钮。
10.设置好TabIndex后,若要将某个控件的TabIndex值调整为 n,则可以在属性窗口中将该控件的值修改为 n-1,则系统会自动调整其他控件的TabIndex值,原来TabIndex为 n 的控件会自动调整为 n+1,其他控件依次类推。这在设置新“插入”控件的TabIndex属性时非常有用。
11.使用键盘输入时,我们喜欢按回车键移动到下一个控件,但在列表框控件上按下回车键时无效,此时,可在列表框的 Keypress 事件中输入以下代码:
IF nKeyCode = 13
KEYBOARD '{TAB}'
ENDIF
12.若要按回车键在控件之间移动,请不要设置“缺省按钮”,即:将某个按钮的 Default 属性设为 .t.,否则按下回车键,可能会激活默认按钮的 Click事件。
13.文本框控件有一个 SelectOnEntry 属性,将其设为 .t.后,通过键盘(如:TAB)使文本框获得焦点时,会选定文本框中的内容。但通过鼠标单击使文本框获得焦点时,文本框中的内容没有被选定。可以在文本框中的 GotFoucs 事件中输入以下代码来解决:
IF THIS.SelectOnEntry
NODEFAULT
DODEFAULT()
ENDIF
14.我比较喜欢自定义类,这些类都是根据自己的使用习惯由基类派生出来的。我将这些类保存在一个叫 MyClass.vcx 文件中,并加入项目文件。在表单上添加自定义类控件时,直接将项目管理器中的类控件拖放到表单上即可。
表单设计是VFP程序设计的核心,这方面涉及的内容太多,下次咱们接着聊。
[ 本帖最后由 liuxingang28 于 2014-3-6 10:22 编辑 ]