高分求~!如何建立与windows资源管理器 中的树型目录 的功能,具有插入和删除。 如何建立与windows资源管理器 中的树型目录 的功能,具有插入和删除。谢谢了~!新手~! 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 用TTreeView就行了,很简单的查查帮助看看例子就搞定了! 树视图同数据库的关联陶占红 单 莹 前 言 树形图用于显示按照树形结构进行组织的数据,其用途比较广泛,如计算机中的文件系统(Windows95中的资源管理器)、企业或公司的组成结构等。VB、PB、Delphi等工具提供了一个功能很强的树型控件TTreeView,可以用来描述复杂的层次关系。由于树形图结构较复杂,使用起来常不知如何下手。笔者结合电信综合统计管理系统中指标维护这一具体实例,详细阐述在Delphi下如何将树型控件的使用与数据库联系起来,实现数据分任意多层显示,方便地进行增加、修改、删除操作,而且用拖放技术实现各层数据之间的移动、复制。笔者希望通过对该实例的阐述,达到抛砖引玉的效果,与各位同仁相互交流,共同进步。 一、指标树的建立 具体方法是:创建一个数据库,设计指标表t_pub_index,包含index_id、parent_id、index_name字段,其它字段根据实际业务而定,指标名称index_name将在树型控件的节点上显示,index_id字段保存节点的唯一标识号,parent_id表示当前节点的父节点号,标识号组成了一个“链表”,记录了树上节点的结构。设计一窗体Frm_sys_index,其上放置TreeView控件tv_zb、Query控件Query1及其它指标属性编辑显示控件。一个树的节点又包含文本(Text)和数据(Data)。Text为String类,用来显示指标或指标目录名称。Data则为无定形指针(Untyped Pointer),可以指向一个与节点相联系的数据结构,该结构与数据库指标表相应域关联,如指标ID、上级节点ID。 Query控件的表达式为: select index_id, parent_id, index_name from t_pub_index start with index_id=0 connect by prior index_id=parent_id 其中start with 和connect by 是Oracle的SQL语句的保留字,使一条记录的parent_id列的值等于前一记录的index_id列的值,并以parent_id等于0的记录开始。 建树的基本思路是: procedure TFrm_sys_index.createtree; var curValue: indexPointer; //指向与节点相联系的数据结构的指针 curNode : TTreeNode; //当前节点 curid : integer; //当前节点标识号 begin curNode := nil; curid := -1; Query_index.Open; Query_index.first; while not Query_index.Eof do begin new(curValue); With curValue^ do 将数据库指标表t_pub_index各字段值赋curValue 所指数据结构 while(curid <> curValue.parent_id) do //当前节点的标识号不等于当前记录的父节点号 begin curNode := curNode.parent; curid:= indexPointer(curNode.data).index_id; end; curNode := tv_zb.Items.AddChildObject(curNode, curValue^.index_name,curValue); //在当前节点上添加子节点,显示节点指标名称,所带指针指向一个与指标数据相联系的数据结构 curid := indexPointer(curNode.data).index_id; Query_index.next; end; Query_index.close; end; 二、增加、删除、修改树节点 单纯在Treeview 上增加、删除、修改节点只需用它本身提供的Treeview.Items. AddChildObject、 Treeview.Selected.Delete、Treeview.Selected.EditText等方法即可,但要相应修改数据库中的数据,必须通过递归调用同一个函数(用于删除一个选项)来遍历所选节点下的所有子节点。下面以删除节点为例介绍具体实现流程: function TFrm_sys_index.delnode(node1:TTreenode):TTreenode; var childnode:TTreenode; begin childnode:=node1.GetLastChild; //按倒序获得子项,因为删除选项时,列表会发生变化 while childnode<>nil do childnode:=delnode(childnode); //如子项不为空,进行递归调用 index_id:=inttostr(indexpointer(node1.data).index_id);//获得该节点对应指标 在数据库删除相应指标; result:=node1.parent.GetPrevChild(node1); //定位到该节点的上一节点 node1.delete; //删除树节点 end; 三、拖动树节点 拖动树节点基本上是通过建立目标项的新子项、向它复制源项、删除原项来移动选项。与上述删除操作相似,也是通过递归调用同一个函数(用于移动一个选项),按倒序移动所选节点下的所有子节点。下面是递归过程的代码: procedure TFrm_sys_index.CopyNodeUnder(treeview:TTreeview; sourcenode,targetnode:ttreenode); var newnode:ttreenode; i:integer; begin newnode:=treeview.items.addchildfirst(targetnode,''); //建立目标项 newnode.assign(sourcenode); //复制源项属性 for i:=sourcenode.count-1 downto 0 do //递归调用,按倒序移动其所有子项 CopyNodeUnder (treeview,sourcenode.item[i],newnode); treeview.items.delete(sourcenode); //删除源项 end; Treeview对拖动操作提供支持,我们将组件的DragKind属性设置为dkDrag,DragMode属性设置为dmAutomatic,并为OnDragOver与OnDragDrop事件编写了处理程序。OnDragOver事件处理程序对允许移动的条件进行判断,排除需要避免的特殊情况。代码如下: procedure TFrm_sys_index.tv_zbDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var targetnode,sourcenode:TTreenode; begin targetnode:=tv_zb.getnodeat(x,y); if (Source=Sender) and (targetnode<>nil) then //保证移动在TreeView上,且目标节点不为空 begin Accept:=true; sourcenode:=tv_zb.selected; //以下代码防止用户将一个选项拖到其子项上(它会随着选项一起移动,导致死循环) while (targetnode.parent<>nil) and (targetnode <> sourcenode) do targetnode:=targetnode.parent; if (targetnode = sourcenode) then Accept:=false; end else Accept:=false; end; OnDragDrop事件处理程序启动前述移动过程CopyNodeUnder,修改数据库数据。此外,在大批量添加数据到Treeview中时最好使用TreeView.Items.BeginUpdate和 TreeView.Items.EndUpdate,这样能加快显示速度。大致流程如下: procedure TFrm_sys_index.tv_zbDragDrop(Sender, Source: TObject; X, Y: Integer); var targetnode,sourcenode:TTreenode; begin targetnode:=tv_zb.getnodeat(x,y); //获得源节点 sourcenode:=tv_zb.selected; //获得目标节点 修改数据库中当前节点的父节点号parent_id,使其等目标节点标识号; tv_zb.items.beginupdate; //禁用TreeView重绘操作 try copynodeunder(tv_zb,sourcenode,targetnode); //启动移动过程 tv_zb.selected:=targetnode; finally tv_zb.items.endupdate; //重新设置 end; end; 一. 如何初始化一个TreeView? 弄一个窗口,放上一个TreeView和一个Button,分别取名为TV1和Btn1。如果需要 在每个节点前有个图,请在窗口上放上一个ImageList,取名为ImageList1,双击 它,加入六个图标。还要记得记得将TV1的Images属性改为ImageList1噢。双击按 钮Btn1,在里面填入以下代码,然后按F9运行,点击Btn1就可以看到效果了。 procedure TForm1.Btn1Click(Sender: TObject); Const MyDocDir = 'C:\My Documents'; PersonDir = '3hSoft'; Var Var I : Word; SubNodeName : array [1..5] of ShortString; RootNode, SubNode : TTreeNode; P : PString; begin SubNodeName[1] := '便笺'; SubNodeName[2] := '发件箱'; SubNodeName[3] := '联系人'; SubNodeName[4] := '任务'; SubNodeName[5] := '日记'; TV1.Items.Clear; TV1.Items.BeginUpdate; New(P); P^ := MyDocDir + '\' + PersonDir; RootNode := TV1.Items.AddObject(Nil, '个人文件夹', P); // 此 Node 的图标已对 Images 属性中取第 0 个了。 For I := 1 to 5 do begin New(P); P^ := MyDocDir + '\' + PersonDir + '\' + SubNodeName[I]; SubNode := TV1.Items.AddChildObject(RootNode, SubNodeName[I], P) ; ; // 如果不想使用图标的话请删除以下两行 SubNode.ImageIndex := I; SubNode.SelectedIndex := I; end; TV1.Items.EndUpdate; end; 二.在TreeView中如何设置选中结点 var i:integer; {i为设置的选中结点的索引值} begin if i>treeview1.items.count then treeview1.items[i].selected:=true; 或 treeview1.selected:=treeview1.items[i]; 三。设置TreeView结点的图形 1. 设置TreeView的images属性为已存在的images对象 treeview1.images:=imagelist1; 2. 在加入结点后执行: var anode:TTreeNode; begein anode:=Treeview1.add(nil,'item1'); anode.imageindex:=0; {结点未选中时显示的图标} anode.selectedindex:=1; {结点选中时显示的图标} end 3. 如果结点图形在改变后未发生变化,可以执行: treeview1.refresh; 四。如何批量处理TreeView结点 使用TreeView的items属性的BeginUpdate和EndUpdate方法,例: TreeView1.items.BeginUpdate; for i:=0 to TreeView1.items.count-1 do begin //将每个结点的文字改成为小写字母 TreeView1.items[i].text:=lowercase(TreeView1.items[i].text); end; TreeView1.items.EndUpdate; 五。实现TreeView结点拖拽的实例 下面的程序片段演示了如何实现拖拽treeview构件结点的例子 {鼠标按下时执行的语句} procedure TForm1.Treeview1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin {判断左键按下并且鼠标点在一个结点上开始实现拖拽} if ( Button = mbLeft ) and ( htOnItem in Treeview1.GetHitTestInfoAt( X, Y ) ) then begin Treeview1.BeginDrag( False ); end; end; {鼠标拖动执行语句} procedure TForm1.Treeview1DragOver( Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var Node : TTreeNode; begin if Source = Treeview1 then begin Node := Treeview1.GetNodeAt( X, Y ); {取当前结点} if Node <> nil then {当前结点不为空才能实现拖拽,accept:=true} Accept := true; end; end; {鼠标释放时执行的语句} procedure TForm1.Treeview1DragDrop( Sender, Source: TObject; X, Y : Integer ); var TempNode : TTreeNode; AttachMode : TNodeAttachMode; begin if Treeview1.Selected = nil then Exit; AttachMode := naAddChild; {设置结点移动模式,设移动结点为子结点} { 注意在这里存在一个bug,当移动结点时,如果目标结点没有子结点,} { 则加入的新的子结点会失败,所以先在当前目标结点的下面 } { 加入一个临时子结点,移动完毕后,再将临时结点删除 } Treeview1.Items.BeginUpdate; try TempNode := Treeview1.Items.AddChild( Treeview1.DropTarget, 'Temp' ); try { 移动选中的结点到目标结点 } Treeview1.Selected.MoveTo( Treeview1.DropTarget, AttachMode ); finally TempNode.Free; { 不要忘了释放临时结点 } end; finally Treeview1.Items.EndUpdate; end; end; 用Tshelltreeview不就行了吗,直接就可以做资源管理器了 dll中dataset报错 帮我修改一下group by的SQL,不要笑:) 如果计算按键的次数,哪位大侠指点下啊! 如何不用设置数据库别名,可以运行系统? 我有一个DELPHI5的程序,但必须要连接到网络才能用,单独一台机就不行了,有没有好的办法, 终于有了五个三角角,尽情高兴,散分! MM,有个难题急需解决是关于数据库中一条记录打印预览的问题请各位帮忙 怎样抓取当前鼠标形状: ie6图片工具拦的原理 sql的日期函数; 怎样在Delphi下反编译??? 怎样让一个窗体始终在最前端?
陶占红 单 莹 前 言 树形图用于显示按照树形结构进行组织的数据,其用途比较广泛,如计算机中的文件系统(Windows95中的资源管理器)、企业或公司的组成结构等。VB、PB、Delphi等工具提供了一个功能很强的树型控件TTreeView,可以用来描述复杂的层次关系。由于树形图结构较复杂,使用起来常不知如何下手。笔者结合电信综合统计管理系统中指标维护这一具体实例,详细阐述在Delphi下如何将树型控件的使用与数据库联系起来,实现数据分任意多层显示,方便地进行增加、修改、删除操作,而且用拖放技术实现各层数据之间的移动、复制。笔者希望通过对该实例的阐述,达到抛砖引玉的效果,与各位同仁相互交流,共同进步。 一、指标树的建立 具体方法是:创建一个数据库,设计指标表t_pub_index,包含index_id、parent_id、index_name字段,其它字段根据实际业务而定,指标名称index_name将在树型控件的节点上显示,index_id字段保存节点的唯一标识号,parent_id表示当前节点的父节点号,标识号组成了一个“链表”,记录了树上节点的结构。设计一窗体Frm_sys_index,其上放置TreeView控件tv_zb、Query控件Query1及其它指标属性编辑显示控件。一个树的节点又包含文本(Text)和数据(Data)。Text为String类,用来显示指标或指标目录名称。Data则为无定形指针(Untyped Pointer),可以指向一个与节点相联系的数据结构,该结构与数据库指标表相应域关联,如指标ID、上级节点ID。 Query控件的表达式为: select index_id, parent_id, index_name from t_pub_index start with index_id=0 connect by prior index_id=parent_id 其中start with 和connect by 是Oracle的SQL语句的保留字,使一条记录的parent_id列的值等于前一记录的index_id列的值,并以parent_id等于0的记录开始。 建树的基本思路是: procedure TFrm_sys_index.createtree; var curValue: indexPointer; //指向与节点相联系的数据结构的指针 curNode : TTreeNode; //当前节点 curid : integer; //当前节点标识号 begin curNode := nil; curid := -1; Query_index.Open; Query_index.first; while not Query_index.Eof do begin new(curValue); With curValue^ do 将数据库指标表t_pub_index各字段值赋curValue 所指数据结构 while(curid <> curValue.parent_id) do //当前节点的标识号不等于当前记录的父节点号 begin curNode := curNode.parent; curid:= indexPointer(curNode.data).index_id; end; curNode := tv_zb.Items.AddChildObject(curNode, curValue^.index_name,curValue); //在当前节点上添加子节点,显示节点指标名称,所带指针指向一个与指标数据相联系的数据结构 curid := indexPointer(curNode.data).index_id; Query_index.next; end; Query_index.close; end; 二、增加、删除、修改树节点 单纯在Treeview 上增加、删除、修改节点只需用它本身提供的Treeview.Items. AddChildObject、 Treeview.Selected.Delete、Treeview.Selected.EditText等方法即可,但要相应修改数据库中的数据,必须通过递归调用同一个函数(用于删除一个选项)来遍历所选节点下的所有子节点。下面以删除节点为例介绍具体实现流程: function TFrm_sys_index.delnode(node1:TTreenode):TTreenode; var childnode:TTreenode; begin childnode:=node1.GetLastChild; //按倒序获得子项,因为删除选项时,列表会发生变化 while childnode<>nil do childnode:=delnode(childnode); //如子项不为空,进行递归调用 index_id:=inttostr(indexpointer(node1.data).index_id);//获得该节点对应指标 在数据库删除相应指标; result:=node1.parent.GetPrevChild(node1); //定位到该节点的上一节点 node1.delete; //删除树节点 end; 三、拖动树节点 拖动树节点基本上是通过建立目标项的新子项、向它复制源项、删除原项来移动选项。与上述删除操作相似,也是通过递归调用同一个函数(用于移动一个选项),按倒序移动所选节点下的所有子节点。下面是递归过程的代码: procedure TFrm_sys_index.CopyNodeUnder(treeview:TTreeview; sourcenode,targetnode:ttreenode); var newnode:ttreenode; i:integer; begin newnode:=treeview.items.addchildfirst(targetnode,''); //建立目标项 newnode.assign(sourcenode); //复制源项属性 for i:=sourcenode.count-1 downto 0 do //递归调用,按倒序移动其所有子项 CopyNodeUnder (treeview,sourcenode.item[i],newnode); treeview.items.delete(sourcenode); //删除源项 end; Treeview对拖动操作提供支持,我们将组件的DragKind属性设置为dkDrag,DragMode属性设置为dmAutomatic,并为OnDragOver与OnDragDrop事件编写了处理程序。OnDragOver事件处理程序对允许移动的条件进行判断,排除需要避免的特殊情况。代码如下: procedure TFrm_sys_index.tv_zbDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var targetnode,sourcenode:TTreenode; begin targetnode:=tv_zb.getnodeat(x,y); if (Source=Sender) and (targetnode<>nil) then //保证移动在TreeView上,且目标节点不为空 begin Accept:=true; sourcenode:=tv_zb.selected; //以下代码防止用户将一个选项拖到其子项上(它会随着选项一起移动,导致死循环) while (targetnode.parent<>nil) and (targetnode <> sourcenode) do targetnode:=targetnode.parent; if (targetnode = sourcenode) then Accept:=false; end else Accept:=false; end; OnDragDrop事件处理程序启动前述移动过程CopyNodeUnder,修改数据库数据。此外,在大批量添加数据到Treeview中时最好使用TreeView.Items.BeginUpdate和 TreeView.Items.EndUpdate,这样能加快显示速度。大致流程如下: procedure TFrm_sys_index.tv_zbDragDrop(Sender, Source: TObject; X, Y: Integer); var targetnode,sourcenode:TTreenode; begin targetnode:=tv_zb.getnodeat(x,y); //获得源节点 sourcenode:=tv_zb.selected; //获得目标节点 修改数据库中当前节点的父节点号parent_id,使其等目标节点标识号; tv_zb.items.beginupdate; //禁用TreeView重绘操作 try copynodeunder(tv_zb,sourcenode,targetnode); //启动移动过程 tv_zb.selected:=targetnode; finally tv_zb.items.endupdate; //重新设置 end; end;
弄一个窗口,放上一个TreeView和一个Button,分别取名为TV1和Btn1。如果需要
在每个节点前有个图,请在窗口上放上一个ImageList,取名为ImageList1,双击
它,加入六个图标。还要记得记得将TV1的Images属性改为ImageList1噢。双击按
钮Btn1,在里面填入以下代码,然后按F9运行,点击Btn1就可以看到效果了。
procedure TForm1.Btn1Click(Sender: TObject);
Const
MyDocDir = 'C:\My Documents';
PersonDir = '3hSoft';
Var
Var
I : Word;
SubNodeName : array [1..5] of ShortString;
RootNode, SubNode : TTreeNode;
P : PString;
begin
SubNodeName[1] := '便笺';
SubNodeName[2] := '发件箱';
SubNodeName[3] := '联系人';
SubNodeName[4] := '任务';
SubNodeName[5] := '日记';
TV1.Items.Clear;
TV1.Items.BeginUpdate;
New(P);
P^ := MyDocDir + '\' + PersonDir;
RootNode := TV1.Items.AddObject(Nil, '个人文件夹', P);
// 此 Node 的图标已对 Images 属性中取第 0 个了。
For I := 1 to 5 do
begin
New(P);
P^ := MyDocDir + '\' + PersonDir + '\' + SubNodeName[I];
SubNode := TV1.Items.AddChildObject(RootNode, SubNodeName[I], P)
;
;
// 如果不想使用图标的话请删除以下两行
SubNode.ImageIndex := I;
SubNode.SelectedIndex := I;
end;
TV1.Items.EndUpdate;
end;
二.在TreeView中如何设置选中结点
var
i:integer; {i为设置的选中结点的索引值}
begin
if i>treeview1.items.count then
treeview1.items[i].selected:=true;
或
treeview1.selected:=treeview1.items[i];
三。设置TreeView结点的图形
1. 设置TreeView的images属性为已存在的images对象
treeview1.images:=imagelist1;
2. 在加入结点后执行:
var
anode:TTreeNode;
begein
anode:=Treeview1.add(nil,'item1');
anode.imageindex:=0; {结点未选中时显示的图标}
anode.selectedindex:=1; {结点选中时显示的图标}
end
3. 如果结点图形在改变后未发生变化,可以执行:
treeview1.refresh;
四。如何批量处理TreeView结点
使用TreeView的items属性的BeginUpdate和EndUpdate方法,例:
TreeView1.items.BeginUpdate;
for i:=0 to TreeView1.items.count-1 do
begin
//将每个结点的文字改成为小写字母
TreeView1.items[i].text:=lowercase(TreeView1.items[i].text);
end;
TreeView1.items.EndUpdate; 五。实现TreeView结点拖拽的实例
下面的程序片段演示了如何实现拖拽treeview构件结点的例子
{鼠标按下时执行的语句}
procedure TForm1.Treeview1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
{判断左键按下并且鼠标点在一个结点上开始实现拖拽}
if ( Button = mbLeft ) and
( htOnItem in Treeview1.GetHitTestInfoAt( X, Y ) ) then
begin
Treeview1.BeginDrag( False );
end;
end;
{鼠标拖动执行语句}
procedure TForm1.Treeview1DragOver( Sender, Source: TObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
var
Node : TTreeNode;
begin
if Source = Treeview1 then
begin
Node := Treeview1.GetNodeAt( X, Y ); {取当前结点}
if Node <> nil then {当前结点不为空才能实现拖拽,accept:=true}
Accept := true;
end;
end;
{鼠标释放时执行的语句}
procedure TForm1.Treeview1DragDrop( Sender, Source: TObject;
X, Y : Integer );
var
TempNode : TTreeNode;
AttachMode : TNodeAttachMode;
begin
if Treeview1.Selected = nil then
Exit;
AttachMode := naAddChild; {设置结点移动模式,设移动结点为子结点}
{ 注意在这里存在一个bug,当移动结点时,如果目标结点没有子结点,}
{ 则加入的新的子结点会失败,所以先在当前目标结点的下面 }
{ 加入一个临时子结点,移动完毕后,再将临时结点删除 }
Treeview1.Items.BeginUpdate;
try
TempNode := Treeview1.Items.AddChild( Treeview1.DropTarget,
'Temp' );
try
{ 移动选中的结点到目标结点 }
Treeview1.Selected.MoveTo( Treeview1.DropTarget, AttachMode );
finally
TempNode.Free; { 不要忘了释放临时结点 }
end;
finally
Treeview1.Items.EndUpdate;
end;
end;