Delphi下Virtual-TreeView(树型控件)使用详解

1. 前言

在 Delphi 中,虽然提供了 TreeViewListView 控件,但它们的功能相对单一,往往无法满足一些特定的个性化需求。幸运的是,借助 Virtual Treeview 控件,我们可以轻松实现各种创新的功能。由于该控件的文档资源相对较少,学习曲线可能较为陡峭。因此,本篇文章将全面讲解 Virtual Treeview 控件的使用,希望能帮助大家快速掌握这个控件的使用,并在实际项目中发挥它的强大优势。

2. 效果演示

请添加图片描述请添加图片描述
请添加图片描述

3. 源码下载

Virtual Treeview控件源码:https://download.csdn.net/download/gust2013/90091385
全部示例Demo源码:https://download.csdn.net/download/gust2013/90155642

4. 控件安装

  • 把源代码中的Source目录添加到IDE的库路径中,32与64平台都添加
  • 双击打开VirtualTreesDevelopment.groupproj
  • 编译VirtualTreesRxxx.bpl
  • 编译、安装VirtualTreesDxxx.bpl

5. 类似TreeView的树形控件

5.1 定义数据结构

使用Virtual Treeview的第一步就是要定义数据结构。

type
  // 定义节点的数据结构,很重要
  PMyRec = ^TMyRec;

  TMyRec = record
    Caption: WideString;
  end;

5.2 为控件分配数据空间

procedure TForm2.FormCreate(Sender: TObject);
begin
  // 让树形控件知道我们需要多少数据空间。
  VST.NodeDataSize := SizeOf(TMyRec);
  // 初始化节点的条目
  VST.RootNodeCount := 10;
end;

5.3 初始化节点、释放节点

// 初始化节点,如指定复选框及状态
procedure TForm2.VSTInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  Data: PMyRec;
begin
  with Sender do
  begin
    Data         := GetNodeData(Node);
    Data.Caption := Format('等级 %d,索引 %d', [GetNodeLevel(Node), Node.Index]);
    Node.CheckType :=ctCheckBox;
  end;
end;

// 释放节点时要做的一些释放动作
procedure TForm2.VSTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Data: PMyRec;
begin
  Data := Sender.GetNodeData(Node);
  Finalize(Data^);
end;

5.4 节点显示文本

// 指定节点的显示文本
procedure TForm2.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: Integer; TextType: TVSTTextType; var CellText: string);
var
  Data: PMyRec;
begin
  Data := Sender.GetNodeData(Node);
  if Assigned(Data) then
    CellText := Data.Caption;
end;

6. 类似ListView的网格控件

6.1 定义数据结构

// 定义列表头的标题与宽度
const
  COLUMNSTRINGS: array [0 .. 3] of string = ('ID', '区服', '用户名', '密码');
  COLUMNWIDTHS: array [0 .. 3] of Integer = (50, 100, 120, 100);

  // 定义节点数据结构
type
  PAccount = ^TAccount;
  TAccount = packed record
    CheckState: string;
    RecordID: string;
    Server: string;
    UserName: string;
    Password: string;
  end;

6.2 创建列表头

procedure TForm2.FormShow(Sender: TObject);
begin
  VSTColumnsCreate;
end;

// 创建列表头,并显示数据
procedure TForm2.VSTColumnsCreate;
var
  i: Integer;
begin
  VST.BeginUpdate;
  with VST.Header do
  begin
    Height := 23;
    Columns.Clear;
    for i := 0 to high(COLUMNSTRINGS) do
    begin
      Columns.Add;
      Columns[i].Width := COLUMNWIDTHS[i];
      Columns[i].Text  := COLUMNSTRINGS[i];
    end;
  end;
  //对齐方式
  VST.Header.Columns[0].Alignment:= taLeftJustify;
  VST.EndUpdate;
end;

6.3 更改单元格数据

// 修改单元格后更新数据
procedure TForm2.VSTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: Integer;
  NewText: string);
var
  Data: PAccount;
begin
  //Modified := true;
  Data     := Sender.GetNodeData(Node);
  if Assigned(Data) then
  begin
    case Column of
      0:  Data.RecordID := NewText;
      1:  Data.Server := NewText;
      2:  Data.UserName := NewText;
      3:  Data.Password := NewText;
    end;
  end;
  if (Column = 0) and (NewText = '') then
  begin
    MessageDlg('A name is needed for the item.', mtError, [mbOK], 0);
  end;
