Lazarus中文社区

 找回密码
 立即注册(注册审核可向QQ群索取)

QQ登录

只需一步,快速开始

Lazarus IDE and 组件 下载地址版权申明
查看: 10071|回复: 7

Virtual TreeView控件如何显示数据库的树型结构

[复制链接]

该用户从未签到

发表于 2010-8-22 19:18:56 | 显示全部楼层 |阅读模式

    关键字:开源,Virtual TreeView,树型结构,数据库,Lazarus

    Virtual TreeView是开放源代码的Delphi控件,拥有漂亮的树型界面,功能强大,支持图形、树列表、拖放、外部数据输入控件嵌入等功能,支持Delphi、BCB及Lazarus(Lazarus是基于FreePascal的仿Delphi的可视化开源跨平台开发项目,目前尚未达产品阶段)。
    通过查阅Virtual TreeView手册及网上资料,都只有针对数据库的平行结构显示资料或示例,而没有如何使用Virtual TreeView控件显示数据库的树型结构的说明,经笔者仔细研究,反复测试,完成了Virtual TreeView对数据库的树型结构的显示问题,特此介绍如下。
一、Virtual TreeView介绍
    Virtual TreeView控件使用了有别于其它同类控件的实现方法,控件仅了解节点显示数据的结构尺寸,而不管理节点标题等实际数据,所有的数据都通过事件获取(或通过继承重构)。代码经过仔细推敲,全面测试,已大量应用于商业或免费软件中。主要特征如下:
· 速度极快,增加100万个节点大约只需700毫秒,是Delphi/BCD下最快的树型显示控件。
· 内存消耗极低,每节点约60字节,适于大量数据的显示。
· 为高速存取而优化,不到0.5秒的时间即可遍历100万个节点。
· 支持多选,包括只能选择某一层节点的限制性选择。
· 通过 PaintTree方法可将树视图输出为位图或输出到打印机。
· 通过 OnHint事件显示节点的提示信息。
· 通过 OnGetHelpContext事件取到节点的上下文帮助信息。
· 通过 OnGetPopupMenu事件显示节点的弹出菜单。
· 鼠标中键及右键可替代左键完成同样的功能(如拖拽、选择等)。
· 可在树中显示一副背景图案。
· 支持节点的形如网页中链接形式的Hot style并可设置特定的光标。
· 支持在节点的标题(caption)后显示一个只读的不同格式的静态文本(static text)
· 支持列宽的自动扩展模式(auto span column)。
· 可选择节点的各列进行编辑操作,如同操作表格一样,并使用制表键在单元格间移动,toGridExtensions属性就是用于支持表格特性。
· 各节点可独立使用高度、图象的垂直排列方式、线型等属性。
· 通过输出内部的拖拽中、编辑中、多选、扩展中等状态事件使应用程序易于接口。
· 支持应用程序通过定义比较回调函数(compare call back)对节点进行排序,也可使用内定的自动排序。
· 提示信息可包含多行文本。
· 拖拽或多选时,当鼠标位于边框附近,可自动翻卷视图。
· 字符型树可设置节点的默认高度及默认文字,以避免重复设置各相同节点值。

二、对数据库树型结构的显示
    1、假定有数据库表test,其字段为:
    id: string;       
    pid: string;
    name: string;
    其中pid标识该节点的父节点。表记录如下:
id
pid
name
1-1


一层节点1
1-2


一层节点2
1-3


一层节点3
2-1
1-1
二层节点1
2-2
1-1
二层节点2
2-3
1-1
二层节点3
2-4
1-2
二层节点4
2-5
1-3
二层节点5
2-6
1-3
二层节点6
3-1
2-2
三层节点1
3-2
2-2
三层节点2
3-3
2-6
三层节点3
    2、在单元文件中定义如下数据类型:
PCustRec = ^TCustRec;
TCustRec = record
id: WideString;
pid: WideString;
name: WideString;
end;
3、在窗体Form1中增加vstTest(TVirtualStringTree)、 zcConn(TZConnection)、 zqTest(TZQuery)控件,并设置有关属性。为窗体增加onCreate事件,为vstTest控件增加onInitNode、onInitChildren、onGetText事件,具体代码如下:

procedure TForm1.FormCreate(Sender: TObject);
begin
//连接数据库
zcConn.Connected:= True;

//运行SQL查询
with zqTest do begin
Close;
SQL.Clear;
SQL.Add('select * from "TEST"');
try
Active:= True;
except
on E:Exception do begin
MessageDlg(E.Message, mtError, [mbOk], 0);
end;
end;

if Active Then begin
//设置过滤条件,剔出根节点
zqTest.Filter:='id=pid or pid is null';
zqTest.Filtered:= true;
vstTest.BeginUpdate;
vstTest.Clear;

vstTest.NodeDataSize:= Sizeof(TCustRec);
First;
vstTest.RootNodeCount:= RecordCount;
vstTest.SortTree(0,sdAscending,true);
vstTest.EndUpdate;
end;
end;
end;

