打開電腦,進入Windows操作系統,在資源管理器的左邊欄中清楚地顯示了系統管理的所有磁盤的信息以及各個磁盤所容納的文件與文件夾(如圖一)。這種常見的顯示方式是由一個根節點和若干個子節點構成的,這被稱為“樹形結構”。這種樹形結構的用途非常廣泛,在很多常用軟件中都出現過它的身影。Windows中將這種結構封裝為“樹形控件”,即TreeView控件,它與ListView、Button等一樣都屬於系統自帶的通用公共控件。在Delphi中,TreeView也被封裝成了VCL組件,它的位置在“Win32組件”面板上,是我們最常用的幾個組件之一。
Delphi自帶的TreeView組件可以顯示樹形結構,也可以為每個節點指定不同的圖標來區分各自的功能。但在平時的使用中,我們發現它並不能嵌入CheckBox或者是RadioButton組件,這樣用戶就不能直觀地選擇某一部分節點或某個節點。如何來解決這個問題呢?我們思考之後發現,有兩種思路可以完成前面所述的任務。一種是在TreeView組件的基礎上繼承的它的功能,並添加所要的功能(使TreeView能嵌入CheckBox或者是RadioButton組件)即重寫一個組件。另一種是利用用戶的錯覺,將CheckBox或者是RadioButton所能實現的外觀用兩種狀態的圖片(一種是選中狀態另一種是未選中狀態)來交替顯示,走迂回路線來完成任務。我們來分析一下這兩種方法的優缺點:第一種方法要重寫一個組件,顯然難度較大,所用時間較長;第二種方法,利用TreeView組件本身就具備的顯示圖標功能,簡便易行,所用時間短,能夠完成需求。比較之後,我們選擇作用第二種方法,先來看一下完成之後的效果(如圖二),應該說是達到了目的,現在我們來細述一下完成的過程:
首先,我們在Win32面板上選擇ImageList組件,設置它的StateImages屬性,包括兩種狀態的圖標,一種是選中狀態,另一種是未先中狀態。
其次,我們調用ToggleTreeView過程(實現方法見後文),實現在鼠標單擊和鍵盤選擇的狀態下改變狀態圖標的功能。
ToggleTreeView過程實現代碼如下:
procedure ToggleTreeViewCheckBoxes(
Node :TTreeNode;
cUnChecked, //CheckBox未選中狀態
cChecked, //CheckBox選中狀態
cRadioUnchecked, //RadioButtion未選中狀態
cRadioChecked :integer); // RadioButtion選中狀態
var
tmp:TTreeNode;
begin
if Assigned(Node) then
begin
//如果當前是未選中狀態則變為選中狀態
if Node.StateIndex = cUnChecked then
Node.StateIndex := cChecked
//如果當前是選中狀態則變為未選中狀態
else if Node.StateIndex = cChecked then
Node.StateIndex := cUnChecked
else if Node.StateIndex = cRadioUnChecked then
begin
tmp := Node.Parent;
if not Assigned(tmp) then
tmp := TTreeView(Node.TreeView).Items.getFirstNode
else
tmp := tmp.getFirstChild;
while Assigned(tmp) do
begin
if (tmp.StateIndex in
[cRadioUnChecked,cRadioChecked]) then
tmp.StateIndex := cRadioUnChecked;
tmp := tmp.getNextSibling;
end;
Node.StateIndex := cRadioChecked;
end; // if StateIndex = cRadioUnChecked
end; // if Assigned(Node)
end;
第三,上面的代碼解決的是狀態圖標轉換的問題,那如何解決在鼠標單擊和鍵盤選擇之後就改變狀態呢?下面給出實現代碼:
procedure TForm1.TreeView1Click(Sender: TObject);
var
P:TPoint;
begin
GetCursorPos(P); //得到光標的位置
P := TreeView1.ScreenToClient(P);
if (htOnStateIcon in
TreeView1.GetHitTestInfoAt(P.X,P.Y)) then
ToggleTreeViewCheckBoxes(
TreeView1.Selected,
cFlatUnCheck,
cFlatChecked,
cFlatRadioUnCheck,
cFlatRadioChecked);
end;
(2)當鍵盤選擇時,代碼如下:
procedure TForm1.TreeView1KeyDown(
Sender: TObject;
var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_SPACE) and
Assigned(TreeView1.Selected) then
ToggleTreeViewCheckBoxes(
TreeView1.Selected,
cFlatUnCheck,
cFlatChecked,
cFlatRadioUnCheck,
cFlatRadioChecked);
end;
最後,我們給出一個小例子,來驗證一下的我們試驗的結果。在窗體上的擺放TreeView、ImageList、Button和一個Memo組件(如圖三),在加入上面的代碼之後,我們來編寫這個Button的單擊事件的代碼:
procedure TForm1.Button1Click(Sender: TObject);
var
BoolResult:boolean;
tn : TTreeNode;
begin
if Assigned(TreeView1.Selected) then
begin
tn := TreeView1.Selected;
BoolResult := tn.StateIndex in
[cFlatChecked,cFlatRadioChecked];
Memo1.Text := tn.Text +
#13#10 +
'Selected: ' +
BoolToStr(BoolResult, True);
//Memo給出所選中的節點和當前的狀態
end;
end;
因為篇幅所限,上面的例子給出是最簡單的一個情況,如果要編寫更為專業的軟件,請讀者朋友充分發揮想象,一定做出更好的效果(如圖四)。