TreeView組件是一個樹狀的列表組件,它在應用程序的編寫中有極其廣泛的應用。如:資源管理器、網際快車(FlashGet)、FoxMail等,其中,Windows的資源管理器就是一個典型的例子。
在C++ Builder中,要使用TreeView組件是件很容易的事情,只要調用TreeView組件的Add或AddChild方法就可以很方便地為TreeView添加一個新的節點。若要將指定的磁盤或目錄的樹狀結構放到TreeView組件中,可以使用遍歷目錄樹的方法將指定磁盤或目錄下的所有目錄(包括子目錄)和文件添加到TreeView中。
下面,讓我們通過實際的例子來實現把C盤目錄樹裝載到TreeView中。
首先,運行Borland C++ Builder 5.0,在窗體Form1上添加兩個Button組件、一個Edit組件、一個TreeView組件和一個Animate組件。然後把組件Button1的Caption屬性改為“裝載TreeView1”,把組件Button2的Caption屬性改為“清除”,把組件Edit1的Text屬性改為“C:\”,用來設置默認的要遍歷的目錄--C盤的根目錄,組件Animate1是在遍歷目錄時用來顯示動畫,在這裡把它的CommonAVI屬性設成“aviFindComputer”,為顯示查找計算機的動畫,你也可以設為其它動畫。
按F12鍵打開代碼編輯窗口,在“TForm1 *Form1;”語句的下面加入下面的這條語句定義自定義函數BrowDir:
void __fastcall BrowDir(TTreeNodes * Nodes,AnsiString PathName,TTreeNode * Num);
BrowDir函數是一個通過遞歸調用來實現遍歷目錄的自定義函數。它有三個參數,第一個參數傳送一個TreeView組件的節點用以增加新的節點,第二個參數是指定目錄的路徑,第三個參數也是傳送一個節點,用來說明要在那個節點增加新節點。
下面是它的程序清單:
void __fastcall BrowDir(TTreeNodes * Nodes,AnsiString PathName,TTreeNode * Num)
{
TSearchRec sr;
TTreeNode* Layel;
//列舉所有的目錄
if (FindFirst(PathName+"*.*", faAnyFile, sr) == 0)
{
do
{
//判斷是否是目錄,並排除目錄“.”和“..”
if((sr.Attr & faDirectory) && sr.Name!="." && sr.Name!="..")
{
//增加新節點
Layel=Nodes->AddChild(Num,"目錄:" + sr.Name);
//調用函數本身,進入子目錄
BrowDir(Nodes,PathName+sr.Name+"\\",Layel);
}
} while (FindNext(sr) == 0);
FindClose(sr);
}
//列舉所有文件
if (FindFirst(PathName+"*.*", faAnyFile, sr) == 0)
{
do
{
if(!(sr.Attr & faDirectory))
Nodes->AddChild(Num,"文件:" + sr.Name);
} while (FindNext(sr) == 0);
FindClose(sr);
}
}
將自定義函數BrowDir()添加到程序中,然後雙擊Button1組件,在它的OnClick事件中加入:
//設置光標為漏斗
Screen->Cursor=crHourGlass;
//激活Animate
Animate1->Active=true;
AnsiString Path=Edit1->Text;
//如果Path最後一個字符不是“\”就在後面加上“\”
if(Path.SubString(Path.Length(),1)!="\\")
Path+="\\";
BrowDir(TreeView1->Items,Path,TreeView1->Items->Add(NULL,Path));
//設置光標為正常狀態
Screen->Cursor=crDefault;
//關閉Animate
Animate1->Active=false;
在Button2的OnClick事件中加入:
TreeView1->Items->Clear();
TreeView2->Items->Clear();
按F9編譯運行,點擊“裝載TreeView1”按鈕,過一會兒TreeView1組件就會出現C盤目錄樹的結構。
這種方法的優點是打開子節點的速度快,缺點就是遍歷目錄時,當子目錄和文件越多,遍歷時所需的時間就越長。用這樣例子來做資源管理器,顯然是不行的。
我們都知道,TreeView組件有一個OnChange事件,當TreeView組件的節點發生改變的時候就會發生該事件。若在該事件中加入相應的代碼,把改變的節點所表示目錄下的子目錄添加到TreeView組件中,這樣,程序運行時速度就會很快。
這種方法實現步驟如下:
往窗體Form1上再添加一個Button組件和一個TreeView組件,它們的Name屬性分別為:Button3和TreeView2。把Button3的Caption屬性改為“裝載TreeView2”,然後雙擊Button3組件,在Button3的OnClick事件中加入以下代碼:
AnsiString Path=Edit1->Text;
if(Path.SubString(Path.Length(),1)!="\\")
Path+="\\";
TreeView2->Items->Add(NULL,Path);
在TreeView2的OnChangeing事件中加入:
Screen->Cursor=crHourGlass;
Animate1->Active=true;
//防止重復增加節點
if(Node->Count==0)
{
TSearchRec sr;
AnsiString DirName,DirTmp;
TTreeNode * NodeTmp=Node;
DirName=Node->Text;
//得到完整的路徑
for(int I=Node->Level ;I>0 ;I--)
{
NodeTmp=NodeTmp->Parent;
DirTmp=NodeTmp->Text;
if(DirTmp.SubString(DirTmp.Length(),1)!="\\")
DirTmp+="\\";
DirName.Insert(DirTmp,0);
}
if(DirName.SubString(DirName.Length(),1)!="\\")
DirName+="\\";
if (FindFirst(DirName+"*.*", faAnyFile, sr) == 0)
{
do
{
if((sr.Attr & faDirectory) && sr.Name!="." && sr.Name!="..")
{
TreeView2->Items->AddChild(Node,sr.Name);
}
} while (FindNext(sr) == 0);
FindClose(sr);
}
}
Screen->Cursor=crDefault;
Animate1->Active=false;
這種方法速度雖然很快,但由於只是添加一層的子目錄,所得到的節點表示的目錄下不管有沒有子目錄,節點左邊都沒有“+”符號(有“+”表示有子節點),因此就有必要將它修改一下了,於是就有第三種方法的出現。
第二種方法是因為只添加了下一級的子目錄,所以才會出現這種問題,如果我們添加到下兩級的子目錄,問題就會得到解決,這就是第三種方法。這樣,當打開一個節點的時候,OnChange事件的代碼就會把下兩級的子目錄添加進來,再打開一個節點,該節點下兩級的子目錄又被添加進來,看起來就像是把整個目錄樹放到了TreeView中一樣。
第三種方法的實現如下:
再添加一個Button組件Button4和一個TreeView組件TreeView3到窗體Form1上,將Button4的Caption屬性改為“裝載TreeView3”,雙擊Button4組件,在Button4的OnClick事件中加入以下代碼:
AnsiString Path=Edit1->Text;
if(Path.SubString(Path.Length(),1)!="\\")
Path+="\\";
TTreeNode * Node1=TreeView3->Items->Add(NULL,Path);
TSearchRec sr;
if (FindFirst(Path+"*.*", faAnyFile, sr) == 0)
{
do
{
if((sr.Attr & faDirectory) && sr.Name!="." && sr.Name!="..")
{
TreeView3->Items->AddChild(Node1,sr.Name);
}
} while (FindNext(sr) == 0);
FindClose(sr);
}
在TreeView3的OnChangeing事件中加入:
Screen->Cursor=crHourGlass;
Animate1->Active=true;
TSearchRec sr;
TTreeNode * NodeTmp=Node;
AnsiString DirName,DirTmp;
DirName=Node->Text;
for(int I=Node->Level ;I>0 ;I--)
{
NodeTmp=NodeTmp->Parent;
DirTmp=NodeTmp->Text;
if(DirTmp.SubString(DirTmp.Length(),1)!="\\")
DirTmp+="\\";
DirName.Insert(DirTmp,0);
}
if(DirName.SubString(DirName.Length(),1)!="\\")
DirName+="\\";
for(int J=0;J<Node->Count;J++)
{
if(Node->Item[J]->Count==0);
{
if (FindFirst(DirName+Node->Item[J]->Text+"\\*.*", faAnyFile, sr) == 0)
{
do
{
if((sr.Attr & faDirectory) && sr.Name!="." && sr.Name!="..")
{
TreeView3->Items->AddChild(Node->Item[J] ,sr.Name);
}
} while (FindNext(sr) == 0);
FindClose(sr);
}
}
}
Screen->Cursor=crDefault;
Animate1->Active=false;
好了,程序代碼加入完後,將各個組件排列一下,按F9再編譯運行一次,這三種將目錄樹的結構裝入TreeView中的方法,你比較喜歡那一種呢?自己比較一下吧。以上的程序在Win98/Win2000,Borland C++ Builder 6.0下運行通過。