procedure TForm1.vstTestInitNode(Sender: TBaseVirtualTree; ParentNode,
Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
CustomerRecord : PcustRec;
begin
//初始化节点数据结构
CustomerRecord := Sender.GetNodeData(Node);
Initialize(CustomerRecord^);

CustomerRecord^.id := zqTest.FieldByName('id').AsString;
CustomerRecord^.pid := zqTest.FieldByName('pid').AsString;
CustomerRecord^.name := zqTest.FieldByName('name').AsString;

//该节点拥有子节点
Include(InitialStates, ivsHasChildren);

if not zqTest.EOF then zqTest.Next;
end;

procedure TForm1.vstTestInitChildren(Sender: TBaseVirtualTree;
Node: PVirtualNode; var ChildCount: Cardinal);
var
CustomerRecord : PcustRec;
begin
CustomerRecord := Sender.GetNodeData(Node);
//设置过滤条件,剔出该节点的子节点
zqTest.Filter:='pid='''+CustomerRecord^.id+'''';
zqTest.Filtered:= true;
ChildCount:=zqTest.RecordCount;
end;

procedure TForm1.vstTestGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
CustomerRecord : PcustRec;
begin
CustomerRecord := Sender.GetNodeData(Node);
case Column of
0:CellText := CustomerRecord^.id;
1:CellText := CustomerRecord^.name;
else CellText := '';
end;
end;

编译运行即可显示如下:
  

三、机理分析
Virtual TreeView构造树的过程是一个递归循环过程。在我们的示例中,当启动onCreate事件时,首先通过SQL查询准备好数据,并设置过滤条件,剔出根节点记录,然后设置 NodeDataSize、RootNodeCount属性,以通知Virtual TreeView控件外部数据的尺寸及根节点的数量;之后程序进入onInitNode事件,该事件根据前一步得到的RootNodeCount进行循环,分配数据空间,指向实际数据,并通过“Include(InitialStates, ivsHasChildren);”语句,通知节点拥有子节点;由于告知存在子节点,于是转入onInitChildren事件,通过设置过滤条件,找到当前节点的子节点;若不存在子节点,则当前节点的ChildCount为零,与该节点相关的生成树的过程结束;若存在子节点,则设置当前节点的ChildCount后,程序又转入onInitChildren事件中继续完成子节点的插入。如此循环,存在子节点的节点不断产生新的调用,递归到叶节点后调用结束,最后生成一颗完整的树结构。

四、更进一步
通过使用如下的SQL查询语句得到每个节点的子节点数量:
select test.*, tmp.cc from TEST
LEFT JOIN
(select pid, count(pid) as cc from test group by pid) as tmp
ON (TEST.id=tmp.pid)
查询结果如下:
ID
PID
NAME
CC
1-1


一层节点1
3
1-2


一层节点2
1
1-3


一层节点3
2
2-1
1-1
二层节点1
0
2-2
1-1
二层节点2
2
2-3
1-1
二层节点3
0
2-4
1-2
二层节点4
0
2-5
1-3
二层节点5
0
2-6
1-3
二层节点6
1
3-1
2-2
三层节点1
0
3-2
2-2
三层节点2
0
3-3
2-6
三层节点3
0

在onInitNode事件中增加如下条件判断:
if zqTest.FieldByName('cc').AsInteger>0 then
Include(InitialStates, ivsHasChildren);

    可以减少循环次数,在数据量大时,可以提高树构造的速度。

    结语Virtual TreeView控件功能强大,接口丰富,可生成漂亮的界面外观。上述程序在Lazarus 0.9.22、FireBird 2.0.1嵌入式环境中调试通过,使用了virtualtreeview-4.0.17.25及ZEOSDBO-6.6.1-beta两个控件,均为开源软件。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册(注册审核可向QQ群索取)

x

评分

参与人数 1威望 +10 收起 理由
猫工 + 10 优秀文章

查看全部评分

回复

使用道具 举报

该用户从未签到

发表于 2010-8-22 20:07:13 | 显示全部楼层
好东西,学习中
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2010-8-24 19:13:08 | 显示全部楼层
下来测试下 。。。。...
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2010-8-24 19:14:46 | 显示全部楼层
不错。。。。
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2012-9-13 10:11:05 | 显示全部楼层
能提供一个完整示例的工程源码吗
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2012-10-5 16:31:59 | 显示全部楼层
学习中
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2012-10-23 00:01:45 | 显示全部楼层
这个写得不错。
回复 支持 反对

使用道具 举报

该用户从未签到

发表于 2012-10-29 21:00:33 | 显示全部楼层
努力学习研究中。
回复 支持 反对

使用道具 举报

*滑块验证:

本版积分规则

QQ|手机版|小黑屋|Lazarus中国|Lazarus中文社区 ( 鄂ICP备16006501号-1 )

GMT+8, 2025-5-2 20:40 , Processed in 0.038344 second(s), 14 queries , Redis On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表