|
关键字:开源,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两个控件,均为开源软件。 |
评分
-
查看全部评分
|