国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

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

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

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

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


VFP 中的表格 第二部分
在該部分中, 我將討論關(guān)于如何使表格開(kāi)發(fā)更快速的列和列頭的竅門(mén). 也將討論關(guān)于確定表格列頭和單元格的確切位置. 象第一章中一樣, 我也包括了一些訣竅和警告.
表格列和列頭的竅門(mén) - 如何使表格開(kāi)發(fā)更快速
許多時(shí)候當(dāng)你試圖創(chuàng)建一個(gè)簡(jiǎn)單的應(yīng)用程序來(lái)檢查是否有些事可以用表格來(lái)做或它將看起來(lái)是什么樣的時(shí)候, 你會(huì)發(fā)現(xiàn)需要設(shè)置許多東西來(lái)使表格得到需要的外觀. 指定 RecordSource, 為各列指定 ControlSource, 指定各列頭的 caption 等等. 有時(shí)你又需要添加一些功能到表格中, 如象雙擊一個(gè)行以彈出別一個(gè)窗口 - 這也要求添加代碼到表格各列中的控件中. 當(dāng)只有一個(gè)只有少量列的表格在你的應(yīng)用程序中時(shí)這不成問(wèn)題. 但是, 當(dāng)你的應(yīng)用程序中有大量表格功表格有很多列時(shí), 你會(huì)很快被各表格和列中的這些事情弄得疲憊不堪.
為表格和表格中的控件定義類(lèi)是容易的. 該方法得到了廣泛的應(yīng)用. 但是, 還有更好的辦法來(lái)組織你的為表格使用的 OOP 的基類(lèi), 這將使得你的應(yīng)用程序的表格編程有一個(gè)好的基礎(chǔ).
首先, 定義一個(gè)提供所有功能和抽象方法的表格基類(lèi)模板. 注意你應(yīng)該保留你的類(lèi)的 ColumnCount 屬性為默認(rèn)值, 不要在表格基類(lèi)中定義任何列數(shù), 否在在類(lèi)對(duì)象被實(shí)例時(shí)將很難處理它們. 調(diào)整表格的外觀為你的應(yīng)用程序中最常用的情況. 例如, 在我的應(yīng)用程序中通常我不在表格中顯示記錄標(biāo)記 (Record Marks) (它們?cè)诒砀癫粨碛薪裹c(diǎn)且當(dāng)前記錄以另一種顏色高亮顯示時(shí)會(huì)不正確地刷新) 和刪除標(biāo)記 (表格通常是只讀的控件).
然后, 定義用于表格列中的控件. 通常它是一個(gè)文本框, 但你可能也想使用復(fù)選框, 組合框, 編輯框 (用于顯示備注字段內(nèi)容) 以及, 也許是一些其它不常用的自定義控件. 你將只在表格內(nèi)部使用這些控件而決不會(huì)在其它地方使用這些控件. 在這些類(lèi)中你可以更好地組織所有表格列中需要的功能. 以調(diào)用表格基類(lèi)中的抽象方法的方式來(lái)這樣做. 例如, 要調(diào)用雙擊方法, 在各表格列使用的類(lèi)的 DblClick 事件中放入以下代碼:
This.parent.parent.eventDblClick(this.parent)
在以上示例代碼中 eventDblClick 是表格基類(lèi)中的一個(gè)抽象方法 - 除接受單一參數(shù)的 ´lparameters´ 語(yǔ)句外它不包含其它代碼 (該參數(shù)是被雙擊的列的對(duì)象引用). 現(xiàn)在我們將從其中得到什么呢? 沒(méi)有這個(gè)我們不得不為表格用雙擊代碼在表單上定義新的方法, 然后從表格各列中的控件的 DblClick 事件中添加該方法調(diào)用. 當(dāng)你有許多的列時(shí), 這是相當(dāng)令人厭煩的. 采用這種新的方法你只需要在你的表格基類(lèi)的 eventDblClick 方法中寫(xiě)你的代碼. 好了, 這也要求放置你自己的類(lèi)到列中來(lái)代替默認(rèn)的文本框, 但這太容易了. 放入以下代碼到你的表格基類(lèi)的 Init 方法中:
LOCAL liColIndex
FOR liColIndex=1 TO this.ColumnCount && 遍歷所有的列
WITH this.Columns(liColIndex)
IF upper(.CurrentControl) == ´Text1´ && 只替換使用默認(rèn)文本框的列
.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)庫(kù)沒(méi)有載入內(nèi)存, 你將需要使用 NewObject 方法來(lái)代替 AddObject.
在進(jìn)行了上述簡(jiǎn)單的嘗試后, 讓我們來(lái)添加更多的東西. 定義更多的被表格列中的控件調(diào)用的事件. 添加更多的你可能用于表格中的類(lèi), 然后用添加與列的 control source 的數(shù)據(jù)類(lèi)型相適應(yīng)的類(lèi)的控件來(lái)增強(qiáng)替換默認(rèn)控件的代碼(你可以用 type 函數(shù)來(lái)檢查列的 control source 的數(shù)據(jù)類(lèi)型, 如: type(.ControlSource)). 通常復(fù)選框用于邏輯字段而編輯框用于備注字段.
現(xiàn)在我們的表格更易于在表單上使用了, 但是... 表格列頭怎么辦? 一個(gè)好辦法是為表格列頭寫(xiě)一些代碼來(lái)自動(dòng)地用列的 control source 來(lái)寫(xiě)入列頭的 caption 屬性. 例如, 在檢查了 ControlSource 是字段而不是表達(dá)式后用 DBGETPROP(.ControlSource, ´FIELD´,´Caption´) 來(lái)從數(shù)據(jù)庫(kù)中獲取字段的 caption. 表格的 Init 中的示例代碼如下:
LOCAL liColIndex, lcCaption
FOR liColIndex=1 TO this.ColumnCount && 遍歷所有的列
WITH this.Columns(liColIndex)
&& 檢查列頭是不沒(méi)有被開(kāi)發(fā)者設(shè)置
IF upper(.Header1.Caption) == ´HEADER1´
&& 檢查 control source 是否是真正的字段而不是表達(dá)式
IF ´.´ $ .ControlSource AND ;
FSIZE(substr(.ControlSource,at(´.´,.ControlSource)+1),.parent.RecordSource) > 0
&& 檢查別名是不是在 VFP 的數(shù)據(jù)庫(kù)中
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 獲取當(dāng)前數(shù)據(jù)庫(kù)設(shè)置要求當(dāng)前有一個(gè)打開(kāi)的數(shù)據(jù)庫(kù)也是必須的.
你可能做得更多. 例如, 在使用字段名作為列頭的 caption 時(shí), 對(duì)它們進(jìn)行一些復(fù)雜的轉(zhuǎn)換以使它們看起來(lái)更好一些, 對(duì)字段名使用命令約定. 作為命名約定的示例如下: 在數(shù)據(jù)庫(kù)中所有除主關(guān)鍵字段外的字段以數(shù)據(jù)類(lèi)型字符為前綴. 主關(guān)鍵字中用 ´ID´ 來(lái)標(biāo)識(shí)它. 因此我們可以從字段名中移去第一個(gè)字符作為列頭的 Caption(當(dāng)字段名中不包含 ´ID_´ 或 ´_ID´ 或它本身來(lái)是 ´ID´ 時(shí)). 然后用 STRTRAN 替換所有下劃線(xiàn)為空格并用 ´PROPER()´ 函數(shù)來(lái)獲得最終結(jié)果. 采用該方法 ´cCust_Name´ 將在列頭中顯示為 ´Cust Name´. 你可以為字段使用其它的命名約定, 看看是否可以改進(jìn)列頭的顯示. 如果你有 StoneField Database Toolkit (注:即眾所周知的 SDT) 或你自己的數(shù)據(jù)這典, 你可以查詢(xún)數(shù)據(jù)字典來(lái)獲得字段的 caption 和任何其它的你想應(yīng)用于列頭的和用于顯示數(shù)據(jù)的字段屬性. 如果字段來(lái)自視圖, 你可以從表中獲取它的源字段來(lái)獲取它的屬性. 你可以從視圖字段上用 "DBGETPROP(.ControlSource, ´Field´, ´UpdateName´)" 來(lái)從表中獲取VFP 的表格透析3
好了, 對(duì)于列頭的 captions - 你可以在表格基類(lèi)的 Init 中設(shè)置它們?nèi)缓笸羲鼈? 但是, 還有一些有用的捕捉列頭的 Click 和其它事件事情. 在易于創(chuàng)建表格列控件類(lèi)時(shí), 創(chuàng)建 header 類(lèi)是相當(dāng)難處理的. 你可以用相似的方法在你的表格基類(lèi)中為列頭組織事件系統(tǒng); 但是, 你可以在程序文件中用 DEFINE CLASS 結(jié)構(gòu)以簡(jiǎn)單的方法定義你自己的 header 類(lèi), 如下所示:
DEFINE CLASS MyHeaderClass AS HEADER
PROCEDURE DblClick
This.parent.parent.eventHeaderDblClick(this.Parent)
ENDPROC
ENDDEFINE
如果你熟悉 VCX 文件結(jié)構(gòu), 你可以在 VCX 庫(kù)中添加兩條記錄并指定其基類(lèi)為 header 來(lái)創(chuàng)建 header 類(lèi). 但是這樣一來(lái), 你除了象編輯 VFP 表一樣編輯它以外, 將不能再以其它方式編輯它. 因此最好還是首先在 PRG 文件中定義它用于開(kāi)發(fā)和調(diào)試. 最后, 當(dāng)它測(cè)試完成后, 如果需要的話(huà)你可以從 PRG 中移運(yùn)它們到 VCX 中. 為什么你會(huì)要它們?cè)?nbsp;VCX 文件中的理由是簡(jiǎn)單的 - 你可以在設(shè)計(jì)時(shí)從控件工具欄拖放它到表格列中, 這樣將在設(shè)計(jì)時(shí)以新的 header 類(lèi)替換默認(rèn)的 header. 我不知道還有其它的理由了... 對(duì)于運(yùn)行時(shí), 以下代碼需要添加到前述的遍歷代碼中 (改變默認(rèn)控件為你自己的控件). 也為列頭類(lèi)添加它們:
IF upper(.Header1.Class) == ´HEADER´    && 僅替換默認(rèn)的 header 類(lèi)
lcOldCaption = .Header1.Caption       && 保存 caption 以應(yīng)用它們到新的 header
.AddObject(´Header2´,´MyHeaderClass´) && 這將自動(dòng)移除原有的 header
.Header2.Name = ´Header1´
.Header1.Caption = lcOldCaption
ENDIF
你可能也想復(fù)制原表格列頭的其它屬性到你用新類(lèi)創(chuàng)建的新的列頭對(duì)象. 也請(qǐng)注意到不需要移除原有的列頭因?yàn)?nbsp;VFP 會(huì)自動(dòng)移除它 - 來(lái)保持列中只有一個(gè)列頭. 對(duì)于 AddObject 定義列頭類(lèi)的 PRG 文件必須事先用 SET PROCEDURE TO ?ADDITIVE 命令載入內(nèi)存, 但是你可以決定用列的 NewObject 方法.
在一般情況下, 建議把替換表格中的控件為你自己的類(lèi)的代碼移動(dòng)到一個(gè)單一的表格的方法中. 為什么需要這樣呢 - 表格重構(gòu). 少數(shù)場(chǎng)合下表格重構(gòu)是必需的. 在表格重構(gòu)后你可以調(diào)用表格的方法來(lái)再次設(shè)置你自己的類(lèi), 因此即使當(dāng)你在相同的表格控件中顯示不同的數(shù)據(jù)時(shí), 它仍然會(huì)以相同的方式運(yùn)行. 這是因?yàn)? 盡管進(jìn)行了重構(gòu), 事件的主要代碼是在表格控件的方法中, 因此在重構(gòu)后它們不會(huì)丟失. 更多的是, 在你運(yùn)行了替換表格控件為你自己的類(lèi)的代碼后, 表格仍然具備所需的功能. 因此代替用不同的數(shù)據(jù)源創(chuàng)建多個(gè)表格類(lèi)在同一個(gè)表單中來(lái)顯示這些不同數(shù)據(jù)源, 你現(xiàn)在可以放置一個(gè)單一的表格并用不會(huì)丟失太多功能的方法來(lái)重構(gòu)它; 然后為每一個(gè) data source 來(lái)創(chuàng)建新的表格類(lèi)并增強(qiáng)不同數(shù)量的 record sources.
不好的事情是列頭的 Click 事件也會(huì)在表格列 resize 或移動(dòng)時(shí)發(fā)生. 列具有 Moved 和 Resized 事件, 但是... 當(dāng)你用自己的列頭類(lèi)替換了默認(rèn)的列頭類(lèi)后, 很難使用列的這些事件, 而且它們的運(yùn)行速度會(huì)變慢. 因此看來(lái)使用列頭類(lèi)并不好. 但是有好的解決辦法. 在你的新的列頭類(lèi)中比較 OldColumnWidth 和 OldColumnOrder 屬性. 然后在列頭的 Init 中設(shè)置它們?yōu)榱械?nbsp;Width 和 ColumnOrder 屬性的初始值. 在 Click 事件代碼中比較你的列頭類(lèi)的當(dāng)前的 Width 和 OldColumnWidth 屬性值及 ColumnOrder 和 OldCoumnOrder 屬性值. 當(dāng)值與原值不同時(shí), 則列是 resized 或 moved 了的. 在 resized 時(shí), 只用當(dāng)前列的 OldColumnWidth 來(lái)更新它. 當(dāng)列 moved 時(shí) - 你需要為列頭更新表格中的所有列的 OldColumnOrder 屬性. 好了, 在一定情況下列的寬度和列序現(xiàn)在可以用編程方法來(lái)修改了, 最好是不要直接這樣做, 而是使用表格類(lèi)中的一個(gè)自定義方法調(diào)用, 例如, ´ChangeColumnWidth(ColumnNumber)´, 或 ´ChangeColumnOrder(ColumnNumber)´. 采用此方法你可以保證 OldColumnWidth 和 OldColumnOrder 總是與列的設(shè)置同步. 而且現(xiàn)在你有機(jī)會(huì)用上述方法在列移動(dòng)或 resize 時(shí)來(lái)激發(fā)你的表格類(lèi)的自定義方法中的代碼了 - 沒(méi)有真正接觸到任何列的方法!
為了加快輸入到列的 ControlSource 中, 你可以用簡(jiǎn)單的方法. 在你的表格類(lèi)中定義一個(gè)屬性 (ControlSourceList) 它是用于保存各列使用的字段名的以分號(hào)分隔的列表. 使用分號(hào)而不是逗號(hào)是因?yàn)槟憧赡軙?huì)在一些 control source 中使用包含逗號(hào)的表達(dá)式. 在 Init 事件中分解該串到各屬性中(如果它不為空), 然后用串中的列表中的項(xiàng)填充各列的 ContolSource 屬性. 注意表格的任何刷新將不會(huì)先于該操作, 因?yàn)楸砀竦某跏蓟谀J(rèn)情況下使用字段在表中的物理順序 (列的重綁定 - 參見(jiàn)文章前面的描述). 當(dāng)你的表格中有自定義控件時(shí), 可能列會(huì)使用對(duì)于該控件來(lái)說(shuō)是不正確的字段類(lèi)型并在刷新時(shí)造成不正確的數(shù)據(jù)類(lèi)型錯(cuò)誤 (Refresh 極少在表格的 Init 事件中發(fā)生, 但是, 在復(fù)雜的類(lèi)代碼中有時(shí)可能會(huì)發(fā)生). 好了, 在設(shè)計(jì)時(shí)屬性表中的屬性長(zhǎng)度限制是 255 字符, 因此在表格有許多列時(shí)將不能這樣做. 在此情況下當(dāng)你在表單中使用表格時(shí)可以在表格的 Init 中用代碼來(lái)指定長(zhǎng)的串到屬性, 然后用 DODEFAULT() 來(lái)調(diào)用父類(lèi)代碼. 你也可以在串中不用字段名前的別名來(lái)節(jié)約空間; 別名將被表格自動(dòng)指定. 任何情況下, 在 ControlSource 中的表達(dá)式中的字段名必須包括別名.
最后, 說(shuō)說(shuō)不同類(lèi)型的 record source. 當(dāng) record source 是別名時(shí), 沒(méi)有問(wèn)題. 但是當(dāng)你打開(kāi)一個(gè)以表名以數(shù)值開(kāi)始的表 record source 時(shí), VFP 會(huì)以另一個(gè)別名來(lái)打開(kāi)它. 表格中的 RecordSource 中的 SQL SELECT 語(yǔ)句也有類(lèi)似情況. 幸運(yùn)的是, 有一種簡(jiǎn)單的方法來(lái)檢查表格使用的別名的正確性. 別名可以用任何列的 ControlSource 屬性來(lái)檢查. 表格會(huì)自動(dòng)添加 "{alias}." 到所有列中的 control sources 字段前. 你可以從列的 control sources 取出別名(當(dāng)它們是簡(jiǎn)單格式而不是復(fù)雜表達(dá)式時(shí)) . (如何檢查 control source 是否是一個(gè)字段引用我已經(jīng)說(shuō)過(guò)了.) 好了, 當(dāng)要用表格顯示一些用 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 的改變并更新我們的自定義屬性.
字段名. 這僅適用于可更新字段, 作為表達(dá)式生成的字段用上述方法返回的結(jié)果將是一個(gè)空串.
VFP 的表格透析4
確切定位表格列頭和單元格
表格中的控件的功能可能與表格外的控件的功能不同. 事實(shí)上關(guān)于這一點(diǎn) VFP 有一些冗余, 致使一些功能在表格中不能正確運(yùn)行. 我不打算在這里一一列舉, 但將說(shuō)明一種好的方案 - 把控件放在表格外并在表格單元格上顯示它來(lái)進(jìn)行數(shù)據(jù)編輯. 采用該方法控件將象單獨(dú)的控件一樣編輯數(shù)據(jù), 而不會(huì)象表格列中的控件一樣有一些功能不能使用. 現(xiàn)在對(duì)每一列采用此方法而你將不再受到表格的限制. 但是, 問(wèn)題是復(fù)蓋在表格單元格上的控件的相對(duì)位置. 這就要求計(jì)算表格中的單元格的準(zhǔn)確位置以便用你的控件來(lái)復(fù)蓋它. 這個(gè)事情不太好做, 列序可能改變了, 表格可能卷動(dòng)了, 僅獲得焦點(diǎn)的表格可以計(jì)算行號(hào)等等. 在這里我說(shuō)明一種如何計(jì)算位置的一般方法.
另一個(gè)要求計(jì)算準(zhǔn)確位置的理由是放置排序標(biāo)記到表格列上來(lái)指明表格是按哪一個(gè)列進(jìn)行排序的. 對(duì)于這一點(diǎn)只要求計(jì)算列的位置.
位置的計(jì)算將在表格卷動(dòng)后, 一些列寬改變后, 一個(gè)列移動(dòng)后, 記錄標(biāo)記和刪除標(biāo)記的可視狀態(tài)改變后, 列高和行高改變后, 表格被分割后 (表格的 Partition 屬性設(shè)置為非零值). 你可以捕捉這些事件:
* 表格卷動(dòng)在 Scrolled 事件中, 如果是用鍵盤(pán)產(chǎn)生的自動(dòng)卷動(dòng)在 AfterRowColChange 事件中. 因列移動(dòng)造成的卷動(dòng) - 捕捉列移動(dòng)或改變用 LeftColumn 屬性 (保存它們的原值). 其它原因造成的自動(dòng)卷動(dòng)則相當(dāng)少見(jiàn).
* 列的移動(dòng)和寬度調(diào)整在列的事件中或用本文中前面描述過(guò)的方法
* 記錄標(biāo)記和刪除標(biāo)記的改變, 以及卷動(dòng)欄的以編程方式的改變; 你可以在表格類(lèi)中定義相應(yīng)屬性的 Assign 方法來(lái)捕捉它們
* Partition, RowHeight 及 HeaderHeight 改變 - 在表格的 MouseUp 事件中用類(lèi)似于前述的列頭方法來(lái)捕捉它們 - 保存原值到表格屬性中并在 MouseUp 事件中用新值比較它們以確定是否發(fā)生了變化.
因?yàn)橐谠S多地方計(jì)算來(lái)更新控件的位置, 在計(jì)算列的位置時(shí)應(yīng)該盡可能地快; 否則在表格中的列數(shù)及計(jì)算位置的控件很多時(shí)會(huì)比較慢.
以下代碼中的算法檢查列的大小和右邊沿. 它是最快的并經(jīng)長(zhǎng)期使用的方法. 它計(jì)算表格除 split 外的所有表格設(shè)置. 代碼中的注釋說(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
當(dāng)前表格行的位置只有在表格獲得焦點(diǎn)時(shí)可以檢查到. 這是因?yàn)槲覀冎荒苡帽砀竦?nbsp;RelativeRow 屬性來(lái)計(jì)算它, 當(dāng)表格不擁有焦點(diǎn)時(shí)計(jì)算值總是零. 這種方法更簡(jiǎn)單且更適于放入單個(gè)的表達(dá)式:
* 計(jì)算表格行相對(duì)于表格矩形區(qū)域的 top 位置, 單位是 pixels
* 如果位置在可視的區(qū)域外則返回零
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 屬性應(yīng)該訪(fǎng)問(wèn)兩次.
下圖顯示了表格屬性及它們的尺寸. 有助于更好地理解示例代碼.
VFP 的表格透析5
表格訣竅
表格的 Format 屬性設(shè)置為 ´Z´ 時(shí), 當(dāng) Sparse=.T. 時(shí)不顯示零值
是不是對(duì)表格列中顯示的備注字段為 ´Memo´ 感到很厭倦? 你可以用類(lèi)似于 "PADR({MemoField},200)" 這樣的表達(dá)式來(lái)顯示備注字段. 也可以在列的 sparse 屬性設(shè)置為 .F. 時(shí)在列中用 EditBox 來(lái)允許對(duì)它們進(jìn)行編輯, EditBox 可以比用表達(dá)式作為列的 ControlSource 時(shí)顯示更多的文本. 在表格中的 EditBox 中卷動(dòng)欄僅在表格的 RowHeight 屬性大于三行文本的高度時(shí)才會(huì)顯示.
在表格可以完整地顯示所有列而沒(méi)有橫向卷動(dòng)欄時(shí), 你可以設(shè)置所有列的寬度小一些來(lái)讓所有的列完全顯示在顯示區(qū)中以避免當(dāng)最后一列得到焦點(diǎn)時(shí)表格的自動(dòng)卷動(dòng).
表格警告
在使用行緩存時(shí)刷新表格會(huì)造成數(shù)據(jù)的自動(dòng)提交(Update). 這是因?yàn)樵?nbsp;VFP 內(nèi)部, 表格會(huì) scan 別名中的要顯示的記錄, 這會(huì)造成記錄指針的移動(dòng), 并因此造成數(shù)據(jù)的自動(dòng)提交. 在用表格工作時(shí), 別名應(yīng)該使用表緩存模式.
在特定情況下, 當(dāng)列的 ControlSource 返回的字符長(zhǎng)于 200 字符且列的 sparse 屬性設(shè)置為 .T. 時(shí)表格會(huì)崩潰或不正常運(yùn)行. 在此情況下用類(lèi)似于 "PADR(...,200)" 這樣的表達(dá)工作為表達(dá)式來(lái)顯示數(shù)據(jù). 如果要在這樣的情況下編輯數(shù)據(jù), 在表格列中用 EditBox 來(lái)代替 TextBox 并設(shè)置列的 Sparse 屬性為 .F.. 當(dāng) SET DELETED 為 ON 時(shí), 被刪除的記錄只有在記錄指針移動(dòng)后才會(huì)不顯示出來(lái). 在刪除后如果記錄指針在被刪除的記錄上時(shí), VFP 保持記錄的可見(jiàn), 包括在表格中.


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

聯(lián)系客服