Grid是VFP功能強大的表格(或者說網(wǎng)格)控件,它提供了比Browse窗口強大得多的控制方式。Grid是包含列對象的容器對象,而列對象包含了列標題對象及列控件對象,均有各自相應(yīng)的屬性集,因此我們可以完全控制表格中的任何一個元素,設(shè)計出通用的報表錄入界面。 下面我們就詳細討論如何使用Grid控件設(shè)計通用錄入界面,并解決幾個設(shè)計過程中經(jīng)常碰到的問題。 一、基本原理思路 設(shè)計靈活性、通用性較強的錄入界面較為合適的方法是:采用數(shù)據(jù)表(DBF文件)控制Grid控件的行列顯示信息、顯示方式。因此,我們將Grid控件的數(shù)據(jù)源RecordSource設(shè)為數(shù)據(jù)表(DBF文件),其數(shù)據(jù)格式、顯示方式及行列提示信息則分別由數(shù)據(jù)表RowMsg.DBF(控制表格的行即記錄方面信息)、ColMsg.DBF(數(shù)據(jù)格式并控制Grid控件的列方面信息)進行控制。另外,一般來說指定Grid控件第一列顯示行提示信息,若有多個提示信息字段,可連接處理為一列。 二、前期準備工作 1.創(chuàng)建表RowMsg.DBF,該表有2個字段,用于顯示Grid控件行信息: Create Table RowMsg(Code C(5),Name C(33)) 2. 創(chuàng)建表ColMsg.DBF,該表有6個字段,用于控制、顯示表格各列信息: Create Table ColMsg(Code C(5),Name C(20),NeedIn L(1),Field_Name C(10),Field_Type C(1),Field_Len N(3),Field_Dec N(3)) 3.創(chuàng)建表單In_Grid,將其屬性Name改為“FrmGrid”, Caption改為“數(shù)據(jù)錄入/修改窗口”;在表單上放置Grid控件,將其屬性Name改為“GridDat”;在表單上放置Command控件,將其屬性Name改為“CmdExit”, Caption改為“關(guān)閉退出”。 三、初步設(shè)計工作 1.先在表單的Init事件中寫入如下代碼: Set Talk Off Set Safe Off CLOSE ALL With ThisForm .AutoCenter=.T. &&表單自動居中 .ControlBox=.F. &&去除表單的控制框 .Width=_Screen.Width &&表單寬度同屏幕 EndWith **/由ColMsg.DBF生成輸入數(shù)據(jù)表Dat.DBF **/ Sele 0 Use ColMsg Copy To TmpStru Field Field_Name,Field_Type,Field_Len,Field_Dec Copy To TmpColMsg For NeedIn Create Dat From TmpStru &&生成Dat.DBF Eras TmpStru.DBF Sele 0 Use TmpColMsg Alias ColMsg Sele 0 Use RowMsg Index On AllTrim(Code) To TmpRowMsg Scan Insert Into Dat(Code) Values (RowMsg.Code) &&由RowMsg.DBF生成Dat.DBF的所有記錄 EndScan Sele Dat Go Top Set Rela To AllTrim(Code) Into RowMsg With ThisForm.GridDat .FontName="宋體" .FontSize=10 .DeleteMark=.F. &&去除Grid的刪除標記列,以美化界面 .RecordMark=.F. &&去除Grid的記錄選擇標記列,以美化界面 .ScrollBars=0 &&去除Grid的水平及垂直滾動條,以美化界面 .AllowHeaderSizing=.F.&&設(shè)定Grid的標題欄的高度不可改變 .AllowRowSizing=.F. &&設(shè)定Grid的各行高度不可改變 .ColumnCount=Recc("ColMsg")+1 .RecordSourceType=2 .RecordSource="Dat" **/下面指定表格第一列顯示行提示信息,并設(shè)置該列有關(guān)屬性。/** .Column1.ControlSource="Code+RowMsg.Name" &&將2個提示信息字段連接處理為一列 .Column1.Enabled=.F. &&該列不能編輯,焦點也不能移入 .Column1.Width=300 .Column1.BackColor=RGB(192,192,192) .Column1.Header1.Caption="指標代碼及名稱" **/下面指定表格其他列及其標題,并設(shè)置有關(guān)屬性。/** Sele ColMsg Scan lcCol=AllTrim(Str(Recn()+1,5)) With .Column&lcCol. .ControlSource="Dat."+Alltrim(Field_Name) .Header1.Caption=AllTrim(Name) .Width=90 EndWith EndScan **/下面設(shè)置表格寬度,并使其水平居中。/** lnGridLen=.Column1.Width+Recc("ColMsg")*.Column2.Width+10 .Width=IIF(m.lnGridLen>ThisForm.Width*0.95,ThisForm.Width*0.95,m.lnGridLen) .Left=(ThisForm.Width - .Width)/2 ThisForm.CmdExit.Left= (ThisForm.Width - ThisForm.CmdExit.Width)/2 &&使命令按鈕水平居中 EndWith 2.在命令按鈕CmdExit的Click事件中寫入代碼:ThisForm.Release 至此, 該表單已可順利運行。 四、進一步完善 該表單雖已可順利運行,但卻存在著如下兩個主要不足之處: 1.輸入焦點不能自動下移,即在第一行最后一個字段上打回車鍵,焦點不能自動轉(zhuǎn)到下一行的第一個字段上,而是回到第一行的第一個字段上。 2.信息提示列(最左一列)無法固定,即隨著輸入焦點的右移,信息提示列將移出屏幕而不再可見,失去其信息提示的作用。 下面我們就來詳細討論并解決這兩個難題。 (一) 輸入焦點自動下移 解決此問題可用的方法較多。經(jīng)過多方比較、測試,我們認為采用表單的KeyPress事件解決這個問題較為合適(當然采用Grid控件的BeforeRowColChange事件或AfterRowColChange事件也完全可以)。KeyPress事件在用戶按下并釋放某鍵時發(fā)生,由具有焦點的對象接收。表單可在下列兩種情況下接收KeyPress事件: (1)表單中不包含任何控件或所有控件都不可見或都未激活。(2)表單的KeyPreview 屬性設(shè)置為.T.。 顯然本例不適用情況(1),只適用情況(2)。為此,需在表單的Init事件代碼中增加如下代碼: ThisForm.KeyPreview=.T. 然后在表單的KeyPress事件中寫入如下代碼: LPARAMETERS nKeyCode, nShiftAltCtrl &&這行代碼由系統(tǒng)自動產(chǎn)生,不必錄入 **/判斷當前焦點是否在Grid控件上及按鍵是否是回車鍵或TAB鍵**/ With ThisForm.ActiveControl IF Upper(.Name)=’GRIDDAT’ And (m.nKeyCode=13 OR m.nKeyCode=9) if .ActiveColumn=Recc("ColMsg")+1 &&判斷當前焦點是否在Grid控件的最后一個字段上 KeyBoard "{DnArrow}" Plain &&是則下移一行 Endif ENDIF EndWith 至此, 輸入焦點自動下移的問題基本解決。 (二) 固定信息提示列(最左一列)
一般情況下,第一列往往是整條記錄的提示信息。Foxpro的Browse命令有Lock參數(shù),可指定在瀏覽窗口左分區(qū)中可見字段數(shù),這樣在數(shù)據(jù)瀏覽時,可保證當發(fā)生水平滾動而使最左幾列不可見時,仍能在瀏覽窗口的左分區(qū)中看到這幾列的內(nèi)容。這在輸入帶有較多字段的數(shù)據(jù)情況下非常有用。 在Grid控件中可通過設(shè)置其Panel、Partition及PanelLink屬性解決這個問題。這里我們要解決更難些的問題: 當Grid控件右面板的第一列可見時,左面板第一列消失,因為這時不需要顯示兩個第一列;而當Grid控件右面板的第一列不可見時,左面板顯示第一列的內(nèi)容。也即所謂的固定信息提示列(最左一列)。 解決此問題的方法也很多,為簡化編程,我們?nèi)圆捎帽韱蔚腎nit事件及KeyPress事件(當然也可采用Grid控件的BeforeRowColChange事件或AfterRowColChange事件)。 在表單的Init事件代碼中增加如下代碼: 注:Partition屬性指定一個表格是否拆分為兩個分區(qū),并且指定相對于表格左分區(qū)的拆分位置。當Partition設(shè)置為零時,表示不拆分表格;為非零時表示拆分位置的值(實際上就是左分區(qū)的寬度)。這里我們判斷一下該Grid控件的寬度是否超過表單寬度(實際上是表單寬度乘0.95,以使Grid左右留有一定空白,以美化界面),是則Partition設(shè)置為信息提示列的寬度,以適應(yīng)前面提出的固定信息提示列的要求;否則Partition設(shè)置為零(因為此時已不需要分區(qū))。 (三)其他細節(jié)問題 經(jīng)過上述一系列改進后,報表通用錄入界面已基本實現(xiàn),但仍存在著幾個細節(jié)問題: ?。?) 初始進入該界面及錄完一行進入下一行時,信息提示列會出現(xiàn)重復顯示,如附圖1示,解決方法:分別在表單的Init事件及KeyPress事件代碼中適當位置增加如下代碼即可: KeyBoard "{TAB}{BACKTAB}" Plain ?。?) 我們希望在最后一行的最后一列數(shù)據(jù)在打回車后,焦點能自動移到命令按鈕上,以便我們選擇進行下一步工作, 如”存盤退出”. “繼續(xù)錄入”. “放棄關(guān)閉”等等,實踐中發(fā)現(xiàn)在KeyPress事件代碼中適當位置增加ThisForm.CmdExit.SetFocus()不行,只能用KeyBoard "{Ctrl+Tab}" Plain實現(xiàn)。 ?。?) 不設(shè)置水平及垂直滾動條的優(yōu)點顯而易見,一是界面簡潔美觀,如附圖2示, 帶有水平及垂直滾動條的錄入界面顯得不簡潔,有些拖泥帶水,且右分區(qū)的左下端垂直滾動條上的那個細黑條是分區(qū)標志,拉動它可以重新調(diào)整左右分區(qū),以致破壞業(yè)已設(shè)計好的錄入界面;二是編程大大簡化,如單擊水平滾動條時左右分區(qū)的拆分會出現(xiàn)問題(當然可通過Grid控件的Scrolled事件編程解決)。但不可否認也帶來上下左右翻屏的不便,主要是上下翻屏的不便(左右滾屏可通過連續(xù)按TAB鍵、Shift+TAB組合鍵實現(xiàn))。上下翻屏原可通過PgUp/PgDn鍵解決,但此時使用PgUp/PgDn會出現(xiàn)信息提示列與右分區(qū)的數(shù)據(jù)列不相對應(yīng)的情況,因此我們重新定義PgUp/PgDn鍵,在表單的Init事件代碼中增加如下2行代碼: ON KEY LABEL PgUp IIF(Bof("Dat"),Application.DoCmd("=MessAgeBox(’已是第一條記錄!’,; ’提示信息’)"),Application.DoCmd("SKIP -12")) ON KEY LABEL PgDn IIF(EoF("Dat"),Application.DoCmd("=MessAgeBox(‘已是最后一條記錄!’,; ’提示信息’)"),Application.DoCmd("SKIP 12")) 注:通過DoCmd函數(shù)、MessageBox函數(shù)實現(xiàn)以下功能: 按PgUp向上滾動12行,若已到首行則提示“已是第一條記錄!”;按PgDn向下滾動12行,若已到末行則提示“已是最后一條記錄!”。 五、結(jié)束語 至此,我們已較為圓滿的實現(xiàn)了使用Grid控件設(shè)計通用錄入界面。應(yīng)該指出的是,可以進一步完善的地方仍很多: 1.信息提示列及各數(shù)據(jù)列的寬度設(shè)置??捎肦owMsg.DBF及ColMsg.DBF中的相應(yīng)字段長度乘上某一系數(shù)得到,也可在RowMsg.DBF及ColMsg.DBF中設(shè)置相應(yīng)字段直接確定其寬度。我們采用了前法。 2.水平及垂直滾動條的設(shè)置。當數(shù)據(jù)列超過30列后,有水平滾動條就顯得較為方便。遺憾的是,VFP本身未提供單獨的滾動條控件(當然損失一些界面的美觀簡潔,使用Grid控件自身的滾動條完全可以實現(xiàn)),因此,我們只能使用OLE控件,如MicroSoft Forms 2.0 ScrollBar,并通過設(shè)置其相應(yīng)的屬性及事件編程,解決這個問題。本文限于篇幅,不再討論。另外,不使用Grid控件而直接使用已較好解決這些問題第三方OLE控件,如TrueDBGrid、FlexGrid、MicroSoft DataGrid Control等等,也是一種解決方法,但已不屬本文范圍,不再討論。 本例在Windows9x/WindowsXP、Visual Foxpro 5.0/6.0下調(diào)試通過。本例所有事件的完整代碼附下。 表單的Init事件完整代碼如下:
Set Talk Off Set Safe Off CLOSE ALL ON KEY LABEL PgUp IIF(Bof("Dat"),Application.DoCmd("=MessAgeBox(’已是第一條記錄!’,’提示信息’)"),Application.DoCmd("SKIP -12")) ON KEY LABEL PgDn IIF(EoF("Dat"),Application.DoCmd("=MessAgeBox(‘已是最后一條記錄!’,’提示信息’)"),Application.DoCmd("SKIP 12")) With ThisForm .KeyPreview=.T. .AutoCenter=.T. .ControlBox=.F. .Width=_Screen.Width EndWith Sele 0 Use ColMsg Copy To TmpStru Field Field_Name,Field_Type,Field_Len,Field_Dec Copy To TmpColMsg For NeedIn Create Dat From TmpStru Eras TmpStru.DBF Sele 0 Use TmpColMsg Alias ColMsg Sele 0 Use RowMsg Index On AllTrim(Code) To TmpRowMsg Scan Insert Into Dat(Code) Values (RowMsg.Code) EndScan Sele Dat Go Top Set Rela To AllTrim(Code) Into RowMsg With ThisForm.GridDat .FontName="宋體" .FontSize=10 .DeleteMark=.F. .ScrollBars=0 .ColumnCount=Recc("ColMsg")+1 .RecordSourceType=2 .RecordSource="Dat" .Column1.ControlSource="Code+RowMsg.Name" .Column1.Enabled=.F. .Column1.Width=Len(Code+RowMsg.Name)*8 .Column1.BackColor=RGB(192,192,192) .Column1.Header1.Caption=" 指標代碼及名稱 " .Panel=1 .PanelLink=.T. lnGridLen=.Column1.Width+10 Sele ColMsg Scan lcCol=AllTrim(Str(Recn()+1,5)) With .Column&lcCol. .ControlSource="Dat."+Alltrim(Field_Name) .Header1.Caption=AllTrim(Name) .Width=Len(.Header1.Caption)*7 lnGridLen=m.lnGridLen+.Width EndWith EndScan .Width=IIF(m.lnGridLen>ThisForm.Width,ThisForm.Width*0.95,m.lnGridLen) .Left=(ThisForm.Width - .Width)/2 .Partition=IIF(m.lnGridLen>ThisForm.Width*.95,.Column1.Width,0) ThisForm.CmdExit.Left= (ThisForm.Width - ThisForm.CmdExit.Width)/2 表單的KeyPress事件完整代碼如下:
LPARAMETERS nKeyCode, nShiftAltCtrl With ThisForm.ActiveControl IF Upper(.Name)=’GRIDDAT’ And (m.nKeyCode=13 OR m.nKeyCode=9) IF .ActiveColumn=Recc("ColMsg")+1 KeyBoard "{DnArrow}" Plain KeyBoard "{Tab}{BackTab}" Plain If .ActiveRow=Recc("RowMsg") KeyBoard "{Ctrl+Tab}" Plain Endif ENDIF ENDIF EndWith 命令按鈕CmdExit的Click事件完整代碼如下: ON KEY LABEL PgUp ON KEY LABEL PgDn Close All Eras TmpColMsg.DBF Eras TmpRowMsg.IDX ThisForm.Release KeyBoard "{Tab}{BackTab}" Plain |
|