⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 树视图同数据库的关联.txt

📁 大量Delphi开发资料
💻 TXT
字号:
>树视图同数据库的关联

前 言 

树形图用于显示按照树形结构进行组织的数据,其用途比较广泛,如计算机中的文件系统
(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; 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -