VFP学习、开发漫谈 (16)
在 VFP 的很多教科书中,都出现过“导航条”的设计,用于移动记录指针,但其功能往往不够完善和实用。下面的导航条,是我参考“Microsoft Visual FoxPro 9\Samples\Classes\Buttons.vcx”中的 Vcr 类建立的,应用于我开发的多个系统:这是本讲座中第一个完整的实例,我已将该类打包,有需要的朋友可以下载:
该导航条有如下特点:
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)
ENDIF2. 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')
ENDCASE3. 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
ENDIF4. 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 编辑 ]









