Virtual Treeview是一套Delphi下优秀的VCL控件,代码质量高,使用灵活、功能强大、性能很是好,能够用于表达Treeview和表格类数据。它的代码如今托管在google code上。css
这套控件使用了好久了,很是满意其表现,以前一直使用V4版,如今V5正式发布了,新版花了几年时间进行重构, 代码结构更加合理,去除了老版大量的小缺陷,不少功能进行了从新设计,参与维护的人也更多了。性能
Virtual Treeview的设计思路与官方的Treeview彻底不同,VT是基于高性能和更丰富的表现而开发的,在上手方面比Treeview要慢一些,但一旦上手,就会发现很是好用。ui
在使用VT以前,就应该设计好以什么方式展现数据,每一个节点应该有些什么属性。在构思好后,就能够按如下步骤来实现VT的使用了。google
在实际使用Virtual Treeview以前,应该先设计好一个节点数据的结构体,用于方便每一个节点的展现。spa
好比一个典型的设计以下:设计
PTreeData = ^TTreeData; TTreeData = record Level: integer; // 节点级别:0根;1节点;2参数 NodeType: integer; // 节点类型 Id: Integer; // 数据id Caption: String; // 节点标题 obj : TObject; // 节点对应的对象或空 end;
其中Caption用于控制节点显示,Id用于方便节点对应数据的快速定位,obj用于把节点与相关的对象关联在一块儿,其它数据也是用于方便进行数据处理的。指针
通常状况下,若是是只当成普通Treeview,无需考虑列(Column)的处理,直接使用便可。若是一行有多列数据须要显示,则得先在属性Header-Columns中增长所需的列,并设置合适的列宽。缺省列(Column)设置为空,列索引(TColumnIndex)应该为-1,在设置了列后,第一列的列索引为0,第二列的列索引为1。code
在树中的全部节点中,只有根节点的父节点为空(nil)。建立根节点的示例代码以下:orm
var Data : PTreeData; Node: PVirtualNode; RootNode: PVirtualNode; vst.Clear; // 清除全部节点 vst.NodeDataSize := Sizeof(TTreeData); // 设置节点数据大小 RootNode := vst.AddChild(nil); // 增长一个根节点 vst.ValidateNode(RootNode, false); Data := vst.GetNodeData(RootNode); // 获取节点数据 Data.Caption := '根节点1'; // 设置将要显示在节点的文本信息
建立彻底根节点后,就能够在根节点后增长各级子节点了。对象
Node := vst.AddChild(RootNode); // 增长一级子节点 data := vst.GetNodeData(Node); // 获取节点数据 data.Level := vst.GetNodeLevel(Node); // 设置节点级别,方便后继处理 Node := vst.AddChild(Node); // 增长二级子节点 data := vst.GetNodeData(Node); data.Level := vst.GetNodeLevel(Node); Node := vst.AddChild(RootNode); // 再增长一个一级子节点 data := vst.GetNodeData(Node); data.Level := vst.GetNodeLevel(Node); // 固然这里能够批量产生多个子节点,好比 // for i:=0 to 99 do // begin Node := vst.addchild(rootNode); ... end; vst.FullExpand(RootNode); // 把根节点下的全部子节点打开
上面只是简单示例,完整示例中须要把节点数据都设置完整。
这里还有一种方式进行更高速的建立子节点,好比:
vst.ChildCount[RootNode] := 100;
这样,就能够一下给根节点设置100个一级子节点。固然,这些节点的数据都仍是缺省状态,咱们能够在适当的时候和适当的位置再进行设置。
若是咱们须要一次插入多个节点,为了提升显示效率,咱们应该在插入前调用BeginUpdate,在插入完成后再调用EndUpdate。
运行后,能够发现树的确出来了,但全部节点显示的都是彷佛"Node"字样的东西,怎么样把咱们须要显示的内容显示出来?
咱们须要设置Virtual Treeview的GetText事件,VT在显示节点信息时会调用GetText来获取要显示的内容。咱们能够增长相似下面的代码:
procedure TfrmMain.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var Data : PTreeData; begin CellText := ''; Data := vst.GetNodeData(Node); if not Assigned(Data) then exit; Case Data.Level of 0: begin CellText := Data.Caption; end; 1: begin CellText := format('一级子节点',[]); end; 2: begin CellText := format('二级子节点',[]); end; end; end;
固然,实际代码要复杂的多。并且这个代码并无处理多列的状况,若是有多列就得针对各列再加一级case语句就能够了。也就是说相似这样的代码,假设咱们设置了一个节点有三列:
procedure TfrmMain.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var Data : PTreeData; begin CellText := ''; Data := vst.GetNodeData(Node); if not Assigned(Data) then exit; Case Column of 0: // 第一列的处理 begin Case Data.Level of 0: begin CellText := Data.Caption; end; 1: begin CellText := format('一级子节点',[]); end; 2: begin CellText := format('二级子节点',[]); end; end; end; 1: // 第二列的处理 begin // 通常是取Data中obj对象,再取其中的数据用于处理显示 end; 2: // 第三列的处理 begin // 通常是取Data中obj对象,再取其中的数据用于处理显示 end; end; end;
也就是说vst各节点的内容,其实都是经过代码来控制并设置的。不管是Treeview形式仍是表格形式或二者综合体,数据的显示都是这么处理的。
常常咱们须要在每一个节点前加个小图标,可用于表达这个节点的状态、类型或纯美观。Virtual Treeview显示图标同样也是要与TImageList配合使用的。咱们须要先在一个ImageList中加载上合适的全部图标,而后在GetImageIndex事件中进行判断处理每一个节点应该使用的图标的索引来加载ImageList中的图标。
好比:
procedure TfrmMain.vstGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); var Data : PTreeData; begin Data := vst.GetNodeData(Node); if not Assigned(Data) then exit; Case Data.Level of 0: begin ImageIndex := 2; end; 1: begin ImageIndex := 0; end; 2: begin ImageIndex := 0; end; end; end;
在Virutal Treeview组件释放时,咱们应该同时清理掉每一个节点对应的数据空间。咱们应该在FreeNode事件中写上如下相似的代码来完成这个工做:
procedure TfrmMain.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); var Data : PTreeData; begin Data := vst.GetNodeData(Node); if Assigned(Data) then begin Data.Caption := ''; // String must be cleaned manual, otherwise there is memory leak // 节点数据记录中对应的数据对象,不必定得在这释放,能够在其它地方统一处理 //FreeAndNil(Data.obj);
end; end;
节点数据空间不须要咱们释放,在前面对vst初始化时写过这么一句代码:
vst.NodeDataSize := Sizeof(TTreeData); // 设置节点数据大小
这里就是初始化节点数据记录空间的,这个空间会被vst自动释放。但这个记录体中的相似字符串或指针所指向的内存空间或对像是不会被自动释放的。因此,这里咱们把Data.Caption字符串清空,以免内存泄漏。