欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
VFP的表格透析 [IT行內話(huà)]
 VFP 的表格透析 收藏到網(wǎng)摘
>> 510group 發(fā)短消息 2006.06.19 11:56 [引用] [編輯] [刪除]

RMH 于 2001-12-17 12:41:05 發(fā)表:
作者 Vlad Grynchyshyn
譯者 RMH
VFP 中的表格 第一部分
什么是表格和什么時(shí)候使用它?
表格控件是一組允許在一個(gè)表格一樣的可卷動(dòng)列表中顯示數據的 VFP 對象. 表格由表格對象自己和一組列組成. 各列必須有一個(gè) header 對象和在表格列中顯示數據的控件. 表格列中的控件用于顯示和編輯數據. 表格是一個(gè)帶網(wǎng)格線(xiàn)條的矩形, 和頂部的標題, 卷動(dòng)條和一些其它有用的東西, 如記錄標記, 刪除標記, 分隔條等.
說(shuō)實(shí)話(huà), 表格顯示數據不象 Excel sheet 那樣自由. 表格要求用 VFP 記錄源 (別名) 來(lái)顯示一些東西. 要在同一列的不同行中顯示不同類(lèi)型的數據是困難的. 表格中的所有行的高度都是相同的, 并且表格中一列中的所有行的寬度也是相同的. 當然還有一些其它相當奇怪的和不可思議的限制, 除非我們記住表格是一個(gè)真實(shí)的基于早期版本的 FoxPro 的瀏覽窗口來(lái)編寫(xiě)的控件. 這一事實(shí)回答了許多關(guān)于表格的奇怪的東西和行為的 ´為什么´. 盡管有許多的麻煩, 表格還是相當有用的控件并在受到 VFP 程序員的歡迎, 因此也有許多的處理和方案來(lái)打破表格的限制并用它來(lái)產(chǎn)生更好的效果. 局限或奇怪的東西將不再是決定是否使用表格的關(guān)鍵.
表格控件對于在一個(gè)簡(jiǎn)潔的表單中顯示(瀏覽)數據是有用的 (每頁(yè)顯示大量數據). 表格對于搜索和定位數據比彈式菜單或整頁(yè)的下拉列表好. 持久穩固的使用表格作為定位數據不是一個(gè)好的主意, 因為表格通常占用許多空間. 許多應用程序有一個(gè)表格作為一個(gè)主要的控制表單 - 復雜的帶有許多功能的可顯示子表單來(lái)進(jìn)行數據編輯的表格.
表格對于所有類(lèi)型的規則的只讀數據是有用的. 使用表格進(jìn)行數據編輯不是一個(gè)好主意. 在表格中進(jìn)行數據編輯的好處僅僅是易于組織和管理應用程序. 在表格中進(jìn)行數據編輯時(shí)如果要打破 VFP 表格的限制會(huì )有許多問(wèn)題. 但是, 例如發(fā)票或訂單表單的行項, 使用表格來(lái)進(jìn)行編輯是更好的, 用戶(hù)也會(huì )感到更舒服. 這樣的表單是一個(gè)例外; 不要僅僅因為表格對于編輯數據時(shí)的簡(jiǎn)單就把它放在每一個(gè)表單中. 當你這樣做時(shí), 你會(huì )很快發(fā)現自己處于麻煩之中, 因為表格是如此復雜且易于失去控制的控件. 這將要求你付出更多的努力來(lái)指出問(wèn)題, 找出解決方案和修正更多的問(wèn)題. 通常, 數據編輯使用文本框顯示字段和帶有定位,保存/恢復和一些其它特定按鈕工具欄來(lái)移動(dòng)記錄的的表單. 為表格生成一個(gè)這樣的表單是花費大量時(shí)間的一種方法. 當然, 如果你有時(shí)間并喜歡玩耍怪異的東西, 表格是一種消磨時(shí)光并找出方案和處理辦法的最好的控件.
表格自動(dòng)進(jìn)行列的重綁定
表格中最常出現的奇怪的東西是列的 control sources 的自動(dòng)改變. 你會(huì )發(fā)現突然表格列顯示了其它的不是你在列的 ControlSource 屬性中指定的數據. 另外, 列中顯示的數據的順序不是你重新安排過(guò)的順序. 為什么會(huì )這樣?
======================================================
因為表格的 RecordSource 屬性在設計時(shí)被改變. 在表格的 RecordSource 屬性被改變后所有的 ControlSource 值被清除了. 在此情況下制作一個(gè)這些值的備份, 通常是在列的 Comment 屬性中.
因為表格的 RecordSource 屬性在運行時(shí)被改變. 好了, 如果你要這樣做, 保存所有的 control sources, 然后恢復它們.
可能的原因是表格被重建了. 關(guān)于這一點(diǎn)將在下一章中說(shuō)明.
======================================================
該行為的副作用并不嚴重. 通常顯示的數據沒(méi)有改變, 除非表格有一些復雜的功能和結構體系. 例如, 列值顯示的是表達式 - 表達式將丟失并只顯示字段. 對于用戶(hù)來(lái)說(shuō)在表格列中顯示 ID 字段 (主關(guān)鍵字段)也不是一件好事. 最危險的是當你在表格中有一些不同的控件時(shí). 當列中有一個(gè)復選框控件時(shí), 但表格決定使用字符型字段作為該列的 control source 時(shí), VFP 將顯示一個(gè)關(guān)于數據類(lèi)型失配的錯誤: ´控件不支持該數據類(lèi)型´.
無(wú)論是上述何種情況, 當以任何方式改變 record source 時(shí), 都有消除表格重建的方案.
以下示例代碼用列的 Comment 屬性保存 control sources 并恢復它們.
&& 備份各列的 ControlSource
with {grid}
local nColumnIndex
for m.nColumnIndex = 1 to .ColumnCount
.Columns(m.nColumnIndex).Comment = .Columns(m.nColumnIndex).ControlSource
endfor
endwith
&& 恢復各列的 ControlSource
with {grid}
local nColumnIndex
for m.nColumnIndex = 1 to .ColumnCount
if !empty(.Columns(m.nColumnIndex).Comment)
.Columns(m.nColumnIndex).ControlSource = .Columns(m.nColumnIndex).Comment
endif
endfor
endwith
上面代碼中的 {grid} 是表格對象的引用(如:Thisform.Grid 或
Thisform.Pageframe1.Page1.Grid1).
把這些代碼放到表格類(lèi)的一個(gè)方法中是一個(gè)不錯的主意. 這在設計時(shí)因改變 record source 而造成 ControlSource 值丟失時(shí)在表格類(lèi)的 Init 方法中調它來(lái)恢復 control sources 也是一種好辦法.
重大注意: 在完全恢復 control sources 前不要執行任何表單上的可視控件或表格的 refreshing. 否則如果你在表格列中使用了自定義控件時(shí)你將會(huì )遇到一個(gè)錯誤信息 ´控件不支持該數據類(lèi)型´. 這是因為正如前面提及的列中可能使用的打亂了的 control sources 中的不正確的字段類(lèi)型造成的.
表格自動(dòng)重構
你是否發(fā)現過(guò)你的表格的行為不再象你在設計時(shí)設計的那樣的情況? 列中的自定義控件丟失? 列, 標題或控件事件中的代碼不再運行? 表格的重構行為會(huì )完全移去所有的表格控件和列并用默認的 VFP 控件和屬性設置再次重建表格. 這造成所有列對象中的所有方法, 屬性設置和對象丟失. 列的 CurrentControl 屬性重置為默認的文本框控件. 自定義標題丟失. 通常這是一種不知道表格發(fā)生了什么情況的災難. 下面說(shuō)明種種原因及解決辦法.
1. 表格自己重構總是發(fā)生在 RecordSource 別名被關(guān)閉后. 如果這是一個(gè)視圖, 通常在你 requery 視圖時(shí)重構不會(huì )發(fā)生. 如果它是一個(gè) SQL 語(yǔ)句, 當你指定另一個(gè) SQL 語(yǔ)句或關(guān)閉表格使用的用于保存查詢(xún)結果的別名時(shí)重構就會(huì )發(fā)生. 當你使用 SQL Pass-Through 來(lái)再次查詢(xún)數據到表格的 record source 使用的別名時(shí)重構也會(huì )發(fā)生, 即使你任然使用的是視圖.
要避免在刷新表格的 record source 的重構, 你需要在上述的任何表格 record source 刷新前, 指定一個(gè)空串 (不是一個(gè)空格 - " ", 而是空串 - "") 到 Record source. 檢查你的代碼是否正確, 你是否以正確的順序這樣做了, 或其它東西沒(méi)有打亂正確的 refreshing 處理順序. (你可以在表格的 BeforeRowColChange 事件中放入 SET STEP ON 或設置跟蹤斷點(diǎn)來(lái)跟蹤代碼). 在 record source 刷新后, 再次指定 record source 到表格. 在此情況下重構不會(huì )發(fā)生, 但是, 因為指定 record source , 列會(huì )發(fā)生自動(dòng)重綁定. 以下是如何用一小點(diǎn)代碼來(lái)修正這種情況的示例.
* 下面保存 control sources
...........
{grid}.RecordSource = ""
* 執行 record source 的刷新
...........
* 恢復 record source
{grid}.RecordSource = "{RecordSourceName}"
* 下面恢復列的 control sources
...........
在上面代碼中 {grid} 是表格對象的引用, {RecordSourceName} 是用于 record source 的別名或 SQL 語(yǔ)句. 通常情況下使用 SQL 語(yǔ)句作為 record source 別名會(huì )使列的 ControlSource 改變, 因為 VFP 總是改變它來(lái)表示為 ´Alias.FieldName´格式而不管在設計時(shí)只指定了字段名. 以上方法在改變表格的 SQL 語(yǔ)句時(shí)也是有用的.
重要注意: 在語(yǔ)句 ´RecordSource=""´ 后到完全恢復 control source 之間, 不要執行任何表單上的可視控件或表格的 refreshing. 否則如果你在表格列中使用了自定義控件, 你將可能收到一個(gè) ´控件不支持該數據類(lèi)型´ 錯誤.
另一種避免表格重構的方法是使用表格的 BeforeRowColChange 事件. BeforeRowColChange 事件在每次表格將要重構時(shí)被激發(fā). 它在包括表格別名關(guān)閉時(shí), SQL Pass-Through 游標重獲取等時(shí)發(fā)生. 無(wú)論表格是否可見(jiàn), 具有焦點(diǎn)及表格的配置. 最令人驚奇的是放置 NODEFAULT 到該事件中以使數據改變時(shí)避免表格重構. 但是, 在此情況下表格會(huì )顯示不可思議的錯誤行為.
thisform.GridRefreshing = .T. && 告訴所有表格控件
&& 將重新獲取表格數據
... 執行數據查詢(xún)
thisform.Grid.RecordSource = thisform.Grid.RecordSource
thisform.Refresh && 或表格刷新
DOEVENTS         && 如果需要 - 只測試不要該命令
&& 在此時(shí)表格停止自己重構>
thisform.GridRefreshing = .F.
在表格類(lèi)的 BeforeRowColChange 事件中放入以下代碼:
if PEMStatus(thisform,"GridRefreshing",5) AND thisform.GridRefreshing
nodefault
return
endif
如果你放置上面代碼到表格類(lèi)中這樣該功能一般性的, 包括使 GridRefreshing 屬性作為你的表格類(lèi)的屬性. 有時(shí)要求設置焦點(diǎn)到表格外然后再設置焦點(diǎn)回到表格, 因為表格中的當前單元會(huì )在使用該方法來(lái)避免表格重構時(shí)顯示星號 (´*******´).
不幸的是, 沒(méi)有辦法知道 BeforeRowColChange 事件被調用的原因是因為它的重構還是移動(dòng)表格單元格的焦點(diǎn)或是表格獲得焦點(diǎn)時(shí). 就象在示例中一樣使用一個(gè)標記. 如果你有時(shí)間, 你也可以生成一個(gè)使用透明的形狀控件復蓋在表格上的表格類(lèi)來(lái)捕捉所有鼠標事件. 采用該方法表格將可以知道表格的 BeforeRowColChange 激發(fā)的原因. 該方法也可以捕捉 KeyPress 事件 (最好是設置表單的 KeyPreview=.T.). 最后, 表格應該放到容器中來(lái)捕捉表格得到焦點(diǎn)時(shí)刻 (BeforeRowColChange 也將在表格得到焦點(diǎn)時(shí)激發(fā)).
兩種方法都有一個(gè)重大的缺點(diǎn): 要求在所有造成重構的地方放置代碼. 當這樣的地方位于多個(gè)表單和類(lèi)中時(shí), 如, 用 SQL Pass-Through 功能重新查詢(xún)一些別名的時(shí), 要定位所有的這些地方是困難的并且它們要求一個(gè)表格的引用來(lái)避免重構. 它有時(shí)也會(huì )造成不希望的列的自動(dòng)再綁定. 表格常用于顯示視圖中的動(dòng)態(tài)數據, 因此它在 requiry 時(shí)會(huì )被其它的數據刷新. 表格的重構在視圖 requiry 時(shí)不會(huì )發(fā)生. 但是, 當移動(dòng)(升級)應用程序到使用遠程視圖時(shí)程序員常常決定使用 SQL Pass Through 功能來(lái)處理數據. 在這樣的情況下各個(gè)被 SQL Pass-Through 使用的別名的 requery 將造成重構. 因此, 程序員在這里的主要錯誤也只是 requery 視圖并象以前一樣放入代碼到這里. 它是一條單一的命令, 因此程序員常常在許多地方放入這樣的命令而沒(méi)有注意到這樣做的不正確之處. 在升級到使用 SQL Pass-Through 功能時(shí)所有 requery 語(yǔ)句應該用適當的命令替換. 另外, 在發(fā)現重構行為時(shí), 程序員開(kāi)始找所有該別名 requery 的地方. 它可能交叉地存在于表單和類(lèi)中的多個(gè)地方, 這就成為一個(gè)大的問(wèn)題. 訣竅: 放置數據 requery, 關(guān)閉, 打開(kāi)和重打開(kāi) (以及其它所有與數據在關(guān)的動(dòng)作) 在一個(gè)地方 - 類(lèi)的方法或函數中, 并用該類(lèi)的對象引用來(lái)調用適當的功能. 總是假設所有數據功能會(huì )在今后的一些附加的代碼中進(jìn)行 requery, 即使當它只是一個(gè)簡(jiǎn)單的視圖的 requery. 使用這種方法將有助于你在你需要修改某些東西而查找所有數據動(dòng)作的地方時(shí)節約時(shí)間. 表格重構是當其出現時(shí)你不能避免的這種情況之一. 例如, 創(chuàng )建一個(gè)具有所有必需的, 處理它顯示的數據的方法的表格類(lèi). 然后使用表格類(lèi)的對象引用. 這樣在所有地方的數據刷新請求將成為一個(gè)單一的功能調用. 好了, 也許你很少創(chuàng )建一個(gè)有如此需求的應用程序…
因表格重構行為的失敗, 程序員有時(shí)創(chuàng )建并維護表格使用的 Record Source 的游標, 然后刷新用刪除和復制來(lái)刷新這樣的游標. 在你已經(jīng)有了這樣的游標的情況下, 從游標中刪除所有的數據并再次添加它們是不困難的.
2. 重構發(fā)生在表格初始化且 record source 屬性為空或 record source 不存在時(shí) (別名沒(méi)有打開(kāi)). 在此情況下表格自己重構并使用當前存在的別名作為 record source (或者在當前工作區中沒(méi)有打開(kāi)表時(shí)保持為空, 但所有的列都不存在了). 如果你需要打開(kāi) record source 在一些其它的事件中(不是表單的 Load 方法中, 在表格初始化前), 使用下面的技術(shù).
在表單的 Load 事件中創(chuàng )建一個(gè)空的與表格使用的 record source 結構相同的游標; 表格的 record source 屬性將使用該空的游標別名. 然后, 當你打開(kāi)真正的數據時(shí), 指定一個(gè)空的串到表格的 record source, 打開(kāi)數據然后再次指定真正的數據別名到表格的 record source. 別一種處理方法是放置一個(gè)不可見(jiàn)的在它的 INIT 事件中創(chuàng )建空的游標的自定義控件. 但是, 要保證該控件的 Init 事件在表格的 INIT 之前激發(fā), 否則將會(huì )發(fā)生重構. 第二種方法是在運行時(shí)添加表格到表單. 創(chuàng )建一個(gè)表格類(lèi)并不在設計時(shí)把它放入表單. 在 Init 事件代碼中放入一個(gè) AddObject 方法調用來(lái)在要使用的別名準備好后添加表格到表單.
3. 表格的自己重構在列數改變?yōu)榱慊?nbsp;-1 發(fā)生. 我希望你不要這樣做(指設置 ColumnCount 為 0 或 -1), 你這樣做了嗎? ;) 總之, 它可以用于簡(jiǎn)單的可以打開(kāi)任何表并在表格中瀏覽它們的管理性表單. 但是, 由于重構的原因, 這樣的表格具有很大的功能限制, 或者所有的表格功能將放入到類(lèi)中并在運行時(shí)的重構后添加到表格.
4. 表格的自己重構將在 record source 超出范圍時(shí)發(fā)生. 這通常發(fā)生在當 record source 指定在一個(gè)數據工作期, 但表格確初始化在另一個(gè)數據工作期中時(shí), 這樣當它試著(zhù)刷新自己時(shí), 被另一個(gè)數據工作期使用的 record source 對于當前數據工作期來(lái)說(shuō)是不存在的. 另一種情況是當程序員使用大量的數據工作期并在其間切換時(shí)發(fā)生.
重構不能以用附加的表格列的引用來(lái)避免它 - 列與表格是分離的. 另外, 讓表格使用已經(jīng)在類(lèi)中定義的列也不能避免重構 - 這是當在表單上使用容器類(lèi)時(shí)容器類(lèi)的子對象不能被刪除這一規則的例外.
別一種流行的消除表格重構問(wèn)題的方法是動(dòng)態(tài)地創(chuàng )建表格. 用你的所有列定義代碼創(chuàng )建一個(gè)自定義表格類(lèi). 當 requery 數據時(shí), 從表單中移去表格控件, requery 數據, 然后在運行時(shí)再次添加表格到表單并設置它的位置. 這要求首先處理表格的添加, 設置一些表格的屬性等等. 但是, 該方法是不好的, 因為需要為表單上的各表格創(chuàng )建類(lèi), 知道類(lèi)的名字, 和表格都要有自己的處理程序 - 不可避免地許多代碼.
也可以在運行時(shí)完全用代碼創(chuàng )建表格對象并用自定義控件裝配它. 但是, 在表格自己重構后, 你需要再次添加這些自定義控件到表格中. 該方法用于表格重構不可避免的情形, 例如, 在管理性程序中 - 要在同一個(gè)表格中顯示任何表的內容, 但也需要一些象使用編輯框來(lái)編輯備注字段和單擊列頭排序這樣的功能時(shí).
VFP 的表格透析2
不管表格如何卷動(dòng)鎖住某列使其總是可見(jiàn)的
在需要鎖住某列而使表格卷動(dòng)時(shí)都可見(jiàn)時(shí), 要記住的第一件事是使用 split 表格. 當用戶(hù)卷動(dòng)表格時(shí)左邊的列總是可見(jiàn)的. 但是, 這讓用戶(hù)看起來(lái)感到相當古怪, 額外的卷動(dòng)條… 等等等等.
表格有一個(gè)很好的屬性 LeftColumn, 它是當前可視的最左邊列的列序號. 當表格橫向卷動(dòng)時(shí), 該值會(huì )改變. 我們可以在 Scrolled 方法中用它來(lái)鎖住某列:
if nDirection>3
this.Columns(1).ColumnOrder = this.LeftColumn
endif
另外, 對于用鍵盤(pán)卷動(dòng)表格, 在 AfterRowColChange 添加一行:
this.Columns(1).ColumnOrder = this.LeftColumn
這樣第一列總是顯示在表格的最左邊!

