- 浏览: 59929 次
- 性别:
- 来自: 杭州
最新评论
一、 引言:
TreeView控件适合于表示具有多层次关系的数据。它以简洁的界面,表现形式清晰、形象,操作简单而深受用户喜爱。而且用它可以实现ListView、ListBox所无法实现的很多功能,因而受到广大程序员的青睐。
树形结构在Windows环境中被普遍应用,但在数据库开发中面对层次多、结构复杂的数据,如何快速构造树形目录并实现导航呢?
二、 实现关键技术:
在Delphi提供的控件中包含了TreeView控件,但树的具体形成还需要用户编写代码。即它的列表项要在程序中动态添加,而这些列表数据通常由用户已录入在数据库表中,并作为数据库维护的一项内容。
许多人用TreeView构造树形目录时,通常都使用多个嵌套循环,或递归算法,将代码“编织”成树。这样不但算法复杂,且运行效率低下,不是最佳选择。这里介绍的是基于编码结构的高效算法。该算法的主要优点是:程序短小精悍,运行效率高,能快速实现数据库的树形结构,可以适应任何复杂的层次数据,实现方法简单,且树的内容有变动时,无需更改程序行。
算法的关键在于代码字典表中的代码字段的设计上一定要符合一定的代码设计要求,数据类型使用字符型。用户新增和修改代码时,必须有严格的约束,否则会导致程序出错。编码表的基本字段包括编码和编码名称,其编码规则是以数字、字母的位数来区分不同层次,同一层编码位数相同,层次按位数递增,程序通过判断编码位数来决定所在层数。
本例程中编码结构是“222”,编码格式为 “XX XX XX”。例如:第一层为10~99两位,第二层为1001~1099四位,用户需要做的是先要设计树的结构和对应编码,并录入相应名称,然后程序在读取这些数据时形成树。本例程不需要用户自己进行编码,程序中自动实现各层编码,保证了代码格式的正确性。
用TreeView导航表时,采用弹出式菜单,通过对话框操作数据表,同步更新树形控件和数据库。在所有操作中,树形控件不用重构,从而避免了重构时TreeView控件出现的闪动,也提高了程序的运行速度。
本示例程序为了使大家看清楚数据表中记录是否同步更新,用TDBGrid控件显示当前数据库表中所有记录。下面给出范例程序和主要代码分析。
三、 范例程序和主要代码分析:
我们以建立一个城市名称的树形结构为例来说明如何快速生成树形并实现导航数据表。
1. 先建立编码表:city_tree(bianma,cityname)。
2. 新建一个项目,按默认保存。
3. 新建一公共单元pubvar,在其中定义以下常量:
Const
cTreeCodeFormat = ‘222’;//编码格式为 XX XX XX
cTreeMaxLevel = 3;//最大编码层次
cTreeRootTxt = ‘城市’;//树根结点名称
这样做为了提高程序的通用性,以后用于其他代码字典的维护时,只需要更改这些特征常量。
4. 程序源代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DB, DBTables, ImgList, ComCtrls , PubVar, Grids, DBGrids, Menus , StrUtils, StdCtrls;
type
TForm1 = class(TForm)
Tree: TTreeView;
ImageList1: TImageList;
Table1: TTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
PopupMenu1: TPopupMenu;
AddMenu: TMenuItem;
DeleteMenu: TMenuItem;
RenameMenu: TMenuItem;
Query1: TQuery;
DataSource2: TDataSource;
procedure AddMenuClick(Sender: TObject);//点击增加子项菜单
procedure RenameMenuClick(Sender: TObject);//点击重命名菜单
procedure DeleteMenuClick(Sender: TObject); //点击删除该项菜单
procedure FormCreate(Sender: TObject);
procedure TreeClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure LoadTree(treeDB:TDBDataSet);//初始化树
procedure UpdateTree(curNode:TTreenode; nodeTxt:string; state:string);//更新树
function GetNodeLevel(sFormat,sCode:string):integer;//获得节点层数
function GetCurrChildNodeMaxMa(curNode:TTreenode):string;
//获得当前节点子节点的最大编码
function GetCurrentNodeBianma(curNode:TTreenode):string;//获得当前节点的编码
procedure UpdateTable(bianma:string; cityname:string ;state:string); //更新数据库
end;
var
Form1: TForm1;
CurrentTreeNode: TTreeNode;
// AddChildeTreeNode: TTreeNode;
// flag:boolean; //用于标识是否需要在重命名树结点时更新数据
implementation
uses AddChildUnit,RenameItemUnit;
{$R *.dfm}
procedure TForm1.LoadTree(treeDB:TDBDataSet);//初始化树
var
curID,nodeTxt:string;
level:integer;
mynode:array[0..3] of TTreenode;
begin //初始化变量
Screen.Cursor:=crHourGlass;
tree.Enabled:=True;
tree.Items.Clear;
level:=0 ;
//设置根节点
mynode[level]:=tree.items.add(Tree.Topitem,cTreeRootTxt);
mynode[level].ImageIndex:=1;
//遍历数据表,利用编码字段记录排序规律,依次添加树节点
with treeDB do
begin
try
if not Active then open;
first;
while not Eof do
begin
curID:=trim(FieldByName(’bianma’).AsString);
nodeTxt:=curID+’-’+trim(FieldByName(’cityname’).AsString);
level:=GetNodeLevel(cTreeCodeFormat,curID);
//这里返回代码的层次数
if level〉0 then
begin
//增加下一节点时,用添加子节点的方法可轻松实现节点间的层次关系
//注意:这里的父节点是用当前节点的上一级节点mynode[level-1]
mynode[level]:= tree.items.addchild(mynode[level-1],nodeTxt);
mynode[level].ImageIndex:=2;
end;
next;//下一条记录
end;
finally
close;
End;
mynode[0].expand(False);
Screen.Cursor:=crDefault;
end;
end;
function TForm1.GetNodeLevel(sFormat,sCode:string):integer;//获得节点层数
var i,level,iLen:integer;
begin
level:=-1 ;
iLen:=0;
if (sFormat〈〉’’) and (sCode〈〉’’) then
for i:=1 to Length(sFormat) do //分析编码格式,找出当前代码层次
begin
iLen:=iLen+StrToInt(sFormat[i]);
if Length(sCode)=iLen then
begin level:=i; break; end;
end;
result:=level;
end;
//以下过程在新增、删除、修改记录时,同步更新树形结构
procedure TForm1.UpdateTree(curNode:TTreenode; nodeTxt:string; state:string);
Begin
if UpperCase(state)=’ADD’ then
begin
curNode:=tree.items.addchild(curNode,nodeTxt);
curNode.ImageIndex:=2;
end;
if UpperCase(state)=’DEL’ then
begin
curNode.DeleteChildren;
curNode.delete;
end;
if UpperCase(state)=’EDI’ then curNode.Text:=nodeTxt;
end;
procedure TForm1.AddMenuClick(Sender: TObject);//点击增加子项菜单
var AddChildText, AddTableText,maxbianma : string;
begin
AddChildForm.Label1.Caption:=’为“’+CurrentTreeNode.Text+’“增加子项 ’;
if AddChildForm.ShowModal=mrOk then
begin
AddChildText:=AddChildForm.Edit1.Text;
maxbianma:=GetCurrChildNodeMaxMa(CurrentTreeNode);
if (CurrentTreeNode.Text=’城市’) and (maxbianma=’1000’) then
maxbianma:=’11’//如果当前节点为根节点,且只有一个子节点,使增加节点编码为11
else if CurrentTreeNode.Text=’城市’ then
maxbianma:=IntToStr(StrToInt(LeftStr(maxbianma,2))+1)
else
maxbianma:=IntToStr(StrToInt(maxbianma)+1); //使子项编码自动增1
if maxbianma〈〉’0’ then
begin
//增加树子层
AddTableText:=maxbianma+’-’+AddChildText;
UpdateTree(CurrentTreeNode,AddTableText,’add’); //更新树
UpdateTable(maxbianma,AddChildText,’add’); //更新表
ShowMessage(’添加成功!’);
end
else ShowMessage(’此层为最低子层,不能在该层增加子层’);
AddChildForm.Edit1.Text:=’’;
end;
end;
function TForm1.GetCurrChildNodeMaxMa(curNode:TTreenode):string;
//获得当前节点子节点的最大编码
var
aSQL,maxbianma:string;
li_pos:integer;
begin
li_pos:=pos(’-’,curNode.Text);
if li_pos=7 then
begin result:=’-1’; exit; end;
if (li_pos=0) and (not(curNode.HasChildren)) then // 如果当前节点为根节点并且没有子节点
begin
result:=’9’; //使根节点第一个节点编码为10
exit;
end
else begin
aSQL:=’select bianma from city_tree where bianma like “’ + MidStr(curNode.Text, 1, li_pos-1) + ’%“’;
Query1.UnPrepare;
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Text:=aSQL;
Query1.Prepare;
Query1.ExecSQL;
Query1.Active:=true;
Query1.Last;
maxbianma:=Query1.fieldbyname(’bianma’).AsString;
if Query1.RecordCount=1 then//如果当前项没有子项
maxbianma:=maxbianma+’00’;
Query1.Active:=false;
end;
result:=maxbianma;
end;
procedure TForm1.RenameMenuClick(Sender: TObject);//点击重命名菜单
var
bianma:string;
itemtext:string; //用于重命名时保存输入的Edit.text
begin
RenameItemForm.Label1.Caption:=’将“’+CurrentTreeNode.Text+’“命名为 ’;
if RenameItemForm.ShowModal=mrOk then
begin
itemtext:=RenameItemForm.Edit1.Text;
bianma:=GetCurrentNodeBianma(CurrentTreeNode);
Table1.Locate(’bianma’,bianma,[]);
UpdateTable(’’,itemtext,’edi’);
itemtext:=bianma+’-’+itemtext;
UpdateTree(CurrentTreeNode,itemtext,’edi’);
ShowMessage(’重命名成功!’);
end;
end;
//以下过程在新增、删除、修改记录时,同步更新数据库表
procedure TForm1.UpdateTable(bianma:string; cityname:string ;state:string); //更新数据库
begin
if state=’add’ then
begin
Table1.Active:=True;
Table1.Insert;
Table1.SetFields([bianma,cityname]);
Table1.Post;
end;
if state=’del’ then
begin
Table1.Active:=True;
Table1.Locate(’bianma’,bianma,[]);
Table1.Delete;
end;
if state=’edi’ then
begin
Table1.Edit;
Table1.FieldByName(’cityname’).AsString:=cityname;
Table1.Post;
end;
end;
procedure TForm1.DeleteMenuClick(Sender: TObject); //点击删除该项菜单
var
bianma:string;
begin
CurrentTreeNode.expand(False);
if CurrentTreeNode.Text=’城市’ then //如果当前节点为根节点
begin
ShowMessage(’不能删除根节点’);
exit;//退出该过程
end;
if CurrentTreeNode.HasChildren then //如果当前节点具有子项
begin
ShowMessage(’请先删除其子项’);
exit;//退出该过程
end;
if Application.MessageBox(PChar(’真的要删除“’+CurrentTreeNode.Text+’“这项吗?’),’警告’,MB_YESNO)=mrYES then
begin
bianma:=GetCurrentNodeBianma(CurrentTreeNode);
UpdateTree(CurrentTreeNode,’’,’del’);
UpdateTable(bianma,’’,’del’);
ShowMessage(’删除成功!’);
Table1.Refresh;//更新TBGrid控件中的显示
Table1.Active:=true;
CurrentTreeNode:=Form1.tree.selected;
end;
end;
function TForm1.GetCurrentNodeBianma(curNode:TTreeNode):string;//获得当前节点的编码
var
li_pos:integer;
bianma:string;
begin
li_pos:=pos(’-’,curNode.Text);
bianma:=MidStr(curNode.Text,1,li_pos-1);
result:=bianma;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadTree(Table1);
Table1.Active:=true;
end;
procedure TForm1.TreeClick(Sender: TObject);
begin
CurrentTreeNode:=Form1.tree.selected; //获得当前节点
end;
end.
unit PubVar;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, DbTables, StdCtrls, ExtCtrls, Buttons, Dialogs,Registry,Db, ComCtrls;
const
cTreeCodeFormat=’222’; //编码格式:xx xx xx
cTreeMaxLevel=3; //最大编码(树节点)层次
cTreeRootTxt=’城市’; //树的根节点名称
implementation
end.
5. 编译运行结果图:(附后)
四、 小结:
本程序与编码法快速生成树形结构,通过TreeView控件直接操作数据表,实现了对表的数据导航。如果你想通过点击TreeView控件某项直接显示该项的相关信息,可在该程序的基础上进行修改。
原文载于《电脑编程技巧与维护》2003年第一期P27
以上源码在http://www.comprg.com.cn/tit1_rjxz.htm 可以下载得到!需要的各位可以去下载!
程序运行结果图(稍后传上)
2004-6-18 8:20:32
查看评语???
2004-6-18 8:22:34 续篇
用Delphi实现Windows文件夹管理树
李鹏 薛志东
摘要:本文利用Windows名空间所提供的IShellFolder接口,用Delphi实现了文件夹管理树的生成。
关键字:文件夹 接口 Delphi
一、概述
Windows95/98视觉感观上区别Windows3.1的一个重要方面就是大量采用了树形视图控件,资源管理器左侧的文件夹管理树便是如此,它将本地和网络上的文件夹和文件等资源以层次树的方式罗列出来,为用户集中管理计算机提供了极大便利,同时在外貌上也焕然一新。Delphi为我们提供了大量Windows标准控件,但遗憾的是在目录浏览方面却只提供了一个Windows3.1样式的DirectoryListBox(Delphi5的测试版也是如此),因此,在Delphi中实现Windows文件夹管理树对开发更“地道”的Windows程序有着重大意义。
二、实现原理
Windows文件夹管理树的实现实质上是对Windows名空间(Namespace)的遍历。名空间中每个文件夹都提供了一个IShellFolder接口,遍历名空间的方法是:
1)调用SHGetDesktopFolder函数获得桌面文件夹的IShellFolder接口,桌面文件夹是文件夹管理树的根节点。
2)再调用所获得的IShellFolder接口的EnumObjects成员函数列举出子文件夹。
3)调用IShellFolder的BindToObject成员函数获得子文件夹的IShellFolder接口。
4)重复步骤2)、3)列举出某文件夹下的所有子文件夹,只至所获得的IShellFolder接口为nil为止。
下面解释将要用到的几个主要函数,它们在ShlObj单元中定义:
1)function SHGetDesktopFolder(var ppshf: IShellFolder): HResult;
该函数通过ppshf获得桌面文件夹的IShellFolder接口。
2)function IShellFolder.EnumObjects(hwndOwner: HWND; grfFlags: DWORD;
out EnumIDList: IEnumIDList): HResult;
该函数获得一个IEnumIDList接口,通过调用该接口的Next等函数可以列举出IShellFolder接口所对应的文件夹的内容,内容的类型由grfFlags来指定。我们需要列举出子文件夹来,因此grfFlags的值指定为SHCONTF_FOLDERS。HwndOwner是属主窗口的句柄。
3)function IShellFolder.BindToObject(pidl: PItemIDList; pbcReserved: Pointer;
const riid: TIID; out ppvOut: Pointer): HResult;
该函数获得某个子文件夹的IShellFolder接口,该接口由ppvOut返回。pidl是一个指向元素标识符列表的指针,Windows95/98中用元素标识符和元素标识符列表来标识名空间中的对象,它们分别类似于文件名和路径。需要特别指出的是:pidl作为参数传递给Shell API函数时,必须是相对于桌面文件夹的绝对路径,而传递给IShellFolder接口的成员函数时,则应是相对于该接口所对应文件夹的相对路径。pbcReserved应指定为nil,riid则应指定为IID_IShellFolder。
其它函数可以查阅Delphi提供的《Win32 Programmer’s Reference》。
三、程序清单
下面的源代码在Windows98中实现,并在Windows2000测试版中测试无误(程序运行结果如图1所示),有兴趣的读者可以将其改写成Delphi组件,以备常用。
unit BrowseTreeView;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ShlObj, ComCtrls;
type
PTreeViewItem = ^TTreeViewItem;
TTreeViewItem = record
ParentFolder: IShellFolder; // 接点对应的文件夹的父文件夹的IShellFolder接口
Pidl, FullPidl: PItemIDList; // 接点对应的文件夹的相对和绝对项目标识符列表
HasExpanded: Boolean; // 接点是否展开
end;
图1 程序运行结果
TForm1 = class(TForm)
TreeView1: TTreeView;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
private
FItemList: TList;
procedure SetTreeViewImageList;
procedure FillTreeView(Folder: IShellFolder; FullPIDL: PItemIDList; ParentNode: TTreeNode);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
ActiveX, ComObj, ShellAPI, CommCtrl;
// 以下是几个对项目标识符进行操作的函数
procedure DisposePIDL(ID: PItemIDList);
var
Malloc: IMalloc;
begin
if ID = nil then Exit;
OLECheck(SHGetMalloc(Malloc));
Malloc.Free(ID);
end;
function CopyItemID(ID: PItemIDList): PItemIDList;
var
Malloc: IMalloc;
begin
Result := nil;
OLECheck(SHGetMalloc(Malloc));
if Assigned(ID) then
begin
Result := Malloc.Alloc(ID^.mkid.cb + sizeof(ID^.mkid.cb));
CopyMemory(Result, ID, ID^.mkid.cb + sizeof(ID^.mkid.cb));
end;
end;
function NextPIDL(ID: PItemIDList): PItemIDList;
begin
Result := ID;
Inc(PChar(Result), ID^.mkid.cb);
end;
function GetPIDLSize(ID: PItemIDList): Integer;
begin
Result := 0;
if Assigned(ID) then
begin
Result := sizeof(ID^.mkid.cb);
while ID^.mkid.cb 〈〉 0 do
begin
Inc(Result, ID^.mkid.cb);
ID := NextPIDL(ID);
end;
end;
end;
function CreatePIDL(Size: Integer): PItemIDList;
var
Malloc: IMalloc;
HR: HResult;
begin
Result := nil;
HR := SHGetMalloc(Malloc);
if Failed(HR) then Exit;
try
Result := Malloc.Alloc(Size);
if Assigned(Result) then
FillChar(Result^, Size, 0);
finally
end;
end;
function ConcatPIDLs(ID1, ID2: PItemIDList): PItemIDList;
var
cb1, cb2: Integer;
begin
if Assigned(ID1) then
cb1 := GetPIDLSize(ID1) - sizeof(ID1^.mkid.cb)
else
cb1 := 0;
cb2 := GetPIDLSize(ID2);
Result := CreatePIDL(cb1 + cb2);
if Assigned(Result) then
begin
if Assigned(ID1) then
CopyMemory(Result, ID1, cb1);
CopyMemory(PChar(Result) + cb1, ID2, cb2);
end;
end;
// 将二进制表示的项目标识符列表转换成有可识的项目名
function GetDisplayName(Folder: IShellFolder; PIDL: PItemIDList;
ForParsing: Boolean): String;
var
StrRet: TStrRet;
P: PChar;
Flags: Integer;
begin
Result := ’’;
if ForParsing then
Flags := SHGDN_FORPARSING
else
Flags := SHGDN_NORMAL;
Folder.GetDisplayNameOf(PIDL, Flags, StrRet);
case StrRet.uType of
STRRET_CSTR:
SetString(Result, StrRet.cStr, lStrLen(StrRet.cStr));
STRRET_OFFSET:
begin
P := @PIDL.mkid.abID[StrRet.uOffset - sizeof(PIDL.mkid.cb)];
SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset);
end;
STRRET_WSTR:
Result := StrRet.pOleStr;
end;
end;
function GetIcon(PIDL: PItemIDList; Open: Boolean): Integer;
const
IconFlag = SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON;
var
FileInfo: TSHFileInfo;
Flags: Integer;
begin
if Open then
Flags := IconFlag or SHGFI_OPENICON
else
Flags := IconFlag;
SHGetFileInfo(PChar(PIDL), 0, FileInfo, sizeof(TSHFileInfo), Flags);
Result := FileInfo.iIcon;
end;
// 获得每个文件夹在系统中的图标
procedure GetItemIcons(FullPIDL: PItemIDList; TreeNode: TTreeNode);
begin
with TreeNode do
begin
ImageIndex := GetIcon(FullPIDL, False);
SelectedIndex := GetIcon(FullPIDL, True);
end;
end;
// 获得系统的图标列表
procedure TForm1.SetTreeViewImageList;
var
ImageList: THandle;
FileInfo: TSHFileInfo;
begin
ImageList := SHGetFileInfo(PChar(’C:\’), 0, FileInfo,
sizeof(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
if ImageList 〈〉 0 then
TreeView_SetImageList(TreeView1.Handle, ImageList, 0);
end;
// 生成文件夹管理树
procedure TForm1.FillTreeView(Folder: IShellFolder;
FullPIDL: PItemIDList; ParentNode: TTreeNode);
var
TreeViewItem: PTreeViewItem;
EnumIDList: IEnumIDList;
PIDLs, FullItemPIDL: PItemIDList;
NumID: LongWord;
ChildNode: TTreeNode;
Attr: Cardinal;
begin
try
OLECheck(Folder.EnumObjects(Handle, SHCONTF_FOLDERS, EnumIDList));
while EnumIDList.Next(1, PIDLs, NumID) = S_OK do
begin
FullItemPIDL := ConcatPIDLs(FullPIDL, PIDLs);
TreeViewItem := New(PTreeViewItem);
TreeViewItem.ParentFolder := Folder;
TreeViewItem.Pidl := CopyItemID(PIDLs);
TreeViewItem.FullPidl := FullItemPIDL;
TreeViewItem.HasExpanded := False;
FItemList.Add(TreeViewItem);
ChildNode := TreeView1.Items.AddChildObject(ParentNode,
GetDisplayName(Folder, PIDLs, False), TreeViewItem);
GetItemIcons(FullItemPIDL, ChildNode);
Attr := SFGAO_HASSUBFOLDER or SFGAO_FOLDER;
Folder.GetAttributesOf(1, PIDLs, Attr);
if Bool(Attr and (SFGAO_HASSUBFOLDER or SFGAO_FOLDER)) then
if Bool(Attr and SFGAO_FOLDER) then
if Bool(Attr and SFGAO_HASSUBFOLDER) then
ChildNode.HasChildren := True;
end;
except
// 你可在此处对异常进行处理
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
try
for I := 0 to FItemList.Count-1 do
begin
DisposePIDL(PTreeViewItem(FItemList[i]).PIDL);
DisposePIDL(PTreeViewItem(FItemList[i]).FullPIDL);
end;
FItemList.Clear;
FItemList.Free;
except
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Folder: IShellFolder;
begin
SetTreeViewImageList;
OLECheck(SHGetDesktopFolder(Folder));
FItemList := TList.Create;
FillTreeView(Folder, nil, nil);
end;
procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var
TVItem: PTreeViewItem;
SHFolder: IShellFolder;
begin
TVItem := PTreeViewItem(Node.Data);
if TVItem.HasExpanded then Exit;
OLECheck(TVItem.ParentFolder.BindToObject(TVItem^.Pidl,
nil, IID_IShellFolder, Pointer(SHFolder)));
FillTreeView(SHFolder, TVItem^.FullPidl, Node);
Node.AlphaSort;
TVItem^.HasExpanded := True;
end;
end.
2004-6-18 8:23:37 再续
仅仅十几行代码实现对TreeView的遍历
摘 要:对TreeView的遍历
关键字:TreeView
类 别:Delphi & IDE
E-Mail:iloveyou9595@sina.com
function TForm1.AllOverTreeView(node:TTreenode):TTreenode;
begin
while node〈〉nil do
begin
if node.HasChildren then
begin
node:=node.getFirstChild;
allovertreeview(node);
node:=node.Parent;
end;
if node.getNextSibling〈〉nil then
node:=node.getNextSibling
else
exit;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
parentnode:TTreenode;
begin
parentnode:=Mytreeview.Items.GetFirstNode;
AllOverTreeView(parentnode);
end;
------------------------------------------------------
遍历TreeView的方法有很多,我经过反复编程实现,上面是我用最少的代码实现TreeView的遍历。效果还不错。
利用这个对所有节点的遍历,我们可以很方便的对所有节点进行各种操作。例如:统计每层节点的个数、对满足要求的节点进行操作、等等。
投稿人:iloveyou9595 投稿日期:2003-3-26 23:51:00
2004-6-18 8:24:32 再续...
TreeView用法参考
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
SearchBtn: TButton;
DirectoryEdt: TMemo;
PathEdt: TEdit;
Label1: TLabel;
Image1: TImage;
procedure SearchBtnClick(Sender: TObject);
procedure MakeTree;
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.MakeTree;
var
Sr: TSearchRec;
Err: Integer;
FilePath: string;
begin
Err := FindFirst(’*.*’,$37,Sr);//$37为除Volumn ID Files外的所有文件
// 如果找到文件
while (Err = 0) do
begin
if Sr.Name[1] 〈〉 ’.’ then
begin
//找到文件
if (Sr.Attr and faDirectory) = 0 then
begin
end;
//找到子目录
if (Sr.Attr and faDirectory) = 16 then
begin
FilePath := ExpandFileName(Sr.Name);
DirectoryEdt.Lines.Add(FilePath);
ChDir(Sr.Name);
MakeTree;
ChDir(’..’);
end;
end;
//结束递归
Err := FindNext(Sr);
end;
end;
procedure TForm1.SearchBtnClick(Sender: TObject);
begin
DirectoryEdt.Lines.Clear;
ChDir(PathEdt.Text);
MakeTree;
end;
end.
这是个递归搜文件的例子,
摘 要:将数据表连接到TreeView中
关键字:数据表 TreeView
类 别:API
CoDelphi.com版权所有,未经允许,不得进行任何形式转载
procedure AddDataToTree(TreeView:TTreeView;DataSet:TDataSet)
var
TreeNodes:TTreeNodes
TreeNode:array[0..100] of TTreeNode;
i:Integer;
begin
DataSet.Close;
DataSet.Open;
TreeNodes=TTreeView.Items;
if DataSet.RecordCount〉0 then
begin
DataSet.First;
while not DataSet.Eof do
begin
TreeNode[0]=TreeNodes.Add(Nil,DataSet.Fields[0].AsString);
for i=1 to DataSet.Fields.Count-1 do
TreeNode[i]=TreeNodes.AddChild(TreeNode[i-1],DataSet.Fields[i].AsString);
DataSet.Next;
end;
end;
end;
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ImgList, StdCtrls, FileCtrl, ComCtrls;
type
TForm1 = class(TForm)
DirTreeView: TTreeView;
DriveComboBox1: TDriveComboBox;
FileListBox1: TFileListBox;
ImageList1: TImageList;
procedure FormCreate(Sender: TObject);
procedure DirTreeViewExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
FirstNode,DirNode : TTreeNode;
ItemCount,Index:integer;
Itemstr:string;
begin
ItemCount:= DriveComboBox1.Items.Count; //所有驅動器的個數
FirstNode := DirTreeView.Items.GetFirstNode;
for index := 0 to ItemCount -1 do
begin
ItemStr:= DriveComboBox1.Items[index];
ItemStr:= copy(ItemStr,1,pos(’:’,ItemStr)) ; //?得驅動器的名稱(比如C/D)
DirNode := DirTreeView.Items.AddChild(FirstNode, ItemStr );
DirNode.HasChildren := true;
DirNode.ImageIndex := 0;
DirNode.SelectedIndex := 1;
end;
end;
procedure TForm1.DirTreeViewExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var
DirNode : TTreeNode;
ItemCount,Index,level,icount:integer;
Itemstr,strPath:string;
begin
if node.Count = 0 then
begin
icount:=0;
level:=node.Level ;
dirnode:=node;
strPath:=node.Text+’\’ ;
while level〈〉0 do
begin
strPath:=dirnode.Parent.Text+’\’+strpath;
dirnode:=dirnode.parent;
level :=level -1;
end;
FileListBox1.Clear ;
FileListBox1.Directory := strpath;
ItemCount:= FileListBox1.Items.Count;
for index:=0 to ItemCount -1 do
begin
itemstr:=filelistbox1.items[index];
itemstr:= copy(ItemStr,2,pos(’]’,ItemStr)-2) ;
if (itemstr〈〉’.’) and (itemstr〈〉’..’) then
begin
DirNode := DirTreeView.Items.AddChild(Node,itemstr );
DirNode.HasChildren :=true;
DirNode.ImageIndex := 0;
DirNode.SelectedIndex := 1;
icount:=icount+1;
end;
if icount = 0 then
Node.HasChildren := false;
end;
end;
end;
end.
procedure TMain.CreateTree(QuerySource:TADOQuery;NodeParent:TTreeNode;treeview1:ttreeview);
var
pstr1, pstr2 : ^string;
NodeTemp : TTreeNode;
begin
pstr1 := NodeParent.Data;
with QuerySource do
begin
close;
sql.Clear;
sql.Text:=’SELECT key,xcode,xname FROM xzdm WHERE parent = ’ + ’’’’ + pstr1^ + ’’’’;
open;
if isempty then exit;
NodeTemp := nil;
while not eof do
begin
new(pstr2);
pstr2^ := FieldByName(’key’).AsString;
NodeTemp := TreeView1.Items.AddChildObject(NodeParent,
trim(FieldByName(’xname’).AsString)+’(’+fieldbyname(’xcode’).AsString+’)’, pstr2);
Next;
end;
end;
while NodeTemp 〈〉 nil do
begin
CreateTree(QuerySource, NodeTemp,treeview1);
NodeTemp := Nodetemp.getPrevSibling;
end;
end;
procedure TMain.RootTree(treeview1:ttreeview);
var
NodeTemp : TTreeNode;
pstr : ^string;
Query:TADOQuery;
begin
Query:=TADOQuery.Create(self);
query.Connection:=BgConnection;
try
Treeview1.Items.BeginUpdate;
with query do
begin
SQL.Text :=’select top 1 * from xzdm ’;
open;
if isempty then exit;
NodeTemp := nil;
while not eof do
begin
new(pstr);
pstr^ := FieldByName(’key’).AsString;
NodeTemp :=treeview1.Items.AddObject(nil,
trim(FieldByName(’xname’).AsString)+’(’+fieldbyname(’xcode’).AsString+’)’, pstr);
Next;
end;
end;
while NodeTemp 〈〉 nil do
begin
CreateTree(Query, NodeTemp,treeview1);
NodeTemp:=NodeTemp.getPrevSibling;
end;
treeview1.Items.EndUpdate;
finally
Query.Free;
end;
end;
var
node1:Ttreenode;
begin
node1:=TreeView1.items.AddChild(node,’收件箱’);//建一个节点
node1.ImageIndex:=86;//节点图象,要加imagelist控件
node1.SelectedIndex:=92;
node1.Data:=pointer(0);//重要,node1.data可以存入你有用ID
end;
其实看在线帮助是最好的了!另外就是要多用,Treeview我用的比较多,有什么问题可以问具体点!
楼上的说的不错,要注意的是Node节点中的.data是一个指针,要小心使用!
Delphi帮助文件里是这样写的:
Set ToolTips to True to specify that items in the tree view control have tooltips (Help Hints
当前的节点为: TreeView1.Selected;
他的字节点:child,父节点:Parent
其中的TreeNode的类型下可以保存一个指针类型的值;
var
CurItem: TTreeNode;
begin
CurItem := TreeView1.Items.GetFirstNode;
while CurItem 〈〉 nildo
begin
ListBox1.Items.Add(CurItem.Text);
CurItem := CurItem.GetNext;
end;
end
用GetFirstNode,GetNext会比较快
怎么知道我选中的是几级节点呀?
procedure TForm1.Button1Click(Sender: TObject);
begin
showmessage(vartostr(TreeView1.selected.level));
end;
在节点展开的事件中如下:
var sID:string;
Node,NewNode;TTreeNode;
begin
Node := TreeView1.Selected;
sID := PCHAR(Node.Data);
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(’select * from mytbl where ParentID=’ + sID);
Query1.Open;
Node.Items.Clear; //清除以下所有节点
While note Query1.Eof do
begin
NewNode:= TTreeView1.Items.AddChild(Node,Query1.FieldByName(’mc’).AsString);
NewNode.ImageIndex := ***;
Node.Data := PChar(Query1.FieldByName(’ID’).AsString); //注意保留索引值
Query1.Next;
end;
另外在TreeView的节点清除时注意释放内存。
在节点展开的事件中如下:
var sID:string;
NewNode;TTreeNode;
begin
sID := PCHAR(Node.Data);
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(’select * from mytbl where ParentID=’ + sID);
Query1.Open;
Node.Items.Clear; //清除以下所有节点
While note Query1.Eof do
begin
NewNode:= TTreeView1.Items.AddChild(Node,Query1.FieldByName(’mc’).AsString);
NewNode.ImageIndex := ***;
Node.Data := PChar(Query1.FieldByName(’ID’).AsString); //注意保留索引值
Query1.Next;
end;
以上程序仅供叁考,没做测试。
PNodeRec = 你定义的record 的变量;
procedure loadRootNode
var pID:integer; aNode: TTreeNode;
Item : PNodeRec;
begin
Query1.close; Query1.SQL.clear;
Query1.SQL.add(’select * from T where parentID=0’);
Query1.open;
where not Query1.eof do
begin
pID:=Query1.fieldByName(’id’).asInteger;
//(1)加载一个根节点
... ...
//aNode:=TV.Items.AddObject(nil, Item^.Name, Item);
//(2)加载此根节点下的所有子节点
loadChilds(pID,aNode);
Query1.next;
end;
end;
prodedure loadChilds(pID:integer;pNode:TTreeNode);
var cpID:integer; aNode: TTreeNode;
Item : PNodeRec;
begin
//(1)加载子节点
Query2.close; Query2.SQL.clear;
Query2.SQL.add(’select * from T where parentID=’+intToStr(pID));
Query2.open;
where not Query2.eof do
begin
//载入子节点
... ... //TV.Items.AddChildobject(pNode,Item^.Name,item);
Query2.next;
end;
//(2)递归载入子节点的子节点
for i:=0 to pNode.Count -1 do
begin
LoadChild(PNodeRec(pNode.Item[i].data)^.parentID,pNode.item[i]);
end;
end;
大家是从算法上来说,我来从GUI方面来说。
TreeList1.Items.BeginUpdate;
....执行添加代码
TreeList1.Items.EndUpdate;
哎呀,来晚了!
Eastunfail(恶鱼杀手)对!浪费时间的部分主要实在绘制上,不用BeginUpdate和用BeginUpdate在数据量较大时,差着“十万八千里”呢!算法当然也很重要,但要是从最快的角度将,影响最大的还是BeginUpdate和EndUpdate(就是等数据全部加载完毕再进行绘制)。我有亲身体会...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//无限层数:数据严格按照(层(在TreeView上),ParentID,ID,Text,图标序号)顺序排序************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
procedure DataSetToTreeView(DataSet: TDataSet; var TreeView: TTreeView; var TreeList: TStringList); overload;
var IsAvtive: Boolean;
TempIndex: integer;
TempNode: TTreeNode;
begin
DataSet.DisableControls;
IsAvtive := DataSet.Active;
if not IsAvtive then DataSet.Open;
//~~~~~~~~~~~~
发表评论
-
脚本是在
2009-03-11 19:00 1092大家交 ... -
DELPHI常见事件、常用方法、属性使用范围说明
2008-06-19 22:24 2848属性使用范围说明 Action 一些组件标识与组件相连接的 ... -
DELPHI常见属性说明
2008-06-19 21:37 1560DELPHI常见属性说明: ALI ... -
Delphi中的时间操作技术
2008-06-19 13:45 2712在编写应用程序时,我 ... -
delphi用于时间操作的函数 (2)
2008-06-19 13:44 1730TDateTime类型与字符串型转换函数: DateTime ... -
delphi用于时间操作的函数(1)
2008-06-19 13:43 1368用于时间操作的函数 Date函数: 定义:Date: TDa ... -
数据库表TreeView树的快速生成
2008-06-18 21:57 1972根据数据表的内容生成TreeView树状结构,通常的做法就是从 ... -
我的DBTreeView--TreeView直接连接数据表
2008-06-04 17:34 1071unit Unit1; interface uses ... -
Delphi下Treeview控件基于节点编号的访问
2008-06-04 17:31 1502有时我们需要保存和重建treeview控件,本文提供一种方法, ... -
Delphi中根据分类数据生成树形结构的最优方法
2008-06-04 17:27 1324很多系统都有类似于如下的表结构(table1): ID ... -
TreeView添加子节点和动态生成控件
2008-06-04 14:06 3932有这样一个TreeView,动态分有类A,B,C...(不一定 ... -
一种较简易的树型控件节点构造法
2008-06-03 21:42 1009把数据库中的数据在树型控件(TreeView)中正确显示是较为 ... -
Delphi中树型控件的使用技巧
2008-06-03 13:40 1416我们都知道,开发者 ... -
TREEVIEW右键菜单示例
2008-06-03 13:31 3622在 Form1 上拖一个 TreeView 控件,然后参考一下 ... -
TreeView的使用
2008-06-03 13:09 1341TreeView 是一个显示树型结构的控件,每一个节点都是一个 ... -
在Dephi中使用TStream读写数据的技巧
2008-06-02 13:29 820在Dephi中提供了一个抽 ... -
Delphi环境下灵活的数据库树表实现
2008-06-02 13:27 12621 数据结构设计 在 ...
相关推荐
在本文中,我们将深入探讨如何在Delphi中根据分类数据生成树形结构,特别是利用TreeView组件来实现这一目标。这在数据库应用、文件系统显示、组织结构展示等方面都有广泛应用。 首先,我们要理解Delphi中的TreeView...
在IT领域,尤其是在软件开发中,树形结构是一种常见的数据表示方式,特别是在处理层次关系时。"自动生成树形结构"这一技术主要应用于构建可视化界面,例如文件系统、组织架构或者数据库表之间的关系展示。本篇文章将...
"Delphi中快速实现数据库树形结构并实现Treeview导航表数据"是一个实用的技术主题,它涉及如何将数据库中的层级关系数据映射到图形化的Treeview组件上,使用户能够通过树形结构来浏览和操作数据。下面我们将深入探讨...
在Delphi编程环境中,开发人员经常需要将数据库中的数据以树形结构展示,这有助于用户直观地理解层次关系。在本教程中,我们将探讨如何快速地在Delphi中实现这样的功能,利用TreeView组件来导航数据库表的数据。我们...
Delphi基于DBTree方法生成树形菜单,菜单数据来自数据库。本树形菜单控件的数据库使用了Access,每次从数据库读取数据后,自动生成节点,节点自动编号,可一次删除节点成母节点,在一些大型的软件系统中,树形菜单的...
本文将详细讲解如何在 Delphi7 中使用树型控件,并自动根据数据集生成树型结构。 首先,理解 TTreeView 控件的基本操作。TTreeView 提供了一个可视化的组件,用于展示具有父节点和子节点的关系的数据。每个节点表示...
在Delphi中,我们可以使用TTreeview控件来创建基本的树形结构,但要实现无限级别的树形结构,通常需要自定义逻辑和递归算法。无限树形结构意味着节点可以有任意数量的子节点,且这些子节点也可以继续扩展出更多的子...
1. **TTreeView** 控件:Delphi提供的TTreeView控件是构建目录树视图的基本组件,它可以显示树形结构的数据,支持添加、删除、展开和折叠节点等操作。 2. **数据库访问组件**:如DBExpress、ADO等,用于连接和查询...
在Delphi中,"树形结构"通常指的是控件TTreeView,它是用户界面中的一个重要元素,用于展示层次化的数据。这种数据结构在很多场景下都非常实用,比如文件系统浏览、组织结构展示等。 在描述中提到的"delphi树形结构...
2. **创建数据源**:树形结构的数据通常包含父节点和子节点,因此你需要一个支持层级数据的数据源。这可以是自定义的类结构,或者是数据库中的表,通过关系字段表示父子关系。例如,你可以使用`DataSet`或`DataTable...
在《Delphi算法与数据结构》中,你将找到各种类型的算法实现,如排序算法(快速排序、冒泡排序、插入排序等)、搜索算法(二分查找、广度优先搜索、深度优先搜索等)以及图论中的算法(最短路径算法、最小生成树等)...
1. **TTreeView组件**:Delphi中用于显示树形结构的主要控件是TTreeView。我们可以使用TTreeView来创建和维护一个节点层次结构,每个节点代表一个地域,如国家、省份、城市等。 2. **数据绑定**:为了将地域数据与...
delphi比啊摹写生成最小生成树,给出点 就可以生成最小胜创术
本篇文章将深入探讨如何在Delphi中实现XML树的生成。 首先,我们需要了解Delphi中处理XML的主要组件——TXMLDocument。TXMLDocument是Indy库中的一个组件,它可以用来创建、加载和保存XML文档。在设计阶段,将...
Delphi使用递归算法读取数据库生成树形菜单,Delphi创建一个动态的树叉菜单,菜单内容基于递归法从数据库读取而来,请参考数据库显示,看数据库内的内容,这里仅仅给了附表和数据库中的数据创建树的示例,至于向...
在 Delphi 开发环境中,构建一个能够从数据库中动态生成目录树的应用程序是一项常见的任务,尤其是在数据管理和文件系统操作相关的项目中。这个标题"Delphi 从数据库生成目录树"暗示我们将利用 Delphi 的组件和...
以下是一个简化的步骤,展示了如何在Delphi中使用ZXing库生成二维码: 1. **引入ZXing库**:首先,你需要下载ZXing.Delphi库,这是一个为Delphi编译器优化的版本。将库的源代码导入到你的项目中,确保所有依赖项都...
总之,Delphi中从Excel导入数据的通用方法主要依赖于第三方组件,如JvExcel。通过这些组件,我们可以方便地访问Excel文件中的数据,并将其集成到Delphi应用程序中。当然,还有其他库和方法,如使用Microsoft的OLE ...
在Delphi中,这些数据结构可以通过内置的TArray、TList、TStack、TQueue等类进行实现,也可以自定义结构体或类来构建更复杂的数据结构。例如,数组提供了固定大小的连续内存空间,适合快速访问;链表则通过节点间的...
在Delphi中,数据结构包括数组、链表、栈、队列、树(如二叉树、AVL树、红黑树等)、图等。这些数据结构的选择和实现方式直接影响到程序的性能和可维护性。例如: 1. **数组**:是最基本的数据结构,提供了固定大小...