VFP学习、开发漫谈 (16)
在 VFP 的很多教科书中,都出现过“导航条”的设计,用于移动记录指针,但其功能往往不够完善和实用。下面的导航条,是我参考“Microsoft Visual FoxPro 9\Samples\Classes\Buttons.vcx”中的 Vcr 类建立的,应用于我开发的多个系统:这是本讲座中第一个完整的实例,我已将该类打包,有需要的朋友可以下载:
Navigator.RAR
(6.08 KB)
该导航条有如下特点:
1. 记录的移动及位置信息都是相对的,过滤条件(Set Filter To)和索引顺序(Set Order To)仍有效。例如:总记录数是通过Count命令获取的,不是取自 Reccount()函数。
2. 对按钮的启用和禁用作了智能判断。比如:当前记录为第一条记录时,则禁用“首记录”和“前一条”按钮;当前记录为最后一条记录时,则禁用“后一条”和“末记录”按钮。无记录时,禁用所有按钮。
3. 在导航条的中间位置添加了一个只读文本框,用于显示当前记录的位置及总记录数。这也是本导航条的最大优点。该思路受 Microsoft Access 中数据表维护界面的启发。
4. 可以由用户指定记录移动的目标位置。
5. 由用户确定是否显示记录的位置信息。在移动记录的操作中,获取当前记录的位置及总记录数需要耗费时间,当记录很多时,可能影响速度,为此设置了一个开关按钮,由用户在功能和速度之间作出权衡。
6. 支持快捷键操作。按下 Ctrl+Home 可移动到第一条记录,按下 PageUp 移动到上一条记录,按下 PageDown 移动到下一条记录,按下 Ctrl+End 移动到最后一条记录。由于上述快捷键与表格的按键冲突,因此,若在表格中按下快捷键不会执行导航条操作,仍执行表格的默认操作。
导航条类有 4 个自定义属性:
1. EnableDisableOnInit:初始化时,是否执行 EnableDisableButtons方法以设置按钮的状态和位置信息,默认值为 .t.。
2. Lock:是否屏蔽当前记录的位置信息,默认值为 .f.,即:显示位置信息。
3. Shortcut:是否支持快捷键,默认值为 .t.。
4. SkipTable:与导航条相关联的表别名。
在导航条的初始化事件中,设置导航条的初始状态及是否支持快捷键操作:
程序代码:
* Vcr.Init * 初始化时,设置导航条状态 IF THIS.EnableDisableOnInit THIS.BeforeRecordPointerMoved && 选定工作区 GO TOP && 移动到首条记录 THIS.EnableDisableButtons && 设置导航条状态 ENDIF THIS.chkLock.Value = THIS.Lock && 设置锁定按钮的初始值 * 支持快捷键操作 IF THIS.ShortCut THISFORM.KeyPreview = .t. BINDEVENT(THISFORM,'KeyPress',THIS,'KeySkip') THIS.cmdTop.ToolTipText = '首记录 (Ctrl+Home)' THIS.cmdBottom.ToolTipText = '末记录 (Ctrl+End)' THIS.cmdPrior.ToolTipText = '前一条 (PageUp)' THIS.cmdNext.ToolTipText = '后一条 (PageDown)' ENDIF
导航条还有 5 个自定义方法:
1. BeforeRecordPointerMoved:移动记录前执行的方法,其作用是选定工作区
* 选定工作区 IF !EMPTY(This.SkipTable) SELECT (This.SkipTable) ENDIF
2. EnableDisableButtons:设置按钮的状态及位置信息,该方法是类的核心方法。
程序代码:
* 表为空或没有满足过滤条件的记录时 IF EOF() THIS.SetAll('Enabled', .f.,'CommandButton') IF !THIS.Lock THIS.txtCnt.Value = '0/0' ENDIF RETURN ENDIF * 记下当前记录(nRec)、首记录(nTop)、末记录(nBottom)的绝对位置 LOCAL nRec, nTop, nBottom ,nCurRec,nCnt nRec = RECNO() GO TOP nTop = RECNO() GO BOTTOM nBottom = RECNO() * 显示当前记录的相对位置 IF !THIS.Lock GO TOP COUNT TO nCurRec WHILE RECNO() # nRec && 当前记录的相对位置 nCurRec COUNT REST TO nCnt nCnt = nCnt + nCurRec && 总记录数 nCnt THIS.txtCnt.Value = LTRIM(STR(nCurRec + 1)) + '/' + LTRIM(STR(nCnt)) ELSE THIS.txtCnt.Value = '(锁定)' ENDIF GO nRec * 设置按钮的可用状态 DO CASE CASE nRec = nTop AND nRec = nBottom && 仅有一条记录时,禁用全部按钮 THIS.SetAll('Enabled', .f.,'CommandButton') CASE nRec = nTop && 当前记录为首记录时,禁用“第一个”和“上一个”按钮 THIS.cmdTop.Enabled = .f. THIS.cmdPrior.Enabled = .f. THIS.cmdNext.Enabled = .t. THIS.cmdBottom.Enabled = .t. THIS.cmdGo.Enabled = .t. CASE nRec = nBottom && 当前记录为最后一条记录时,禁用“下一个”和“最后一个”按钮 THIS.cmdTop.Enabled = .t. THIS.cmdPrior.Enabled = .t. THIS.cmdNext.Enabled = .f. THIS.cmdBottom.Enabled = .f. THIS.cmdGo.Enabled = .t. OTHERWISE && 当前记录为中间记录时,所有按钮可用 THIS.SetAll('Enabled', .t.,'CommandButton') ENDCASE
3. KeySkip:用键盘操作导航条
程序代码:
* 用键盘操作导航条 LPARAMETERS nKeyCode, nShiftAltCtrl IF THIS.Enabled DO CASE CASE TYPE('_Screen.ActiveForm.ActiveControl') = 'O' ; AND _Screen.ActiveForm.ActiveControl.BaseClass = 'Grid' * 不处理表格中的按键 CASE nKeyCode = 18 AND THIS.cmdPrior.Enabled * 按下 PgUp 键,移动到上一条记录 THIS.cmdPrior.Click CASE nKeyCode = 3 and THIS.cmdNext.Enabled * 按下 PgDn 键,移动到下一条记录 THIS.cmdNext.Click CASE nKeyCode = 29 and THIS.cmdTop.Enabled * 按下 Ctrl+Home 键,移动到第一条记录 THIS.cmdTop.Click CASE nKeyCode = 23 and THIS.cmdBottom.Enabled * 按下 Ctrl+End 键,移动到最后一条记录 THIS.cmdBottom.Click ENDCASE ENDIF
4. RecordPointerMoved:移动记录指针后执行的方法,该方法十分简单,只有一条语句:THISFORM.Refresh
5. VcrRefresh:刷新导航条。当新增或删除记录后,只需执行导航条的 VcrRefresh 方法即可:
程序代码:
* 刷新导航条 LOCAL nSelect nSelect = SELECT() && 保存环境 THIS.BeforeRecordPointerMoved && 选定工作区 THIS.EnableDisableButtons && 设置导航条状态 THIS.RecordPointerMoved && 刷新表单 SELECT (nSelect) && 恢复环境
四个记录移动按钮的代码大同小异,下面仅列出 cmdNext 的代码:
程序代码:
* 移动到下一条记录 WITH THIS.Parent .BeforeRecordPointerMoved && 选定工作区 SKIP && 移动记录指针 IF EOF() GO BOTTOM ENDIF .EnableDisableButtons && 设置导航条状态 .RecordPointerMoved && 刷新表单 ENDWITH
cmdGo 按钮的作用是移动到用户指定的记录,代码如下:
程序代码:
* 移动到指定记录 LOCAL cValue,i,nCurRec,nCnt,nRec,cInput WITH THIS.Parent * 获取当前位置和总记录数 cValue = .txtCnt.Value && 包含当前位置和总记录的字符串,如:1/100 i = AT('/',cValue) IF i > 0 nCurRec = VAL(LEFT(cValue,i-1)) && 当前位置 nCnt = VAL(SUBSTR(cValue,i+1)) && 总记录数 ELSE * 不显示记录位置时,获取当前位置和总记录数 .BeforeRecordPointerMoved && 选定工作区 nRec = RECNO() GO TOP COUNT TO nCurRec WHILE RECNO() # nRec && 当前记录的相对位置 nCurRec COUNT REST TO nCnt nCnt = nCnt + nCurRec && 总记录数 nCnt nCurRec = nCurRec + 1 && 当前位置 nCurRec GO nRec ENDIF * 由用户指定位置 cInput = INPUTBOX('要移动到哪条记录(1-' + TRAN(nCnt)+')?','定位记录') DO CASE CASE EMPTY(cInput) * 取消 CASE BETWEEN(VAL(cInput),1,nCnt) * 有效位置 .BeforeRecordPointerMoved && 选定工作区 SKIP VAL(cInput) - nCurRec && 移动记录指针 .EnableDisableButtons && 设置导航条状态 .RecordPointerMoved && 刷新表单 OTHERWISE * 无效位置 MESSAGEBOX('指定的位置超出了范围(1-' + TRAN(nCnt)+')!',64,'提示') ENDCASE ENDWITH
chkLock 复选框的作用是:是否显示记录的位置信息,其 Style 属性设为“1-Graphical”,代码如下:
程序代码:
* 显示/隐藏记录位置信息 WITH THIS.Parent .Lock = THIS.Value && 保存锁定状态,在设置导航条状态时是通过 .Lock 来判断的 IF THIS.Value && 不显示记录位置 .txtCnt.Value = '(锁定)' ELSE && 重新显示记录位置 .BeforeRecordPointerMoved .EnableDisableButtons ENDIF ENDWITH
在使用时,将该类添加到表单,并设置一下其 SkipTable 属性即可。
[ 本帖最后由 liuxingang28 于 2014-4-10 07:58 编辑 ]