表格訣竅
我所要說(shuō)的第一個(gè)訣竅不僅僅是相對于表格的. 搜索 UT 站點(diǎn)關(guān)于表格的信息. 你會(huì )找到許多有用的東西! 而且總是可以在這里提出任何問(wèn)題.
在使用 SQL SELECT 語(yǔ)句作為 record source 時(shí), 總是在語(yǔ)句的后面添加 ´INTO CURSOR …´, 否則將會(huì )在表單載入時(shí)或該查詢(xún)運行時(shí)顯示一個(gè)瀏覽窗口.
可以使用表達式作為 column source! 只需使列成為只讀的并在列的 ControlSource 中寫(xiě)入要顯示的表達式. 可惜的是, 表格中所有行中顯示的表達式的結果只能是相同數據類(lèi)型; 否則你會(huì )得到奇怪的格式, 星號或一些其它奇怪的結果. 對于不同數據類(lèi)型最好在表格列中使用容器…
要添加新控件到表格列中, 在屬性窗口的組合框中選擇該列, 單擊正在編輯的表單的標題來(lái)選擇它, 選擇所需的控件并拖動(dòng)它到表格中. 要移去該控件, 在屬性窗口中選擇該控件, 單擊表單的標題來(lái)選擇表單, 然后按下 ´Del´ 按鈕.
在沒(méi)有橫向表格線(xiàn)時(shí), 當 Sparse=.F. 或 HighlightRow=.F. 時(shí), 光標所在的單元格會(huì )顯示一個(gè)灰色的框. 放置一個(gè)白色的 shape 到表格下可以消去灰色的框.
可以在列的 MouseMove 事件中設置 mouse pointer 來(lái)改變鼠標光標.
表格警告
本章為程序員提供關(guān)于表格的警告. 有很多…
*********
記住在設計時(shí)改變 record source 屬性時(shí)需要記住各列的 control source. 因為在 record source 改變后它們都會(huì )被清除! 在列的 Comment 屬性中創(chuàng )建一個(gè) ControlSource 屬性的拷貝是一個(gè)不錯的辦法.
在表格被始化前, 不要讓它的 Record Source 屬性為空.
表格列中的控件的事件和屬性在列的 sparse 屬性為 .T. 時(shí)僅作用于當前單元格. 當 Sparse 為 .F. 時(shí), 屬性用于顯示, 但事件也僅作用于當前單元格.
Scrolled 事件只被 scrollbars 激發(fā). 當表格是被鍵盤(pán)卷動(dòng)或在特定情況下以編程方式卷動(dòng)時(shí), Scrolled 事件不激發(fā).
RelativeRow 和 ActiveRow 屬性?xún)H在表格具有焦點(diǎn)時(shí)可用. 在當前記錄超出可見(jiàn)的記錄時(shí) ActiveRow 總是為零.
*********


