VFP 5.0的新的ActiveX控件TreeView是一個(gè)有力的、可視的、吸引人的工具, 你可以將它使用于許多應用程序。 但是,其使用的復雜和技術(shù)文檔的簡(jiǎn)單使很多人望而卻步。本文探索了一些使用TreeView的有用的技術(shù)。
VISUAL FoxPro 5.0 包含了很多新的ActiveX (原來(lái)的OLE) 控件,這些控件可以為你的應用程序增加很多新功能。 這些控件包括TreeView、ListView、StatusBar、和CommonDialog 控件, 允許你為你的應用程序建立Windows 95 風(fēng)格的界面。 ActiveX 控件很容易找到: 在表單控件工具欄中的查看類(lèi)菜單中選擇ActiveX 控件, 會(huì )出現30 個(gè)新的控件。 簡(jiǎn)單的從工具條拖放一個(gè)控件到一個(gè)表單中,就象使用VFP自身的控件一樣, 給它一個(gè)名字, 設置一些屬性, 等等… 在附加到VFP 的屬性表單, 每一個(gè)控件都有一個(gè)定制的屬性表。 要訪(fǎng)問(wèn)這個(gè)屬性表, 在控件上右擊鼠標并從出現的快捷菜單中選擇適當的項。
TreeView 控件
我的意見(jiàn)是, 最重要的新控件是TreeView。 你已經(jīng)使用過(guò)TreeView 控件-它被用于Windows Explorer的左邊窗口中, 以及VFP的項目管理和類(lèi)瀏覽器。 該控件在視覺(jué)上比 VFP 3.0 中的 Outline 控件更吸引人, 且更容易處理。 例如, 你要使節點(diǎn)顯示你必須順序添加節點(diǎn)到 Outline 控件。 在 TreeView 控件中你可以以任意順序添加節點(diǎn),因為在添加新節點(diǎn)時(shí)你可以指定哪一節點(diǎn)是父節點(diǎn)。
你可以用 TreeView 控件來(lái)做許多事。 以下是一些例子:
1.在庫存控件系統中顯示一些原始單據清單。
2.用于向下延伸顯示。 例如, 你可以顯示一個(gè)客戶(hù)清單并為該客戶(hù)向下延伸到訂單的訂貨和產(chǎn)品。 另一個(gè)示例是區域表及所屬銷(xiāo)售人員及各銷(xiāo)售人員的客戶(hù), 等等。
3.改善組織圖表: 分隔主管人員擴展的分支,及其員工。
4.創(chuàng )建任何其它層次顯示信息。
TreeView 控件位于 COMCTL32.OCX (在 \WINDOWS\SYSTEM 中)。 如果你安裝了 Visual Basic Control Creation Edition , 會(huì )安裝一個(gè)更新版本的 COMCTL32.OCX ,這個(gè)版本有許多問(wèn)題。 如果你的 COMCTL32.OCX 大于 325K , 你應該從你的 VFP 5.0 CD-ROM 上重新安裝一個(gè)正確的版本(位于光盤(pán)上的 \OS\SYSTEM 目錄中)。很多用戶(hù)訴說(shuō)在他們的系統中 TreeView 不能正確工作,這也可能是原因之一。
TreeView 控件的幫助文檔在 CTRLREF.HLP 中(在 VFP 主目錄中)。 這個(gè) TreeView 控件的幫助文件的內容可能是所有 ActiveX 控件中最可憐的幫助文件了(可以說(shuō)是高深莫測): 它的組織混亂, 內容在多處有誤, 且經(jīng)常是很模糊, 尤其在文檔中的如何訪(fǎng)問(wèn)一些方法和屬性的地方更是如此。
TreeView 控件允許你處理三個(gè)不同類(lèi)型的對象:TreeView 控件自身, 節點(diǎn)集合和節點(diǎn)對象。 節點(diǎn)集合與表單中的控件集合一樣; 它允許你用不同的索引號來(lái)訪(fǎng)問(wèn)個(gè)別節點(diǎn)對象。 但是, 也可以不通過(guò)節點(diǎn)集合來(lái)訪(fǎng)問(wèn)不同的節點(diǎn)對象。 例如, TreeView 的SelectedItem 屬性是一個(gè)選定節點(diǎn)的對象引用, 因此象 NodeClick 和 Expanded 這樣的方法接受一個(gè)節點(diǎn)的對象引用作為參數。
我將分別探討這三個(gè)對象類(lèi)型的屬性, 事件和方法。在示例表單TREEVIEW.SCX 中展示了我在本文中描述的許多技術(shù)。
TreeView 控件的方法和事件
TreeView 控件響應一些我們覺(jué)的VFP 控件的方法和事件: Click, DblClick, Drag, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, Move, Refresh, SetFocus, ShowWhatsThis, 和 ZOrder。 有一個(gè)很大的例外: 沒(méi)有 RightClick 事件。 在稍后,你會(huì )看到如何處理這種情況。
除這些方法和事件外,TreeView 控件有一些它自己的方法和事件(幫助文件中列出的 Clear 和 Remove 方法, 實(shí)際上是屬性節點(diǎn)集合而不是 TreeView 控件自己的):
BeforeLabelEdit 和 AfterLabelEdit 發(fā)生在標簽被用戶(hù)修改前和修改后(就象在 Windows 的資源管理器中, 你可以單擊選定的節點(diǎn)并修改它的文本(在稍后你可以看到如何廢止該自動(dòng)編輯功能)。 該事件中的代碼通常用于在某處保存新的文本, 如一個(gè)表中的字段。
Collapse 和 Expand 當用戶(hù)收縮或擴展一個(gè)節點(diǎn)時(shí)激發(fā)。 它們接受的參數是選定節點(diǎn)的對象。 收縮或擴展一個(gè)節點(diǎn)不會(huì )使該節點(diǎn)成為活動(dòng)的節點(diǎn), 這一點(diǎn)通常會(huì )使用戶(hù)糊涂。 添加以下代碼到 Collapse 和 Expand 事件來(lái)確保存該節點(diǎn)成為活動(dòng)的節點(diǎn)(調用了我將在稍解釋的 NodeClick(), 它們只在你在這些方法中有一些自定義代碼是才是必須的):
lparameters node
Node.Selected = .T.
This.NodeClick(Node)
GetVisibleCount 顯示控件中全部可見(jiàn)的節點(diǎn)數。 如果控件可以顯示最后的節點(diǎn),該數可能看起來(lái)會(huì )很小。
HitTest :如果傳遞的 X 和 Y 座標上存在節點(diǎn),它返回一個(gè)節點(diǎn)對象引用,否則返回 .NULL. 這里有一個(gè)新的問(wèn)題: HitTest 期待 X 和 Y 座標值是緹(twips) (用于 Visual Basic), 但 VFP 使用象素。 以下代碼將轉換象素值到緹。TREEVIEW 示例表單的 INIT 中有這些代碼, 并有兩個(gè)自定義屬性來(lái)保存計算值: nTreeFactorX 和 nTreeFactorY。
local liHWnd, ;
liHDC, ;
liPixelsPerInchX, ;
liPixelsPerInchY
* 定義一些常數。
#define cnLOG_PIXELS_X 88
* 來(lái)自 WINGDI.H
#define cnLOG_PIXELS_Y 90
* 來(lái)自From WINGDI.H
#define cnTWIPS_PER_INCH 1440
* 每英寸1440 緹
* 定義一些 Windows API 函數
declare integer GetActiveWindow in WIN32API
declare integer GetDC in WIN32API ;
integer iHDC
declare integer GetDeviceCaps in WIN32API ;
integer iHDC, integer iIndex
* 為 VFP 取得圖形設備(device context)。
liHWnd = GetActiveWindow()
liHDC = GetDC(liHWnd)
* 取得每英寸象素值。
liPixelsPerInchX = GetDeviceCaps(liHDC, cnLOG_PIXELS_X)
liPixelsPerInchY = GetDeviceCaps(liHDC, cnLOG_PIXELS_Y)
* 取得每象素緹并保存到表單屬性中。
with This
.nTreeFactorX = cnTWIPS_PER_INCH/liPixelsPerInchX
.nTreeFactorY = cnTWIPS_PER_INCH/liPixelsPerInchY
endwith
HitTest() 有助于解決一個(gè)處理鼠標右擊事件。 你可能想在用戶(hù)在節點(diǎn)上右擊時(shí)顯示彈式菜單, 但在節點(diǎn)上右擊時(shí)不會(huì )使它成為選定節點(diǎn)。 在控件的 MouseDown 事件中用以下代碼來(lái)處理這一點(diǎn)(因為沒(méi)有 RightClick 事件,所以這是你能處理右擊的唯一辦法)。 同時(shí), 調用 NodeClick() 僅當你在該方法中放有代碼時(shí)才是必須的:
lparameters Button, Shift, X, Y
local loNode
if Button = 2
* 如果這是右鼠標鍵, 取得鼠標下的節點(diǎn)的引用。
loNode = This.HitTest(X * Thisform.nTreeFactorX, ;
Y * Thisform.nTreeFactorX)
* 如果有可用的節點(diǎn), 選擇它。
if not isnull(loNode)
loNode.Selected = .T.
This.NodeClick(loNode)
endif
* 現在顯示右擊快捷菜單
else
* 處理必要的鼠標左擊
endif
NodeClick: 當用戶(hù)在節點(diǎn)上單擊時(shí)激活 (在 Click 事件之前)。 NodeClick 接受一個(gè)選定節點(diǎn)的對象引用作為參數。 該方法被典型地用于在一個(gè)節點(diǎn)被選定時(shí)更新一些東西(例如其它控件的值)。 如果 NodeClick 中的代碼執行時(shí)花的時(shí)間太長(cháng), 選定項會(huì )被高亮顯示但以前的項會(huì )被斷續線(xiàn)包圍。 移動(dòng)鼠標(即使沒(méi)有單擊鼠標按鈕) 會(huì )恢復以前項的高亮顯示。 要避免這一問(wèn)題, 可以在 NodeClick 事件中放入以下代碼來(lái)保證節點(diǎn)被單擊而成為選定項:
Node.Selected = .T.
StartLabelEdit: 通常被用于編輯節點(diǎn)的標簽。 它用于 LabelEdit 屬性被設置為1-手動(dòng)時(shí)。
TreeView 控件屬性
如同事件一樣, TreeView 控件支持一些其它控件所擁有的屬性, 包括 DragIcon, DragMode, Enabled 和 Visible。 許多 TreeView 特定屬性可以在設計時(shí)在 TreeView 控件上單擊鼠標右鍵調出的 TreeView 控件屬性表中設置。 包括 Style, LineStyle, Indentation, PathSeparator 和 HideSelection。
在屬性表中可編輯的屬性中, 你最需要改變其默認值的是:
Style: 指明 TreeView 控件顯示什么。 可以選擇是否顯示圖像,線(xiàn)條, 加號/減號等。
LineStyle: 指明顯示根線(xiàn)。 如果沒(méi)有設置該屬性為 1-根線(xiàn), 無(wú)論 Style 是如何設置的,頂級對象上都不會(huì )有加號/減號標記。
LabelEdit: 如果你不想讓用戶(hù)修改各節點(diǎn)上的文本, 設置該屬性為 1-手動(dòng)。
ImageList: 一個(gè) ImageList 控件對象引用,它包括了控件中的節點(diǎn)使用的圖象; 參見(jiàn)本文 ImageList 一章中關(guān)于該簡(jiǎn)單控件的說(shuō)明。 不幸的是,該屬性不能可視化地進(jìn)行設置; 你必須以編程方式用代碼進(jìn)行設置,象下面一樣在表單的 Init 方法中:
This.oTree.ImageList = This.oImageList.Object
注意你需要在表單的 Init 事件中而不是 TreeView 的 Init 事件中進(jìn)行上述設置, 因為 TreeView 可能會(huì )在 ImageList 之前實(shí)例化, 如果這樣,試著(zhù)設置 ImageList 屬性為一個(gè)尚不存在的對象將會(huì )失敗。 同時(shí), 注意上述代碼中的”object” 關(guān)鍵字; 這是必須的。
Indentation: 子節點(diǎn)縮進(jìn)多少。
HideSelection: 如果沒(méi)有關(guān)閉這一選項, 當 TreeView 控件失去焦點(diǎn)時(shí),選定節點(diǎn)不會(huì )保持高亮度。 這很容易把用戶(hù)糊涂。
Font: 用于節點(diǎn)文本的字體名字, 字號和字型。該屬性可從屬性表中修改(注意幫助文件中說(shuō)明了 ScrollBars 屬性, 但沒(méi)有該屬性):
DropHighlight: 該屬性用于支持TreeView 控件的 DragOver 事件, 因此可以在鼠標經(jīng)過(guò)一個(gè)節點(diǎn)時(shí),高亮顯示該節點(diǎn)。 但是, 這只會(huì )在 VFP 6 中的版本 6 的 TreeView 控件中才實(shí)現了; 在老版本的控件中設置該屬性為節點(diǎn)對象時(shí),會(huì )引發(fā)一條”類(lèi)型失配” 錯誤。 在這種情況下, 要達到同樣目的,可用 HitTest() 來(lái)檢查鼠標經(jīng)過(guò)的是哪一節點(diǎn)并設置該節點(diǎn)的 Selected 屬性為 .T. TREEVIEW 示例表單在 DragOver 事件中使用了該技術(shù)。
Nodes: 控件中的節點(diǎn)集合引用。
SelectedItem: 當前選定節點(diǎn)對象的引用。
節點(diǎn)集合方法
以下是TreeView 控件的節點(diǎn)集合的方法(被指定的 <Object>.Nodes 引用, 其中 <Object> 是 TreeView 控件名):
Add: 添加一個(gè)新節點(diǎn)并返回它的引用。 它使用以下語(yǔ)法:
<Object>.Nodes.Add(Relative, Relationship, Key, Text, Image, ;
SelectedImage)
其中:
Relative: 一個(gè)已存在節點(diǎn)的索引或鍵值。 如果未指定, 新節點(diǎn)放在頂級節點(diǎn)的末端。
Relationship: 新節點(diǎn)放置的相對于第一個(gè)參數中指定的節點(diǎn)的位置,可以是以下值:
1. 節點(diǎn)放在相對節點(diǎn)所在級的所有節點(diǎn)的末端。
2. 節點(diǎn)放在相對節點(diǎn)的后面。
3. 節點(diǎn)放在相對節點(diǎn)的前面。
4. 節點(diǎn)成為相對節點(diǎn)的子節點(diǎn)。
Key: 用于指明節點(diǎn)的唯一串。 如果顯示內容是從一個(gè)表的記錄中載入的, 可使用記錄的主關(guān)鍵字段值 (如果不是字符型的,還需要轉換為字符型)。 否則, 可使用一個(gè)轉換為串的順序號。
Text: 控件節點(diǎn)中顯示的文本。
Image: 相關(guān) ImageList 控件中的圖象索引。
SelectedImage: 當節點(diǎn)選定時(shí),要顯示的相關(guān) ImageList 控件中的圖象索引。
Clear: 清除全部節點(diǎn)。
Remove: 移去指定索引號的節點(diǎn)。
節點(diǎn)集合屬性
以下是節點(diǎn)集合屬性:
Count: 節點(diǎn)號。
[<Index>]: 一個(gè)節點(diǎn)號的對象引用。
節點(diǎn)對象方法
以下是節點(diǎn)對象的方法:
CreateDragImage: 該方法不能用于 VFP ,因為該方法返回一個(gè)圖象時(shí), DragIcon 需要一個(gè) CUR 文件名。
EnsureVisible: 確保指定節點(diǎn)是可見(jiàn)的。 該方法在必要時(shí)卷動(dòng) TreeView 控件并擴展所有指定節點(diǎn)的父節點(diǎn)。
節點(diǎn)對象屬性
以下是節點(diǎn)對象屬性:
Children: 如果節點(diǎn)對象擁有子節點(diǎn),該值為.T.
Expanded: 如果節點(diǎn)對象已經(jīng)擴展,該值為.T.
FullPath: 該節點(diǎn)的所有父節點(diǎn)(祖節點(diǎn),曾祖節點(diǎn)等)的文本串接, 各節點(diǎn)的文本間用控件的 PathSeparator 屬性中指定的分隔符分隔。 它非常類(lèi)似于帶路么的文件名。
Image, ExpandedImage 和 SelectedImage: 相關(guān)ImageList 控件中的適當的圖象號。
Index: 節點(diǎn)集合中的節點(diǎn)對象的索引。
Key: 當節點(diǎn)添加時(shí)指定的唯一鍵值。
Child, FirstSibling, LastSibling, Previous, Parent, Next 和 Root: 指向與指定節點(diǎn)相關(guān)的節點(diǎn)對象。
Selected: 如果節點(diǎn)對象是選定的,該值為.T.。 設置該屬性為 .T. 會(huì )自動(dòng)高亮顯示該節點(diǎn)并設置先前選定的節點(diǎn)的 Selected 屬性為 .F.
Text: 控件中顯示的文本。
載入TreeView 控件
在載入顯示內容的初期相對的比較簡(jiǎn)單, 可以使用一些的趣的手法。 首先, 讓我們看看直截了當的示例。 假如我們想載入一個(gè)客戶(hù)及其訂單(TREEVIEW 示例表單就是這樣做的)。 以下是代碼; 這些代碼可以在表單初始化時(shí)調用(如, 在表單的Init() 方法或 TreeView 的 Init() 方法中調用表單的一個(gè)自定義方法):
with This
* 設置 CUSTOMER 和 ORDERS 表。
select CUSTOMER
set order to CUST_ID in ORDERS
set order to COMPANY
scan
* 從客戶(hù)表中添加節點(diǎn)。
lcCustomerKey = 'C' + CUST_ID
.oTree.Nodes.Add(, 1, lcCustomerKey, trim(COMPANY), 1)
* 找到該客戶(hù)的第一個(gè)訂單并逐個(gè)處理。
select ORDERS
seek CUSTOMER.CUST_ID
scan while CUST_ID = CUSTOMER.CUST_ID
.oTree.Nodes.Add(lcCustomerKey, 4, 'O' + ORDER_ID, ;
dtoc(ORDER_DATE) + ' ' + ;
transform(ORDER_AMT, '$99,999.99'), 2)
endscan
select CUSTOMER
endscan
endwith
注意:指定到各節點(diǎn)的鍵值是字段名加上一個(gè)前綴來(lái)指明該記錄來(lái)自哪一個(gè)表 (如”C” 表示 CUSTOMER , “O” 表示 ORDER)。 前綴可避免可能客戶(hù)和訂單表中具有相同主關(guān)鍵字值的問(wèn)題, 并且前綴加主關(guān)鍵字段值允許我們快速地找到與特定節點(diǎn)匹配的記錄(使用節點(diǎn)的 Key 屬性, 從第一個(gè)字符取得表名,并用基本關(guān)鍵字來(lái)搜索余下部分的串)。
只展示這些代碼是因為必須在表單顯示前處理每一個(gè)客戶(hù)的每一個(gè)訂單。 如果有許多客戶(hù)或訂單, 這會(huì )花很長(cháng)時(shí)間。 一個(gè)較好的方法是只載入頂級的項 (本例中是客戶(hù)), 然后在 Expand() 方法中, 檢查需要擴展的節點(diǎn)是否已載入了子節點(diǎn)。 如果沒(méi)有, 則只載入當前節點(diǎn)的子節點(diǎn)。 這就比在一開(kāi)始時(shí)載入全部?jì)热菘於嗔恕?這里有一個(gè)技巧: 如果一個(gè)節點(diǎn)沒(méi)有載入任何子節點(diǎn), 就不會(huì )有 + 符號出現在該節點(diǎn)上。 因此你需要為每一個(gè)項級節點(diǎn)至少添加一個(gè)子節點(diǎn), 那怕是一個(gè)”占位”節點(diǎn)。 當該節點(diǎn)第一次擴展時(shí), 移去占位節點(diǎn), 然后添加真正的子節點(diǎn)內容。
TREEVIEW 示例表單展示了如何使用該技術(shù)。 它有一個(gè) lLoadChildren 自定義屬性用于確定在表單顯示前是否載入全部訂單和產(chǎn)品。 試著(zhù)設置該屬性為 .F. 并注意在表單顯示前用了多長(cháng)時(shí)間。
你可能需要在一些點(diǎn)上重新載入顯示內容。 例如, 如果其它應用程序用戶(hù)正在添添客戶(hù)或訂單, 你可能需要用新添加的客戶(hù)和訂單刷新顯示內容。 要避免解決哪些記錄已經(jīng)顯示哪些沒(méi)有顯示的爭論, 可以使用節點(diǎn)集合的 Clear() 方法來(lái)清除顯示內容, 然后重新載入它。 該方法唯一的問(wèn)題是一些節點(diǎn)可能已經(jīng)擴展。 清除并重載顯示內容會(huì )使所有節點(diǎn)以默認的收縮狀態(tài)顯示。 要克服這一點(diǎn), 可以保存各個(gè)已存在節點(diǎn)的 Expanded 和 Key 或 FullPath 屬性到一個(gè)數組或游標中, 清除并重載顯示內容, 然后遍歷數組或游標恢復那些節點(diǎn)的Expanded 屬性。 TREEVIEW 示例表單的GetExpanded 和 SetExpanded 方法就是做這個(gè)工作的。
拖放
我曾在 CompuServe 看到過(guò)相關(guān)信息, 有很多關(guān)于似否可以拖放 TreeView 控件的混亂。一些人認為因為 VFP 沒(méi)有實(shí)現與其它應用程序(如 WORD )間的拖放, 故不能在 ActiveX 控件中實(shí)現它。 你可以拖放 ActiveX 控件的理由是雖然它們是非 VFP 應用程序,當你在表單上拖放一個(gè) ActiveX 控件時(shí), VFP 將它放入一個(gè) VFP 容器內, 這就是支持拖放的原因。 作為結果, 對 TreeView 進(jìn)行拖放(拖出或放進(jìn)) 時(shí)與其它控件沒(méi)有區別。
TREEVIEW 示例表單實(shí)現了拖和放。 你可以從樹(shù)上拖動(dòng)一個(gè)節點(diǎn)到表單下面的文本框中; 節點(diǎn)中的文本將顯示在文本框。 也可以拖動(dòng)”drag me to tree” 標簽并將其放入樹(shù)中; 一個(gè)新的節點(diǎn)將添加到顯示樹(shù)中,其內容為當前的日期時(shí)間。
聯(lián)系客服