end;

6.4 实现行复选框

procedure TForm2.CheckBox1Click(Sender: TObject);
begin
  if CheckBox1.Checked then
  begin
    VST.TreeOptions.MiscOptions:= VST.TreeOptions.MiscOptions + [toCheckSupport];
  end
  else
    VST.TreeOptions.MiscOptions:= VST.TreeOptions.MiscOptions - [toCheckSupport];
end;

6.5 实现拖拽功能

procedure TForm2.VSTDragAllowed(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: Integer;
  var Allowed: Boolean);
begin
  Allowed := True;
end;

procedure TForm2.VSTDragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState;
  State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
begin
  Accept := (Source = Sender);
end;

procedure TForm2.VSTDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject;
  Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
var
  pSource, pTarget: PVirtualNode;
  attMode: TVTNodeAttachMode;
begin
  pSource := TVirtualStringTree(Source).FocusedNode;
  pTarget := Sender.DropTargetNode;

  case Mode of
    dmNowhere: attMode := amNoWhere;
    dmAbove: attMode := amInsertBefore;
    dmOnNode, dmBelow: attMode := amInsertAfter;
  end;

  Sender.MoveTo(pSource, pTarget, attMode, False);
end;

6.6 保存与加载数据

procedure TForm2.VSTSaveToFile(VST: TVirtualStringTree; FileName: TFileName);
var
  sl         : TStringList;
  Node       : PVirtualNode;
  Data       : PAccount;
  FormatStr  : string;
  ctx        : TRttiContext;
  rttiType   : TRttiType;
  rttiField  : TRttiField;
  FieldValue : TValue;
begin
  if VST.TotalCount = 0 then Exit;
  sl := TStringList.Create;
  try
    Node := VST.GetFirst;
    while Assigned(Node) do
    begin
      Data := VST.GetNodeData(Node);
      // 获取 RTTI 类型信息
      ctx := TRttiContext.Create;
      try
        rttiType := ctx.GetType(TypeInfo(TAccount));
        // 构建一个格式化字符串
        FormatStr := '[';
        for rttiField in rttiType.GetFields do
        begin
          FieldValue := rttiField.GetValue(Data);
          FormatStr := FormatStr + FieldValue.ToString + ' | ';
        end;
        // 去掉最后一个多余的 " | "
        FormatStr := Copy(FormatStr, 1, Length(FormatStr) - 3);
        FormatStr := FormatStr + ' ]';
      finally
        ctx.Free;
      end;
      sl.Add(FormatStr);
      Node := VST.GetNext(Node);
    end;
    sl.SaveToFile(FileName);
  finally
    sl.Free;
  end;
end;

procedure TForm2.VSTLoadFromFile(VST: TVirtualStringTree; FileName: TFileName);
var
  sl            : TStringList;
  i             : Integer;
  Node          : PVirtualNode;
  Data          : PAccount;
  ParsedData    : TStringList;
  FieldCount    : Integer;
  ctx           : TRttiContext;
  rttiType      : TRttiType;
  rttiField     : TRttiField;
begin
  VST.Clear;
  sl := TStringList.Create;
  ParsedData := TStringList.Create;
  try
    sl.LoadFromFile(FileName);
    for i := 0 to sl.Count - 1 do
    begin
      Node := VST.AddChild(nil);
      Data := VST.GetNodeData(Node);
      VST.CheckType[Node] := ctCheckBox;
      // 解析每行数据
      ParsedData.Clear;
      ParsedData.Delimiter := '|';
      ParsedData.StrictDelimiter := True;
      ParsedData.DelimitedText := Trim(Copy(sl[i], 2, Length(sl[i]) - 2));
      // 获取 RTTI 类型信息
      ctx := TRttiContext.Create;
      try
        rttiType := ctx.GetType(TypeInfo(TAccount));
        // 遍历所有字段,并赋值
        FieldCount := ParsedData.Count;
        for rttiField in rttiType.GetFields do
        begin
          if FieldCount > 0 then
          begin
            // 根据字段名称赋值
            rttiField.SetValue(Data, Trim(ParsedData[0]));
            ParsedData.Delete(0);  // 移除已经使用的字段值
          end;
        end;
      finally
        ctx.Free;
      end;
    end;
  finally
    sl.Free;
    ParsedData.Free;
  end;