VFP 中的表格 第二部分
在該部分中, 我將討論關(guān)于如何使表格開(kāi)發(fā)更快速的列和列頭的竅門(mén). 也將討論關(guān)于確定表格列頭和單元格的確切位置. 象第一章中一樣, 我也包括了一些訣竅和警告.
表格列和列頭的竅門(mén) - 如何使表格開(kāi)發(fā)更快速
許多時(shí)候當你試圖創(chuàng )建一個(gè)簡(jiǎn)單的應用程序來(lái)檢查是否有些事可以用表格來(lái)做或它將看起來(lái)是什么樣的時(shí)候, 你會(huì )發(fā)現需要設置許多東西來(lái)使表格得到需要的外觀(guān). 指定 RecordSource, 為各列指定 ControlSource, 指定各列頭的 caption 等等. 有時(shí)你又需要添加一些功能到表格中, 如象雙擊一個(gè)行以彈出別一個(gè)窗口 - 這也要求添加代碼到表格各列中的控件中. 當只有一個(gè)只有少量列的表格在你的應用程序中時(shí)這不成問(wèn)題. 但是, 當你的應用程序中有大量表格功表格有很多列時(shí), 你會(huì )很快被各表格和列中的這些事情弄得疲憊不堪.
為表格和表格中的控件定義類(lèi)是容易的. 該方法得到了廣泛的應用. 但是, 還有更好的辦法來(lái)組織你的為表格使用的 OOP 的基類(lèi), 這將使得你的應用程序的表格編程有一個(gè)好的基礎.
首先, 定義一個(gè)提供所有功能和抽象方法的表格基類(lèi)模板. 注意你應該保留你的類(lèi)的 ColumnCount 屬性為默認值, 不要在表格基類(lèi)中定義任何列數, 否在在類(lèi)對象被實(shí)例時(shí)將很難處理它們. 調整表格的外觀(guān)為你的應用程序中最常用的情況. 例如, 在我的應用程序中通常我不在表格中顯示記錄標記 (Record Marks) (它們在表格不擁有焦點(diǎn)且當前記錄以另一種顏色高亮顯示時(shí)會(huì )不正確地刷新) 和刪除標記 (表格通常是只讀的控件).
然后, 定義用于表格列中的控件. 通常它是一個(gè)文本框, 但你可能也想使用復選框, 組合框, 編輯框 (用于顯示備注字段內容) 以及, 也許是一些其它不常用的自定義控件. 你將只在表格內部使用這些控件而決不會(huì )在其它地方使用這些控件. 在這些類(lèi)中你可以更好地組織所有表格列中需要的功能. 以調用表格基類(lèi)中的抽象方法的方式來(lái)這樣做. 例如, 要調用雙擊方法, 在各表格列使用的類(lèi)的 DblClick 事件中放入以下代碼:
This.parent.parent.eventDblClick(this.parent)
在以上示例代碼中 eventDblClick 是表格基類(lèi)中的一個(gè)抽象方法 - 除接受單一參數的 ´lparameters´ 語(yǔ)句外它不包含其它代碼 (該參數是被雙擊的列的對象引用). 現在我們將從其中得到什么呢? 沒(méi)有這個(gè)我們不得不為表格用雙擊代碼在表單上定義新的方法, 然后從表格各列中的控件的 DblClick 事件中添加該方法調用. 當你有許多的列時(shí), 這是相當令人厭煩的. 采用這種新的方法你只需要在你的表格基類(lèi)的 eventDblClick 方法中寫(xiě)你的代碼. 好了, 這也要求放置你自己的類(lèi)到列中來(lái)代替默認的文本框, 但這太容易了. 放入以下代碼到你的表格基類(lèi)的 Init 方法中:
LOCAL liColIndex
FOR liColIndex=1 TO this.ColumnCount && 遍歷所有的列
WITH this.Columns(liColIndex)
IF upper(.CurrentControl) == ´Text1´ && 只替換使用默認文本框的列
.Text1.Visible = .F.
.AddObject(´Text2´,´{GridTextBoxClass}´)
.Text2.Visible = .T.
.CurrentControl = ´Text2´
.RemoveObject(´Text1´)
.Text2.Name = ´Text1´
ENDIF
ENDWITH
ENDFOR
上述代碼中 {GridTextBoxClass} 是你的表格文本框類(lèi)的名字. 注意如果你的保存該類(lèi)的類(lèi)庫沒(méi)有載入內存, 你將需要使用 NewObject 方法來(lái)代替 AddObject.
在進(jìn)行了上述簡(jiǎn)單的嘗試后, 讓我們來(lái)添加更多的東西. 定義更多的被表格列中的控件調用的事件. 添加更多的你可能用于表格中的類(lèi), 然后用添加與列的 control source 的數據類(lèi)型相適應的類(lèi)的控件來(lái)增強替換默認控件的代碼(你可以用 type 函數來(lái)檢查列的 control source 的數據類(lèi)型, 如: type(.ControlSource)). 通常復選框用于邏輯字段而編輯框用于備注字段.
現在我們的表格更易于在表單上使用了, 但是... 表格列頭怎么辦? 一個(gè)好辦法是為表格列頭寫(xiě)一些代碼來(lái)自動(dòng)地用列的 control source 來(lái)寫(xiě)入列頭的 caption 屬性. 例如, 在檢查了 ControlSource 是字段而不是表達式后用 DBGETPROP(.ControlSource, ´FIELD´,´Caption´) 來(lái)從數據庫中獲取字段的 caption. 表格的 Init 中的示例代碼如下:
LOCAL liColIndex, lcCaption
FOR liColIndex=1 TO this.ColumnCount && 遍歷所有的列
WITH this.Columns(liColIndex)
&& 檢查列頭是不沒(méi)有被開(kāi)發(fā)者設置
IF upper(.Header1.Caption) == ´HEADER1´
&& 檢查 control source 是否是真正的字段而不是表達式
IF ´.´ $ .ControlSource AND ;
FSIZE(substr(.ControlSource,at(´.´,.ControlSource)+1),.parent.RecordSource) > 0
&& 檢查別名是不是在 VFP 的數據庫中
IF !empty(cursorgetprop(´Database´,.parent.RecordSource)
lcCaption = DBGETPROP(.ControlSource,´FIELD´,´CAPTION´)
ELSE
lcCaption = ´´
ENDIF
IF empty(lcCaption)
&& 沒(méi)如沒(méi)有其它情況只指定字段名
lcCaption = substr(.ControlSource,at(´.´,.ControlSource)+1)
ENDIF
.Header1.Caption = lcCaption
ENDIF
ENDIF
ENDWITH
ENDFOR
這是最簡(jiǎn)單的方法. 為了正確用 DBGetProp 獲取當前數據庫設置要求當前有一個(gè)打開(kāi)的數據庫也是必須的.
你可能做得更多. 例如, 在使用字段名作為列頭的 caption 時(shí), 對它們進(jìn)行一些復雜的轉換以使它們看起來(lái)更好一些, 對字段名使用命令約定. 作為命名約定的示例如下: 在數據庫中所有除主關(guān)鍵字段外的字段以數據類(lèi)型字符為前綴. 主關(guān)鍵字中用 ´ID´ 來(lái)標識它. 因此我們可以從字段名中移去第一個(gè)字符作為列頭的 Caption(當字段名中不包含 ´ID_´ 或 ´_ID´ 或它本身來(lái)是 ´ID´ 時(shí)). 然后用 STRTRAN 替換所有下劃線(xiàn)為空格并用 ´PROPER()´ 函數來(lái)獲得最終結果. 采用該方法 ´cCust_Name´ 將在列頭中顯示為 ´Cust Name´. 你可以為字段使用其它的命名約定, 看看是否可以改進(jìn)列頭的顯示. 如果你有 StoneField Database Toolkit (注:即眾所周知的 SDT) 或你自己的數據這典, 你可以查詢(xún)數據字典來(lái)獲得字段的 caption 和任何其它的你想應用于列頭的和用于顯示數據的字段屬性. 如果字段來(lái)自視圖, 你可以從表中獲取它的源字段來(lái)獲取它的屬性. 你可以從視圖字段上用 "DBGETPROP(.ControlSource, ´Field´, ´UpdateName´)" 來(lái)從表中獲取VFP 的表格透析3
好了, 對于列頭的 captions - 你可以在表格基類(lèi)的 Init 中設置它們然后忘掉它們. 但是, 還有一些有用的捕捉列頭的 Click 和其它事件事情. 在易于創(chuàng )建表格列控件類(lèi)時(shí), 創(chuàng )建 header 類(lèi)是相當難處理的. 你可以用相似的方法在你的表格基類(lèi)中為列頭組織事件系統; 但是, 你可以在程序文件中用 DEFINE CLASS 結構以簡(jiǎn)單的方法定義你自己的 header 類(lèi), 如下所示:
DEFINE CLASS MyHeaderClass AS HEADER
PROCEDURE DblClick
This.parent.parent.eventHeaderDblClick(this.Parent)
ENDPROC
ENDDEFINE
如果你熟悉 VCX 文件結構, 你可以在 VCX 庫中添加兩條記錄并指定其基類(lèi)為 header 來(lái)創(chuàng )建 header 類(lèi). 但是這樣一來(lái), 你除了象編輯 VFP 表一樣編輯它以外, 將不能再以其它方式編輯它. 因此最好還是首先在 PRG 文件中定義它用于開(kāi)發(fā)和調試. 最后, 當它測試完成后, 如果需要的話(huà)你可以從 PRG 中移運它們到 VCX 中. 為什么你會(huì )要它們在 VCX 文件中的理由是簡(jiǎn)單的 - 你可以在設計時(shí)從控件工具欄拖放它到表格列中, 這樣將在設計時(shí)以新的 header 類(lèi)替換默認的 header. 我不知道還有其它的理由了... 對于運行時(shí), 以下代碼需要添加到前述的遍歷代碼中 (改變默認控件為你自己的控件). 也為列頭類(lèi)添加它們:
IF upper(.Header1.Class) == ´HEADER´    && 僅替換默認的 header 類(lèi)
lcOldCaption = .Header1.Caption       && 保存 caption 以應用它們到新的 header
.AddObject(´Header2´,´MyHeaderClass´) && 這將自動(dòng)移除原有的 header
.Header2.Name = ´Header1´
.Header1.Caption = lcOldCaption
ENDIF
你可能也想復制原表格列頭的其它屬性到你用新類(lèi)創(chuàng )建的新的列頭對象. 也請注意到不需要移除原有的列頭因為 VFP 會(huì )自動(dòng)移除它 - 來(lái)保持列中只有一個(gè)列頭. 對于 AddObject 定義列頭類(lèi)的 PRG 文件必須事先用 SET PROCEDURE TO ?ADDITIVE 命令載入內存, 但是你可以決定用列的 NewObject 方法.
在一般情況下, 建議把替換表格中的控件為你自己的類(lèi)的代碼移動(dòng)到一個(gè)單一的表格的方法中. 為什么需要這樣呢 - 表格重構. 少數場(chǎng)合下表格重構是必需的. 在表格重構后你可以調用表格的方法來(lái)再次設置你自己的類(lèi), 因此即使當你在相同的表格控件中顯示不同的數據時(shí), 它仍然會(huì )以相同的方式運行. 這是因為, 盡管進(jìn)行了重構, 事件的主要代碼是在表格控件的方法中, 因此在重構后它們不會(huì )丟失. 更多的是, 在你運行了替換表格控件為你自己的類(lèi)的代碼后, 表格仍然具備所需的功能. 因此代替用不同的數據源創(chuàng )建多個(gè)表格類(lèi)在同一個(gè)表單中來(lái)顯示這些不同數據源, 你現在可以放置一個(gè)單一的表格并用不會(huì )丟失太多功能的方法來(lái)重構它; 然后為每一個(gè) data source 來(lái)創(chuàng )建新的表格類(lèi)并增強不同數量的 record sources.
不好的事情是列頭的 Click 事件也會(huì )在表格列 resize 或移動(dòng)時(shí)發(fā)生. 列具有 Moved 和 Resized 事件, 但是... 當你用自己的列頭類(lèi)替換了默認的列頭類(lèi)后, 很難使用列的這些事件, 而且它們的運行速度會(huì )變慢. 因此看來(lái)使用列頭類(lèi)并不好. 但是有好的解決辦法. 在你的新的列頭類(lèi)中比較 OldColumnWidth 和 OldColumnOrder 屬性. 然后在列頭的 Init 中設置它們?yōu)榱械?nbsp;Width 和 ColumnOrder 屬性的初始值. 在 Click 事件代碼中比較你的列頭類(lèi)的當前的 Width 和 OldColumnWidth 屬性值及 ColumnOrder 和 OldCoumnOrder 屬性值. 當值與原值不同時(shí), 則列是 resized 或 moved 了的. 在 resized 時(shí), 只用當前列的 OldColumnWidth 來(lái)更新它. 當列 moved 時(shí) - 你需要為列頭更新表格中的所有列的 OldColumnOrder 屬性. 好了, 在一定情況下列的寬度和列序現在可以用編程方法來(lái)修改了, 最好是不要直接這樣做, 而是使用表格類(lèi)中的一個(gè)自定義方法調用, 例如, ´ChangeColumnWidth(ColumnNumber)´, 或 ´ChangeColumnOrder(ColumnNumber)´. 采用此方法你可以保證 OldColumnWidth 和 OldColumnOrder 總是與列的設置同步. 而且現在你有機會(huì )用上述方法在列移動(dòng)或 resize 時(shí)來(lái)激發(fā)你的表格類(lèi)的自定義方法中的代碼了 - 沒(méi)有真正接觸到任何列的方法!
為了加快輸入到列的 ControlSource 中, 你可以用簡(jiǎn)單的方法. 在你的表格類(lèi)中定義一個(gè)屬性 (ControlSourceList) 它是用于保存各列使用的字段名的以分號分隔的列表. 使用分號而不是逗號是因為你可能會(huì )在一些 control source 中使用包含逗號的表達式. 在 Init 事件中分解該串到各屬性中(如果它不為空), 然后用串中的列表中的項填充各列的 ContolSource 屬性. 注意表格的任何刷新將不會(huì )先于該操作, 因為表格的初始化在默認情況下使用字段在表中的物理順序 (列的重綁定 - 參見(jiàn)文章前面的描述). 當你的表格中有自定義控件時(shí), 可能列會(huì )使用對于該控件來(lái)說(shuō)是不正確的字段類(lèi)型并在刷新時(shí)造成不正確的數據類(lèi)型錯誤 (Refresh 極少在表格的 Init 事件中發(fā)生, 但是, 在復雜的類(lèi)代碼中有時(shí)可能會(huì )發(fā)生). 好了, 在設計時(shí)屬性表中的屬性長(cháng)度限制是 255 字符, 因此在表格有許多列時(shí)將不能這樣做. 在此情況下當你在表單中使用表格時(shí)可以在表格的 Init 中用代碼來(lái)指定長(cháng)的串到屬性, 然后用 DODEFAULT() 來(lái)調用父類(lèi)代碼. 你也可以在串中不用字段名前的別名來(lái)節約空間; 別名將被表格自動(dòng)指定. 任何情況下, 在 ControlSource 中的表達式中的字段名必須包括別名.
最后, 說(shuō)說(shuō)不同類(lèi)型的 record source. 當 record source 是別名時(shí), 沒(méi)有問(wèn)題. 但是當你打開(kāi)一個(gè)以表名以數值開(kāi)始的表 record source 時(shí), VFP 會(huì )以另一個(gè)別名來(lái)打開(kāi)它. 表格中的 RecordSource 中的 SQL SELECT 語(yǔ)句也有類(lèi)似情況. 幸運的是, 有一種簡(jiǎn)單的方法來(lái)檢查表格使用的別名的正確性. 別名可以用任何列的 ControlSource 屬性來(lái)檢查. 表格會(huì )自動(dòng)添加 "{alias}." 到所有列中的 control sources 字段前. 你可以從列的 control sources 取出別名(當它們是簡(jiǎn)單格式而不是復雜表達式時(shí)) . (如何檢查 control source 是否是一個(gè)字段引用我已經(jīng)說(shuō)過(guò)了.) 好了, 當要用表格顯示一些用 SET RELATION aliases 關(guān)聯(lián)的表時(shí), 這將不能工作, 但是, 顯示在表格中的關(guān)聯(lián)表通常使用別名作為 record source 類(lèi)型. 那么, 在我們的程序中用一個(gè)自定義屬性 (MainAlias) 來(lái)代替 RecordSource, 用它來(lái)保存表格使用的真實(shí)的別名. 我們也還定義了 RecordSource_Assign 代碼來(lái)捕捉 record source 的改變并更新我們的自定義屬性.
字段名. 這僅適用于可更新字段, 作為表達式生成的字段用上述方法返回的結果將是一個(gè)空串.
VFP 的表格透析4
確切定位表格列頭和單元格
表格中的控件的功能可能與表格外的控件的功能不同. 事實(shí)上關(guān)于這一點(diǎn) VFP 有一些冗余, 致使一些功能在表格中不能正確運行. 我不打算在這里一一列舉, 但將說(shuō)明一種好的方案 - 把控件放在表格外并在表格單元格上顯示它來(lái)進(jìn)行數據編輯. 采用該方法控件將象單獨的控件一樣編輯數據, 而不會(huì )象表格列中的控件一樣有一些功能不能使用. 現在對每一列采用此方法而你將不再受到表格的限制. 但是, 問(wèn)題是復蓋在表格單元格上的控件的相對位置. 這就要求計算表格中的單元格的準確位置以便用你的控件來(lái)復蓋它. 這個(gè)事情不太好做, 列序可能改變了, 表格可能卷動(dòng)了, 僅獲得焦點(diǎn)的表格可以計算行號等等. 在這里我說(shuō)明一種如何計算位置的一般方法.
另一個(gè)要求計算準確位置的理由是放置排序標記到表格列上來(lái)指明表格是按哪一個(gè)列進(jìn)行排序的. 對于這一點(diǎn)只要求計算列的位置.
位置的計算將在表格卷動(dòng)后, 一些列寬改變后, 一個(gè)列移動(dòng)后, 記錄標記和刪除標記的可視狀態(tài)改變后, 列高和行高改變后, 表格被分割后 (表格的 Partition 屬性設置為非零值). 你可以捕捉這些事件:
* 表格卷動(dòng)在 Scrolled 事件中, 如果是用鍵盤(pán)產(chǎn)生的自動(dòng)卷動(dòng)在 AfterRowColChange 事件中. 因列移動(dòng)造成的卷動(dòng) - 捕捉列移動(dòng)或改變用 LeftColumn 屬性 (保存它們的原值). 其它原因造成的自動(dòng)卷動(dòng)則相當少見(jiàn).
* 列的移動(dòng)和寬度調整在列的事件中或用本文中前面描述過(guò)的方法
* 記錄標記和刪除標記的改變, 以及卷動(dòng)欄的以編程方式的改變; 你可以在表格類(lèi)中定義相應屬性的 Assign 方法來(lái)捕捉它們
* Partition, RowHeight 及 HeaderHeight 改變 - 在表格的 MouseUp 事件中用類(lèi)似于前述的列頭方法來(lái)捕捉它們 - 保存原值到表格屬性中并在 MouseUp 事件中用新值比較它們以確定是否發(fā)生了變化.
因為要在許多地方計算來(lái)更新控件的位置, 在計算列的位置時(shí)應該盡可能地快; 否則在表格中的列數及計算位置的控件很多時(shí)會(huì )比較慢.
以下代碼中的算法檢查列的大小和右邊沿. 它是最快的并經(jīng)長(cháng)期使用的方法. 它計算表格除 split 外的所有表格設置. 代碼中的注釋說(shuō)明了算法.
Procedure ColumnRightPosition
* returns position in pixels of right edge of column relative to the grid rectangle area
* returns 0 if column is outside of visible area of grid
* parameters:
*   toColumn - reference to the column for which position should be calculated
lparameters toColumn
with this && "this" here is a grid reference
local lnIndex, lnColumn, lnColumnCountToLeft, lnPosition, lnVAreaWidth
local array laColumnsOrder(.ColumnCount)
&& fill array by column order numbers with column indexes in a single
&& array column. Note that we get only columns that could be visible and skip
&& columns that are not visible for sure (that have ColumnOrder= .LeftColumn
m.lnColumnCountToLeft = m.lnColumnCountToLeft + 1
m.laColumnsOrder[m.lnColumnCountToLeft] = ;
.Columns(m.lnColumn).ColumnOrder * 10000 + m.lnColumn
endif
endfor
m.lnPosition = 0
if m.lnColumnCountToLeft >0
&& truncate array from rest of unnessesary values
dimension laColumnsOrder(m.lnColumnCountToLeft)
&& Main trick here - use asort() VFP function. It will quickly
&& change order in array from column index to the order by
&& ColumnOrder. This will help us to reference each column
&& after LeftColumn in their visibility order.
asort(laColumnsOrder)

&& scan columns in the visibility order and increment width (result)
&& until we reach our column or right edge of the grid.
m.lnVAreaWidth = .Width - iif(.RecordMark, 10, 0) + ;
iif(.DeleteMark, 8, 0) + ;
iif(.ScrollBars>1,sysmetric(5),0) && Width of the visible portion of grid
&& note we now go through array of columns starting from left visible in order
&& of columns, i.e. first value appropriate to column .LeftColumn
for m.lnIndex = 1 to m.lnColumnCountToLeft
m.lnColumn = m.laColumnsOrder[m.lnIndex] % 10000
m.lnPosition = m.lnPosition + .Columns(m.lnColumn).Width + 1
if m.toColumn = .Columns(m.lnColumn) or m.lnPosition > m.lnVAreaWidth
&& so we will no need to scan remained columns
exit
endif
endfor
&& if we reached required column
if m.toColumn = .Columns(m.lnColumn)
&& if the column is last, its width is cut to fit into grid visible area
m.lnPosition = min(m.lnVAreaWidth,m.lnPosition)
if m.lnPosition > 0
&& add left grid edge controls width if they are shown
m.lnPosition = m.lnPosition + iif(.RecordMark, 10, 0) +;
iif(.DeleteMark, 8, 0)
endif
else
m.lnPosition = 0
endif
endif
endwith
m.toColumn = ´´
return m.lnPosition
當前表格行的位置只有在表格獲得焦點(diǎn)時(shí)可以檢查到. 這是因為我們只能用表格的 RelativeRow 屬性來(lái)計算它, 當表格不擁有焦點(diǎn)時(shí)計算值總是零. 這種方法更簡(jiǎn)單且更適于放入單個(gè)的表達式:
* 計算表格行相對于表格矩形區域的 top 位置, 單位是 pixels
* 如果位置在可視的區域外則返回零
Local lnRow
m.lnRow = this.RelativeRow
m.lnRow = this.RelativeRow
if m.lnRow=0
return 0
esle
return this.HeaderHeight + this.RowHeight*(this.RelativeRow-1)
endif
注意為了得到正確可靠的值, 處理中的表格獲得焦點(diǎn) RelativeRow 屬性應該訪(fǎng)問(wèn)兩次.
下圖顯示了表格屬性及它們的尺寸. 有助于更好地理解示例代碼.
VFP 的表格透析5
表格訣竅
表格的 Format 屬性設置為 ´Z´ 時(shí), 當 Sparse=.T. 時(shí)不顯示零值
是不是對表格列中顯示的備注字段為 ´Memo´ 感到很厭倦? 你可以用類(lèi)似于 "PADR({MemoField},200)" 這樣的表達式來(lái)顯示備注字段. 也可以在列的 sparse 屬性設置為 .F. 時(shí)在列中用 EditBox 來(lái)允許對它們進(jìn)行編輯, EditBox 可以比用表達式作為列的 ControlSource 時(shí)顯示更多的文本. 在表格中的 EditBox 中卷動(dòng)欄僅在表格的 RowHeight 屬性大于三行文本的高度時(shí)才會(huì )顯示.
在表格可以完整地顯示所有列而沒(méi)有橫向卷動(dòng)欄時(shí), 你可以設置所有列的寬度小一些來(lái)讓所有的列完全顯示在顯示區中以避免當最后一列得到焦點(diǎn)時(shí)表格的自動(dòng)卷動(dòng).
表格警告
在使用行緩存時(shí)刷新表格會(huì )造成數據的自動(dòng)提交(Update). 這是因為在 VFP 內部, 表格會(huì ) scan 別名中的要顯示的記錄, 這會(huì )造成記錄指針的移動(dòng), 并因此造成數據的自動(dòng)提交. 在用表格工作時(shí), 別名應該使用表緩存模式.
在特定情況下, 當列的 ControlSource 返回的字符長(cháng)于 200 字符且列的 sparse 屬性設置為 .T. 時(shí)表格會(huì )崩潰或不正常運行. 在此情況下用類(lèi)似于 "PADR(...,200)" 這樣的表達工作為表達式來(lái)顯示數據. 如果要在這樣的情況下編輯數據, 在表格列中用 EditBox 來(lái)代替 TextBox 并設置列的 Sparse 屬性為 .F.. 當 SET DELETED 為 ON 時(shí), 被刪除的記錄只有在記錄指針移動(dòng)后才會(huì )不顯示出來(lái). 在刪除后如果記錄指針在被刪除的記錄上時(shí), VFP 保持記錄的可見(jiàn), 包括在表格中.


VFP 中的表格 第三部分
在該部分中, 我將描述關(guān)于單擊列頭時(shí)表格排序, 表格的排序標記 (指示符) 和雙擊列頭時(shí)讓表格列的寬度與列中的數據寬度一致. 按照慣例, 將包括一些訣竅和警告.
單擊列頭排序
上面提到的單擊列頭排序表格和列表中的項是很流行的做法. 該功能在有成百上千的行列表中搜索某些東西時(shí)特別有用. VFP 的表格沒(méi)有內置該功能, 但可以編程方式來(lái)實(shí)現.
有多種現存的方法來(lái)達到此目的. 我們將不包含所有這些方法, 而只是說(shuō)明最常用對于創(chuàng )建具有排序功能的表格類(lèi)有用的方法. 這意味著(zhù)描述的方法中將去掉它們中太復雜和不常用的部分. 建議將此功能作為你的應用程序中的表格基類(lèi)的一部分這樣你可以在多個(gè)應用程序中重用它們.
首先, 準備一個(gè)列頭的 Click 事件來(lái)在正確的時(shí)候激發(fā)排序功能. Click 事件也會(huì )在列重調大小和移動(dòng)時(shí)激發(fā), 因此你需要使用一些特殊的類(lèi)似于本文前面描述過(guò)的方法 - 使列頭類(lèi)跟蹤列的重調大小和移動(dòng)來(lái)區別它只是在單擊列頭. 當然, 這將要求表格中所有其它的東西支持并維護用新的列頭類(lèi)替換默認的列頭類(lèi). 無(wú)論如何如果你想在任何表格中使用排序功能的話(huà)這是必需的. 當列是可調整大小的時(shí)候, 單擊 resize 區域而不調整列的寬度也會(huì )被捕捉到 - 因此在鼠標的光標是 resizeg 箭頭是對列進(jìn)行排序不是一個(gè)好的辦法, 這會(huì )把用戶(hù)搞糊涂 (我們也為本文稍后論及的另一個(gè)功能保留這一點(diǎn)). 表格列頭的 resize 區域是 11 象素寬 (列頭線(xiàn)的左邊 6 象素 4 象素右邊). 當你單擊 resize 區而沒(méi)有進(jìn)行 resize 時(shí), 僅管鼠標此時(shí)已經(jīng)在下一個(gè)列頭上, 鼠標和單擊事件僅在可調寬度的列頭區被激發(fā). 在示例中你可以看到在列頭類(lèi)中是如何進(jìn)行跟蹤的 - 使用 MouseUp 事件我們得到鼠標指針的座標并檢查它是否只是在調整區域進(jìn)行了單擊. 如果是, 設置特殊的標志這樣在 Click 和 DblClick 事件中我們可以檢查鼠標是否是在調整區.
一但純粹的單擊被從所有其它動(dòng)作中分離出來(lái)后, 執行排序. 它由三部分組成 - 排序管理器, 排序例程和排序后的表格刷新.
排序管理器跟蹤當前要排序的是哪一個(gè)列, 為排序定義屬性關(guān)保存一個(gè)當前排序的狀態(tài). 要這樣做, 使用了以下屬性:
Grid.SortedColumn - 包含當前排序列的索引如果沒(méi)有排序它將是零. 索引將用于快速跟蹤哪一個(gè)列是最后排過(guò)序的并在另一個(gè)列被為排序而單擊時(shí)關(guān)閉該列的排序 (盡管這不是必需的 - 排序列的索引將在一段簡(jiǎn)單的遍歷所有列并檢查 SortingState 屬性值的代碼中被檢查).
Grid.lAllowSorting - 默認值為 .T. - 允許廢止對整個(gè)表格的排序.
header.lAllowSorting - 默認值為 .T. - 允許廢止對該列的排序.
Header.SortingState - 0 - 未排序, 1 - 按升序排序, 2 - 按降序排序. 排序管理器將卷動(dòng)這些值并在需要時(shí)調用排序例程.
Header.DefaultSorting - 該屬性決定在列頭被單擊時(shí)使用什么樣的排序. 例如, 首選的日期排序在默認情況下是用降序, 因此你可能想改變該屬性從默認的 1 到 2  - 這將得不斷單擊列頭時(shí)的排序的排序順序由 {無(wú)排序}->{升序}->{降序} 變?yōu)?nbsp;{無(wú)排序}->{降序}->{升序}.
Header.lEventSwitchOffSorting - 一個(gè)事件 - 如果任何排序的特定處理造成了速度緩慢或錯誤, 關(guān)閉排序的屬性并去掉所有附加的索引. 它在以 Grid.SetAll("lEventSwitchOffSorting",.T.) 的格式調用被列頭中的該屬性的 _Assign 方法跟蹤. 該屬性在表格類(lèi)中, 當 SetAll 函數被調用時(shí)表格的列頭還沒(méi)有準備好的情況下也是需要的 (否則該屬性未找到任何控件時(shí), VFP 有時(shí)會(huì )出現錯誤提示).
在最復雜的情況下列會(huì )從列格中移去, 因此 SortedColumn 屬性可能要求在進(jìn)行這種處理后進(jìn)行更新. 在這樣的情況下, 建議使用表格中的一個(gè)方法來(lái)掃描所有的列, 并用正確的, 包含非零排序狀態(tài)的列的索引來(lái)更新 SortedColumn 值. 好了, 你可以用另一種方法來(lái)跟蹤當前排序列的索引 - 使用一個(gè)返回當前排序列的索引的簡(jiǎn)單的循環(huán).
排序例程是該功能的主要部分. 有多種方法來(lái)實(shí)現表格中的數據的排序: - 保存數據到一個(gè)數組并在表格中顯示數組內容, 用 asort() 函數對數組進(jìn)行排序. 該方法有在些時(shí)候比較慢且限制了顯示的記錄數是數組元素的最大值. - 為排序而用另一個(gè)選項重新查詢(xún)視圖或 SQL Pass-Through 結果集. 對于大的數據集來(lái)說(shuō)這會(huì )比較慢, 但這是最簡(jiǎn)單的多列排序方法. - 使用在運行時(shí)為結果集創(chuàng )建的索引或已存在的索引. 該方法最快因為它不保存任何中間的結果到內存中且不要求再次從服務(wù)器查詢(xún)數據, 但要求編寫(xiě)一些代碼.
VFP 的表格透析6
在本文中我們只討論使用索引的方法. 其它的方法也可以用替換排序例程中的數據集來(lái)實(shí)現, 且該方法在排序后刷新表格.
在排序例程中使用了以下屬性:
Header.CurrentTag - 該屬性用于保存一個(gè)用于該列排序的標識名.
Header.SortingExpression - 當該屬性不為空時(shí), 它的表達式將用于排序. 當它為空時(shí), 排序例程將用列的 ControlSource 來(lái)創(chuàng )建排序表達式.
Header.SortingTag - 如果該屬性非空, 該屬性中的索引標識名將被認為已存在于 record source 中. 為什么要在該字段已經(jīng)存在一個(gè)索引時(shí)還要在運行時(shí)創(chuàng )建一個(gè)索引標識呢?
最后兩個(gè)屬性給了排序編程以更多的選擇. 例如, 當一個(gè)表格包含 First Name 和 Last Name 兩個(gè)列時(shí), 無(wú)論是哪一個(gè)列被單擊了, 按 First Name + Last Name 來(lái)排序是一個(gè)不錯的想法. 當然, 它可能要求額外的排序指示器和代碼, 當默認的排序例程不能適當地排序列時(shí)最好保持該屬性為空. 我們將在稍后討論多列排序.
排序例程檢查是否 SortignTag 或 CurrentTag 屬性非空, 并使用這些標識來(lái)排序. CurrentTag 在列對象的生存期間不會(huì )被清除, 或在要求刪除該臨時(shí)標識的特殊情況出現前不會(huì )被清除 (這會(huì )在設置 lEventSwitchOffSorting 屬性為 .T. 時(shí)發(fā)生). 這是可重用排序 - 我們只創(chuàng )建索引一次 (這會(huì )花一些時(shí)間), 保存創(chuàng )建的索引標識名到該屬性中, 然后在下一次需要排序時(shí)只需重用它而不再花時(shí)間. 當 CurrentTag 屬性為空時(shí), 例程將為記錄源創(chuàng )建一個(gè)索引標識并保存標識名到該屬性中.
當指定了索引表達式時(shí), 例程將只使用它而不進(jìn)行驗證 (但基本的錯誤跟蹤仍然會(huì )進(jìn)行). 否則排序例程將試圖檢查所有的詳情來(lái)創(chuàng )建表達式, 包括 NULL 值和 SET COLLATE 設置. 忘住最大索引表達式長(cháng)度為 240, 但在 SET COLLATE 設置為非 "MACHINE" 時(shí)下降到 120. 索引不接受 NULL 值, 因此我們添加 NVL() 函數來(lái)假定沒(méi)有 null 值出現在結果集中. 對于長(cháng)字符串和備注字段我們用 PADR() 函數. 最后, 當 ControlSource 值包含表達式而不是字段時(shí), 我們對字符值使用 PADR() 來(lái)保證表達式結果的長(cháng)度總是相同的. 當然, 我們不能對通用字段排序.
對于不同的記錄源進(jìn)行索引還有一個(gè)重大區別. 對于視圖, 游標和 SQL Pass-Through 游標, 我們可以創(chuàng )建結構化索引標識而不會(huì )有任何問(wèn)題, 因為這些索引標識產(chǎn)生的文件會(huì )在記錄源關(guān)閉時(shí)自動(dòng)從磁盤(pán)刪除. 但是, 帶表緩存的視圖不能用 INDEX 命令創(chuàng )建索引. 我們可以快速地臨時(shí)切換到行緩存, 對視圖進(jìn)行索引, 然后再次設置緩存為表緩存. 但是如果視圖包含未提交的修改,改變緩存模式會(huì )造成錯誤. 好了, 你可以告訴用戶(hù)該表單上的該表格在數據被修改且未保存時(shí)不能排序, 或者只在打開(kāi)視圖且用各列頭的 SortingTag 值來(lái)創(chuàng )建索引, 然后設置緩存模式為表緩存.
為一個(gè)表創(chuàng )建結構化索引不是一個(gè)好的辦法, 因為 ControlSource 中的表達式可能包含對其它字段的引用并有可能是關(guān)聯(lián)表中的字段, 因此在我們創(chuàng )建索引后, 其它打開(kāi)表的用戶(hù)將因此而發(fā)生錯誤. 另外, 創(chuàng )建結構化索引要求對表的獨占使用權. 對于這種情況, 我們使用保存在臨時(shí) CDX 文件中的非結構化索引標識, 并以相同的名字作為索引標識. 這會(huì )產(chǎn)生一些其它可能的問(wèn)題. 在數據工作期中包含了打開(kāi)了非結構化索引的別名時(shí), "BEGIN TRANSACTION" 不能啟動(dòng)事務(wù)處理. 在該命令前你必須關(guān)閉所有非結構化索引. 另外, 最好是不要把包含上萬(wàn)條記錄的數據集整個(gè)顯示給用戶(hù). 準備一個(gè)篩選條件并從大的表中只選擇一個(gè)小的數據子集到表格中. 這樣會(huì )使速度更快, 并沒(méi)有直接訪(fǎng)問(wèn)表的問(wèn)題, 如象這種情況下的非結構化索引標識問(wèn)題. 另外, 通過(guò)網(wǎng)絡(luò )訪(fǎng)問(wèn)來(lái)索引大的表速度會(huì )相當慢. 總之, 在你必須對表進(jìn)行排序時(shí), 盡可能地試著(zhù)使用已經(jīng)存在于表中的索引標識, 然后關(guān)閉表中沒(méi)有索引標識的列的排序. 另一個(gè)方案是 - 在開(kāi)始事務(wù)處理前, 使用 Grid.SetAll("lEventSwitchOffSorting",.T.) 這樣表格將刪除可能存在的非結構化索引. 這將要求特別關(guān)注從使用表格的表單中調用的子表單中的事務(wù)處理.
當記錄源中包含 1000 以上的記錄時(shí), 我們用列頭的 DispSortingMessage 方法來(lái)顯示信息, 在默認情況下在排序期間顯示一個(gè)簡(jiǎn)單的 "WAIT WINDOW" 信息. 你可能準備用進(jìn)度條來(lái)顯示索引進(jìn)度或用一些其它方法來(lái)指明索引(排序)進(jìn)度. 另外, 也可以在索引表達式中用自定義函數調用來(lái)顯示排序進(jìn)程: 函數將被每一個(gè)被索引的記錄調用, 函數中的代碼更新圖形化的進(jìn)度條 (在此情況下會(huì )稍稍降低索引速度).
當以該方法對視圖, 游標或 SQL Pass-Through 游標進(jìn)行排序時(shí), 排序速度是令人驚異的. 當表在本地磁盤(pán)上時(shí)對表排序上很快的, 但通常通過(guò)網(wǎng)絡(luò )訪(fǎng)問(wèn)數據庫和它的表時(shí)速度是慢的. VFP SELECT 語(yǔ)句的結果游標也會(huì )被排序. 但是, 對結果集使用 NOFILTER 選項, 否則因為直接通過(guò)網(wǎng)絡(luò )訪(fǎng)問(wèn)表, 索引時(shí)會(huì )變慢 (另外使用非結構化索引不是一個(gè)好的辦法, 已在前面描述).
在排序后需要正確地刷新表格. 有這樣的情形, 改變排序的方向會(huì )給表格行顯示帶來(lái)強烈影響. 例如, 以升序排序, 將當前排序結果的第一條記錄放在第一行, 然后再次按降序排序. 通常表格在這種情況下只顯示單一的行 - 降序排序的最后一行, 在它的下面是空白的. 用戶(hù)在任何情況下可以使用卷動(dòng)欄來(lái)使表格返回到好看的狀態(tài), 但是, 最奇怪的事情是, 表格常常在記錄源中的所有的行都可以顯示下時(shí)顯示該行為. 假設這樣的情形: 當你看表數據源中所有的行都顯示在表格中, 單擊排序, 這時(shí)你只看到一行... 這通常會(huì )把用戶(hù)搞糊涂 - 為什么在所有內容都顯示得下時(shí)表格會(huì )卷動(dòng)? 要修正表格的該行為, 我們使用一個(gè)表格的額外的刷新: 設置記錄指針到當前排序結果中的第一條記錄, 然后再返回到當前記錄. 這假定當前記錄是在表的當前的可視區內或所有行都顯示在表格中. 這也可有更多的改進(jìn) - 當記錄在新排序的記錄源的尾部時(shí), 用該方法顯示最后一條記錄(當表格底部有一部分沒(méi)有記錄的空白區域時(shí)). 要這樣做, 萬(wàn)一記錄是在表格的底部, 移動(dòng)記錄指針到頂部, 再移動(dòng)它回到表格當前可視區中的記錄數減半的位置, 然后設置記錄指針回到希望的位置. 注意這只在記錄指針在接近最后一條記錄時(shí)有用. 向后和向前移動(dòng)記錄指針在特定的記錄源中可能會(huì )比較慢, 特別是有很多的記錄或設置了篩選的記錄源時(shí), 因此這不是一個(gè)好的通常情況下的辦法.
VFP 的表格透析7
以上方法對一按單一的列排序是好的. 當你想按多列進(jìn)行排序時(shí), 你需要從當前加入到排序表達式中的且排序狀態(tài)非零的所有列中收集所有的排序表達式. 這是必需的, 因為用戶(hù)可能關(guān)閉了該部分列的排序以保證其它列的正確排序. 在此情況下, 重使用已存在的索引標識是非常困難的, 因為單個(gè)的列現在使用一些排序表達式. 通常這要求轉換所有的表達式為字符型并限制它們的長(cháng)度不大于 240 字符 (或在使用了 比較序列時(shí)為 120 字符). 另外, 很難實(shí)現此種情況下的某列要求降序排列而其它列使用升序排列的要求. 這要求以降序轉換表達式的值. 對于不同的數據類(lèi)型轉換是不同的. 例如, 數值型的值委容易用 "-" 操作符進(jìn)行轉換. 在我們的情況中將只處理字符型的值. 在此情況下我們需要改變 "A" 為 "z" 等等. 你可以用 chrtran() 函數來(lái)快速地比較兩個(gè)串. 例如:
"Control" > "Binding"
chrtran("Control", cAllChars, cAllReverseChars) < chrtran("Binding", cAllChars, cAllReverseChars)
cAllChars and cAllReverseChars are prepared by following way:
cAllChars = ""
cAllReverseChars = ""
for nCharIndex = 0 to 255
cAllChars = cAllChars + chr(nCharIndex)
cAllReverseChars  =cAllReverseChars + chr(255-nCharIndex)
endfor
因此, 例如, 索引表達式為兩列 Last name 和 First Name, 當按 Last Name 升序排列且 First Name 降序排列時(shí), 將看起來(lái)如下:
NVL(LastName,space(35)) + chrtran(NVL(FirstName,space(35)), cAllChars, cAllReverseChars)
在使用了比較序列的情況下(譯者注:即 SET COLLATE 設置為非 "MACHINE" 時(shí)), 問(wèn)題會(huì )更復雜. 字符的排列與字符碼的序列不匹配, 因此串 cAllChars 和 cAllReverseChars 將以不同方式組成. 要改正這一問(wèn)題, 創(chuàng )建一個(gè)只有一個(gè)字符字段的表并用所有的字符填充它. 按使用的比較序列索引該表, 用已排序的表讀取所有字符到這些串中 - 這將保證這些串中的所有字符以正確的順序排列. 當串字符不是單字節時(shí), 這些串變得太長(cháng)(用于索引表達式中), 速度也因 chrtranC() 而慢下來(lái). 好了, 按不同的排方向排序在使用視圖或查詢(xún)語(yǔ)句時(shí)可能用相當簡(jiǎn)單的方法實(shí)現 -  SELECT 語(yǔ)句的 ORDER BY 子句允許為各排序元素指定排序方向.
以下是一小點(diǎn)按多列排序的不同模式.
所有列交叉的共同排序, 如, First Name + Last Name - 排序一個(gè)列造成按組中的所有列排序
修正: 單擊列來(lái)排序它. 單擊第二個(gè)列添加排序到第一個(gè)排序中. 在此后單擊第一個(gè)列會(huì )只剩下第二個(gè)列的排序.
級聯(lián): 兩個(gè)或更多的列加入到排序中, 當一個(gè)列(主列)排序時(shí), 第二個(gè)列將與主列同時(shí)排序, 當主列未排序時(shí), 單擊第二列致使主列自動(dòng)排序. 在三列情況下第三列的單擊致使第二列和第一列(主列)自動(dòng)排序. 有時(shí)復雜的模式允許一個(gè)以上的二級列.
動(dòng)態(tài): 與修正相似, 但所有列加入到一個(gè)單一的排序處理中, 因此用戶(hù)可以組織任何排序列的組合.
這可以用象 "cMultipleSortingColumns" 這樣額外的列頭屬性來(lái)實(shí)現, 該屬性是一個(gè)用于組合排序的列索引列表的串, 級聯(lián)排序模式也要靠它來(lái)排序 (優(yōu)先排序號 - 在排序中哪一個(gè)是主列, 哪一個(gè)是次列等等). 對于動(dòng)態(tài)排序不需要額外的屬性, 只需要另一個(gè)排序管理.
多列排序也要求特殊的方法來(lái)顯示排序指示符. 在此情況下要求一次顯示多個(gè)控件來(lái)指出所有列的排序, 和當前排序的列. 對于動(dòng)態(tài)排序指示器的數量將與表格中可以排序的列數匹配.
在示例中沒(méi)有如此復雜的多列排序的東西, 但是這里討論的關(guān)于排序的大多數的東西.
VFP 的表格透析8
表格排序標記 (指示器)
有多種方式向用戶(hù)顯示表格是按哪一列排序的. 最簡(jiǎn)單的方法是改變列頭 caption 的背景色和前景色, 或者添加一個(gè)特殊的類(lèi)似于箭頭的字符到列頭的 caption 來(lái)指出排序的列 (通常是字符 "^" (升序) 和 "v" (降序). 這種做法看起來(lái)是可接受的但不是最好的. 可以用一個(gè)單獨的控件來(lái)指出排序狀態(tài), 但這并不是一件容易的事. 好了, 在表格列頭的 header 上放置類(lèi)似于箭頭的控件就象放置控件到表格單元格上一樣容易  - 所有尺寸也適用于這里 (參見(jiàn) VFP 中的表格第二部分). 但是, 在許多應用程序中你可以在列頭內看見(jiàn)一個(gè)小的箭頭. 在 VFP 是難于實(shí)現這樣的東西, 并且這要求一些特殊的方法. 這里有兩種方法.
第一種是用 "DEFINE WINDOW ... IN WINDOW ... NAME ..." 命令使用單獨的窗口定義并用 Windows API 函數 SetWindowRgn() 來(lái)改變窗口形狀來(lái)使它看起來(lái)象一個(gè)箭頭, 并在箭頭下面使用一組線(xiàn)段控件來(lái)顯示一些東西以模仿凹凸效果. 第二種方法是放置一個(gè)通常的透明容器控件到表格面上并用一組線(xiàn)段來(lái)生成箭頭. 也可以用線(xiàn)段控件, 簡(jiǎn)單的在表單方法中在表頭上上繪出箭頭, 但這要求更多的努力來(lái)刷新這樣的排序指示器.
在表格列頭上放置一些東西的問(wèn)題是非常困難的 - 表格列頭的自己重繪(redraw) 總是在其它 VFP 控件的上面, 即使你用了 ZOrder 方法來(lái)放置你的控件到所有其它控件的上面. 有報告說(shuō)在 W2K 操作系統上運行 VFP 的應用程序時(shí)不會(huì )出現該行為; 在 Windows NT 下它總是這樣. 一但特定的致使列頭刷新的事件發(fā)生時(shí), 表格列頭將在其它控件的上面自己繪制. 以下是這些事件和要求刷新排序指示器的事件的清單:
列頭的 click (在任何情況下) 和 right click (奇怪, 但的確在右擊時(shí)表格列頭移動(dòng)到其它任何控件).
表格的 refresh() 方法調用
改變當前單元 (AfterRowColChange 事件)
表格移動(dòng)或重調大小或表格列頭被移動(dòng)或調整大小后
表格卷動(dòng)
列頭的高度調整后 (在表格的 MouseUp 事件中捕捉它)
表格失去焦點(diǎn)時(shí) - 放置表格到容器中來(lái)捕捉它
好了, 所有這些東西, 正如你可以看到的一樣, 除最后一個(gè)外其它都可以用簡(jiǎn)單的方法捕捉. 在表格列頭刷新后表格失去焦點(diǎn)不會(huì )激發(fā)任何表格事件. 它可以把表格放入一個(gè)容器中來(lái)捕捉它, 在一些情況下這樣做并不好, 尤其對于一般的表格類(lèi).
如果不會(huì )造成模式窗口的沖突, 窗口化的指示器是比較好的. 在一些情況下, 在模式表單中的另一個(gè)用 "DEFINE WINDOW ... IN WINDOW" 定義的窗口會(huì )造成許多方面的影響. 特別困難的是這樣的窗口不激發(fā)任何事件不能正確地被鼠標單擊 (表格列頭的排序指示器需要鼠標單擊). 對于無(wú)模式表單這樣的窗口獲得焦點(diǎn), 也不是好事情, 因為會(huì )激發(fā)許多事件而且這需要特殊的處理.
在代碼中從多個(gè)地方刷新排序指示器, 使用了單獨的表格類(lèi)的方法. 刷新計算正確的指示器位置并放置它. 它包含了刷新指示器的方法 - 指明不同的排序方向, 并適當地設置箭頭的顏色為列頭的背景色. 顏色計算使用顏色亮度屬性百分比來(lái)實(shí)現顯示線(xiàn)段來(lái)模仿凸起效果. 它也保證列頭的背景色很淺或很深時(shí)箭頭也是可見(jiàn)的.
只在運行時(shí)使用指示器控件是個(gè)不錯的辦法. 在表格中, 它創(chuàng )建于 Init 事件并在表格的 Destroy 事件中清除. 對于多列排序你可能要使用一個(gè)數組, 用于保存為排序列創(chuàng )建的所有指示器控件的引用, 或在列頭中按排序引用為各列創(chuàng )建的指示器.
在雙擊時(shí)以數據寬度重調表格列寬
你可以在帶有表格或列表的應用程序中看到一個(gè)有用的功能 - 雙擊列頭的 resize 區自動(dòng)調整列寬為列中顯示的信息的寬度. 在示例中你可以看到在列頭的 DblClick 和 MouseUp 事件中我們如何在列頭的 resize 區從其它的事件中區分開(kāi)雙擊. 這會(huì )激發(fā) auto-resizing 算法. 它從 controlsource 中得到字段大小, 并用重復字段大小次數的字符 &acute;O&acute;(它擁有字符的平均寬度)和 TxtWidth() 函數計算列的象素寬度. 特殊字段如備注字段和帶有長(cháng)空格的字符字段. 另外, 列的 ControlSource 表達式不能給出要顯示的最大字符數. 不擴展列寬為最大大小是一個(gè)好的辦法, 只擴展為列中已有信息的寬度 - 要適應大多數行信息而不占用更多的列寬. 對于這種情況, 掃描當前記錄位置附近的記錄并計算 TxtWidth() 函數返回的表達式結果的最大值. 該值將成為列的寬度. 但是, 如果當前記錄附近的所有行的值都是空值, 最好指定一個(gè)默認的列寬, 如, 在列頭的 DefaultWidth 屬性中 (它也可用于一些其它場(chǎng)合).
表格訣竅
你可以用容器控件在表格單元格中顯示任何東西. 要刷新各行中的容器, 在 Dynamic* 屬性的表達式中你可以使用函數調用. 函數將被各表格行調用, 因此在該函數代碼中你可以修改容器中的任何東西并刷新它. 在調用這樣的函數時(shí)記錄源中的記錄指針是在正確的位置上.
要產(chǎn)生多行表頭, 在 VFP7 中使用列頭的 WordWrap 屬性. 在 VFP6 中多行表頭可以用復蓋在列頭上的標簽代替, 或類(lèi)似于表格列頭的容器控件 (可以在  Universal 的下載節中下載這樣的示例).
要為表格的不同部分顯示 tooltip, 在 timer 事件中用 MROW() 和 MCOL() 函數檢查鼠標在特定時(shí)間內是否沒(méi)有移動(dòng)并顯示你自己的控件. 表格的部分可以用表格的 GridHitTest 方法來(lái)檢查. (多行/奇特 ToolTip 控件以該方式處理, 你可以為該用途修改它; 可以在  Universal 的下載節中下載這樣的示例).
表格警告
不要用表格列控件的 "Value" 屬性進(jìn)行計算. 要從計算中得到數據, 直接訪(fǎng)問(wèn)表格的別名. 這是因為在非活動(dòng)列中的控件通常用于表格外觀(guān)的刷新, 并因此它的 Value 屬性對于當前行的值是不正確的.
列頭總是表單上的其它 VFP 控件上而不管它是被如何安排或放置的. 使用單獨的窗口控件也有一些缺點(diǎn).
用 RecCount() 和當前 RecNo() 計算縱向卷動(dòng)欄位置, 不會(huì )真正地顯示記錄數. 當記錄源是經(jīng)篩選的或包含大量有刪除標記的記錄時(shí), 卷動(dòng)欄常常會(huì )以不正確的位置讓用戶(hù)糊涂. 在此情況下建議使用查詢(xún)或視圖.
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
vfp 關(guān)于combo等控件在grid中的方法
VFP 的Grid表格透析
看實(shí)例學(xué)VFP:同時(shí)向兩個(gè)表中添加記錄
VFP基礎教程 表格
第七章 表單設計與應用
vfp的編程知識<一>
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久