end;

6.7 列表头排序

//排序
VST.Header.Options := VST.Header.Options + [TVTHeaderOption.hoVisible, TVTHeaderOption.hoHeaderClickAutoSort];
//VST.TreeOptions.AutoOptions := VST.TreeOptions.AutoOptions + [TVTAutoOption.toAutoSort];

//By default sort descrending on date
VST.Header.SortDirection := sdAscending;
//VST.Header.SortColumn := 1;

procedure TForm2.VSTCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
Column: Integer; var Result: Integer);
var
NodeData1, NodeData2: PAccount;
Num1, Num2: Integer;
begin
// 获取节点数据
NodeData1 := Sender.GetNodeData(Node1);
NodeData2 := Sender.GetNodeData(Node2);
case Column of
  0:
    begin
      //Result := CompareText(NodeData1.RecordID, NodeData2.RecordID);
      Num1 := StrToIntDef(NodeData1.RecordID, MaxInt);
      Num2 := StrToIntDef(NodeData2.RecordID, MaxInt);
      // 进行数字比较
      Result := Num1 - Num2;
    end;
end;
end;

6.8 添加ico图标

VirtualTreeView的Images属性设置为ImageList1

procedure TForm2.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
  Column: Integer; var Ghosted: Boolean; var ImageIndex: TImageIndex);
var
  NodeRec: PAccount;
begin
  NodeRec := Sender.GetNodeData(Node);
  if Assigned(NodeRec) then
  begin
    if (Column = 0) then
    begin
      if Kind in [ikNormal, ikSelected] then
      begin
        {case NodeRec.Server of // check the needed status(es)
          1: ImageIndex := 1; // whichever image you need
          2: ImageIndex := 2; // whichever image you need
          // ...
        end; }
        if NodeRec.Server = '广东一区' then
          ImageIndex := 0
        else if NodeRec.Server = '广东二区' then
          ImageIndex := 1;
      end;
    end;
  end;
end;

6.9 过滤显示

procedure TForm2.Button2Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: PAccount;
begin
  // 遍历所有节点并应用过滤
  Node := VST.GetFirst;
  while Node <> nil do
  begin
    Data := VST.GetNodeData(Node);
    if Data.Server = '广东二区' then
      VST.IsVisible[Node] := False  // 显示节点
    else
      VST.IsVisible[Node] := True;  // 隐藏节点
    Node := VST.GetNext(Node);
  end;
end;

procedure TForm2.Button4Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: PAccount;
begin
  // 遍历所有节点并应用过滤
  Node := VST.GetFirst;
  while Node <> nil do
  begin
    VST.IsVisible[Node] := True;
    Node := VST.GetNext(Node);
  end;
end;

7. 控件美化

7.1 添加背景图

procedure TForm2.Button1Click(Sender: TObject);
begin
  with OPD do
  begin
    if Execute then
    begin
      VST.Background.LoadFromFile(FileName);
      if CheckBox1.Checked then
        VST.Invalidate
      else
        CheckBox1.Checked := True;
    end;
  end;
end;

procedure TForm2.CheckBox1Click(Sender: TObject);
begin
  if CheckBox1.Checked then
  begin
    VST.TreeOptions.MiscOptions:= VST.TreeOptions.MiscOptions + [toCheckSupport];
  end
  else
    VST.TreeOptions.MiscOptions:= VST.TreeOptions.MiscOptions - [toCheckSupport];
end;

7.2 每个列表行不同的颜色

procedure TForm2.VSTBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas;
  Node: PVirtualNode; Column: Integer; CellPaintMode: TVTCellPaintMode; CellRect: TRect;
  var ContentRect: TRect);
begin
  if Odd(Node.Index) then
  begin
    TargetCanvas.Brush.Color := $EEEEFF;
    TargetCanvas.FillRect(CellRect);
  end
  else
  begin
    TargetCanvas.Brush.Color := $FFEEEE;
    TargetCanvas.FillRect(CellRect);
  end;
end;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NolanDing

请我喝杯咖啡吧,鼓励一下创作!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值