VirtualStringTree 的用法详解:
作者:张志宝go淘宝

例子:创建了一个树名为VirtualStringTree1,第一列显示数据库字段ID的值,标题为编号;第二列显示字段Name的值,标题为名字。
一、【TVirtualStringTree常用属性】
    BorderStyle   :设置边框选项  bsSingle设置单边框
   为TVirtualStringTree添加列及列标题:   
    Header--Columns : 设置列  点击“…”,在弹出界面点击add new按钮,就增加一列,在其text中输入列名,在width输入列宽度;
    Header--options-hovisible: 设置列可显示!!

Header-Style  :设置树的主题类型
    ScrollBaroptions--AlwaysVisible 滚动条是否总是可见
    ScrollBarOptions-ScrollBars-ssVertical  设置为只有竖向滚动条
   为TVirtualStringTree添加勾选框、选择框、复选框:
     TreeOptions----MiscOpitions---toCheckSupport  设置为True即可
     CheckImageKind  为勾选框设置图标

Header-Maincolumn  设置勾选框在第几列前面,默认为0也就是在第一列前面

并设置ongettext()事件和onInitailNode()事件,见下面事件部分。

--------------------------------------------------------------------------------------------------------------------------------------------

二、 【TVirtualStringTree常用方法 】 
    VirtualStringTree1.NodeDataSize := sizeOf(TMyNodeData);  //设置占存储空间大小

树节点选中:VirtualStringTree1.Selected[p_node]True;  //设置节点p_node处于选中状态

树节点勾选框选中:p_node.CheckState:=cscheckedNormal;//设置节点p_node处于勾选状态

onchecked()事件:设置节点勾选时触发的事件,其中第二个参数就是勾选的节点

取消选中的节点:VirtualStringTree1.ClearSelection;

树节点删除:  VirtualStringTree1.DeleteSelectedNodes; //删除选中的节点

树节点内容清空:VirtualStringTree1.clear;

得到树的第一个节点:VirtualStringTree1.GetFirst;

得到树的第一个选中节点:VirtualStringTree1.GetFirstSelected;

得到节点的下一个节点:VirtualStringTree1.Getnext(p_node);

得到下一个选中的节点:VirtualStringTree1.GetNextSelected(p_node);

得到父节点:p_node.Parent

得到子节点个数:p_node.ChildCount

得到第一个子节点:p_node.FirstChild

得到最后一个子节点:p_node.FirstChild

得到上一个兄弟节点:p_node.PrevSibling

得到下一个兄弟节点:p_node.NextSibling

---------------------------------------------------------------------------------------------------------------------------

三、【TVirtualStringTree显示数据库字段内容】:

//定义一个指针

type

Tjdtransline =record

id: integer;     //编号

name : string;  //名称

levelID : integer;//级别,例如总公司levelID 为0、二级公司为1、三级公司levelID 为2等

FatherID:integer;  //节点的父节点ID

end;

pjdtransline =^Tjdtransline ;

private

procedure iniVirtualStringTree1;   //事件声明为全局私有事件

//树显示数据库字段内容

procedure form1.iniVirtualStringTree1;

var

p_jdtransline: pjdtransline;

begin

while not adoquery1.Eof do
  begin
    New(p_jdtransline);
    p_jdtransline^.ID := client_dataset_jdtransline.FieldByName('ID').AsInteger;
    p_jdtransline^.Name := Trim(client_dataset_jdtransline.FieldByName('Name').AsString);
    VirtualStringTree1.AddChild(nil, p_jdtransline);  //*********************添加树节点***********************************
    adoquery1.Next;
  end;

end;

//在窗体创建时调用树的初始化事件

procedure form1.formcreate(sender:Tobject);

begin

iniVirtualStringTree1;

end;

//设置各列字段的显示OnGetText() var
  p_node_data       : PMyNodeData;
  p_jdtransline     : PJDTranslineTree;
begin
  p_node_data := VirtualStringTree1.GetNodeData(Node);
  p_jdtransline := p_node_data^.my_data_ptr;

case Column of
       0 : CellText :=  inttostr( p_jdtransline^.ID);

1: CellText := p_jdtransline^.Name;

end;

end;

------------------------------------------------------------------------------------------------------------------------------------

四、 【TVirtualStringTree设置节点展开与折叠的图标 】 
  OnInitNode()  //设置结点的节点是否有子节点类型(‘+’、‘—’显示)

begin
  Include(InitialStates, ivsHasChildren);
  Node.CheckType := ctTriStateCheckBox;

end;

-------------------------------------------------VirtualStringTree树拖动操作实现开始-------------------------------------------------------------------------

五、【VirtualStringTree树节点拖动实现】

主要设置其DragOver和DragDrop事件。其中DragOver控制是否允许移动;DragDrop控制移动后执行的操作。

//一、DragOver事件
procedure form1.VirtualStringTree1Over(
  Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState;
  State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer;
  var Accept: Boolean);

//事件内部函数1.获取节点的值

function GetVNodeData(vn: PVirtualNode; vst: TVirtualStringTree): PVNodeData;
  begin
    Result := PVNodeData(PPointer(vst.GetNodeData(vn))^);
  end;

//事件内部函数2.获取所有选中节点的列表
  function GetSelectedVNodes(vst: TVirtualStringTree): TVNodeArray;
  var
    list: TList;
    n: PVirtualNode;
    i: Integer;
  begin
    list := TList.Create;
    n := vst.GetFirstSelected;
    while n <> nil do
    begin
      list.Add(n);
      n := vst.GetNextSelected(n);
    end;

SetLength(Result, list.Count);
    for i := 0 to list.Count-1 do
    begin
      Result[i] := list[i];
    end;

list.Free;
  end;

// //事件内部函数3.判断是否是移动到自己的父节点

function IsSameLevel(selectedList: TVNodeArray): Boolean;
  var
    n: PVirtualNode;
    l: Cardinal;
    i: Integer;
  begin
    Result := False;
    if Length(selectedList) = 0 then
    begin
     Result := True;
     Exit;
    end;
    for i := 0 to High(selectedList) do
    begin
      n := selectedList[i];
      if  n.Parent = Sender.DropTargetNode then //移动到自己父节点不允许移动
      begin    
        HintBar.Panels[0].Text := '不允许移动到自己的父节点';
        Result := True;
        Break;
      end;

end;
  end;

// //事件内部函数4.判断目标(父、子)节点中是否与选中节点名称相同
  function IsExistSelectObject(selectedList: TVNodeArray): Boolean;
  var
    dstNode             : PVirtualNode;
    n                   : PVirtualNode;
    i                   : Integer;
    TargetChildNode     : PVirtualNode;
    TargetParentNode    : PVirtualNode;
    SelectNodeName      : String;
    TargetNodeName      : String;
    TargetChildNodeName : String;
    TargetParentNodeName: String;
    SelectNodeLevel     : Integer;
    TargetNodeLevel     : Integer;
  begin
    Result := False;
    if Length(selectedList) = 0 then
    begin
     Exit;
    end;
    dstNode          := Sender.DropTargetNode;
    TargetChildNode  := dstNode.FirstChild;
    TargetParentNode := dstNode.Parent;   
    TargetNodeName   := pjdtransline(VtPurview.GetNodeData(dstNode)^).name;
    TargetNodeLevel  := pjdtransline(VtPurview.GetNodeData(dstNode)^).LevelID;

for i := 0 to High(selectedList) do
    begin
      n := selectedList[i];
      SelectNodeName   := pjdtransline(VtPurview.GetNodeData(n)^).name;
      SelectNodeLevel  := pjdtransline(VtPurview.GetNodeData(n)^).LevelID;

if (TargetNodeLevel > SelectNodeLevel) then  //目标节点级别不能比选中节点级别低
      begin
        HintBar.Panels[0].Text := '目标节点级别比选中节点级别低,不能移动!';
        Result := True;
        Break;
      end;

if (TargetNodeName = SelectNodeName) then  //目标节点与选中节点相同
      begin
        HintBar.Panels[0].Text := '目标节点值与选中节点值相同,不能移动!';
        Result := True;
        Break;
      end;

while  TargetChildNode <> nil do
      begin
       TargetChildNodeName := pjdtransline(VtPurview.GetNodeData(TargetChildNode)^).name;
        if TargetChildNodeName = SelectNodeName then  //目标子节点与选中节点相同
        begin
          HintBar.Panels[0].Text := '目标子节点存在与选中节点值相同的节点,不能移动!';
          Result := True;
          Break;
        end;
        TargetChildNode := TargetChildNode.NextSibling;
      end;
    end;
  end;

// //事件内部函数6.判断是否移动自己子节点或子子节点
  function IsAncestor(vnDescendant, vnAncestor: PVirtualNode): Boolean;
  var
    p: PVirtualNode;
  begin
    Result := False;
    p := vnDescendant.Parent;
    while p <> nil do
    begin
      if p = vnAncestor then
      begin
        HintBar.Panels[0].Text := '不能移动到自己的子节点或子子节点';
        Result := True;
        Exit;
      end;
      p := p.Parent;
    end;
  end;

//事件定义变量
var

Node : TTreeNode;
  VirtualStringTree1: TVirtualStringTree;
  dstNode, srcNode: PVirtualNode;
  srcText, dstText, s: string;
  selectedList: TVNodeArray;
  i: Integer;
begin    
  Accept := False;
  if not (Source is TVirtualStringTree) then Exit;

VirtualStringTree1 := TVirtualStringTree(Source);
  dstNode := Sender.DropTargetNode;
  srcNode := VirtualStringTree1.FocusedNode;
  if (srcNode = nil) or (dstNode = nil) then Exit;
  if srcNode = dstNode then
  begin    
    HintBar.Panels[0].Text := '目标节点必须与选中节点不同!';
    Exit;
  end;
  selectedList := GetSelectedVNodes(VirtualStringTree1);
  if IsSameLevel(selectedList) then
  begin
    Accept := False;
    Exit;
  end;

if IsExistSelectObject(selectedList) then
  begin
    Accept := False;
    Exit;
  end;

//  dstText := GetVNodeData(dstNode, VirtualStringTree1).Name;
  case Mode of
    dmAbove:  // 成为父节点;
      begin
        Accept := False;
      end;
    dmBelow:  // 成为子节点,本例主要采用成为子节点模式
      begin
        for i := 0 to High(selectedList) do
        begin
          srcNode := selectedList[i];
          Accept := not IsAncestor(dstNode, srcNode);
          if not Accept then Break;
        end;
      end;
    dmOnNode: // 成为同级节点
      begin
        for i := 0 to High(selectedList) do
        begin
          srcNode := selectedList[i];
          Accept := not IsAncestor(dstNode, srcNode);
          if  Accept then
            HintBar.Panels[0].Text := '可以移动'
          else
            Break;
        end;

end;
    dmNowhere:
      Accept := False;
  else
  end;
end;

二、DragDrop事件

procedure Tform1.VirtualStringTree1DragDrop(
  Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject;
  Formats: TFormatArray; Shift: TShiftState; Pt: TPoint;
  var Effect: Integer; Mode: TDropMode);
//事件内部函数--获取选中节点的list
  function GetSelectedVNodes(VtPurview: TVirtualStringTree): TVNodeArray;
  var
    list: TList;
    n: PVirtualNode;
    i: Integer;
  begin
    list := TList.Create;
    n := VtPurview.GetFirstSelected;
    while n <> nil do
    begin
      list.Add(n);
      n := VtPurview.GetNextSelected(n);
    end;

SetLength(Result, list.Count);
    for i := 0 to list.Count-1 do
    begin
      Result[i] := list[i];
    end;

list.Free;
  end;
var
  VirtualStringTree1: TVirtualStringTree;
  dstNode, srcNode: PVirtualNode;
  selectedList: TVNodeArray;
  i: Integer;
  TargetNodeID : integer;
  SelectNodeID : integer;
  SelectNodeFID: integer; 
  sql_str      : string;
begin
  VirtualStringTree1 := TVirtualStringTree(Source);
  dstNode := Sender.DropTargetNode;
  TargetNodeID := PPurview(VtPurview.GetNodeData(dstNode)^).ID;
  selectedList := GetSelectedVNodes(VirtualStringTree1);
  srcNode := VirtualStringTree1.FocusedNode;
  if (srcNode = nil) or (dstNode = nil) then Exit;

case Mode of
    dmAbove:  // 成为父节点;
      begin
      end;
    dmBelow:  // 成为子节点
      begin   
       if IDYES = MessageBox(Handle, PChar('是否将操作保存到数据库?'), '确认保存',MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) then
       begin
        for i := 0 to High(selectedList) do
        begin
          srcNode := selectedList[i];
          VirtualStringTree1.MoveTo(srcNode, dstNode, amAddChildFirst, False);

SelectNodeID := PPurview(VtPurview.GetNodeData(srcNode)^).ID;
          SelectNodeFID := PPurview(VtPurview.GetNodeData(srcNode)^).FatherID;
          //执行数据库操作    
           if  sql_str = '' then
            sql_str := Format('exec p_VehicleGroupTreeDragMove %d,%d,%d ',
                               [SelectNodeID,
                                SelectNodeFID,
                                TargetNodeID
                               ])
           else
            sql_str := sql_str + Format(';exec p_VehicleGroupTreeDragMove %d,%d,%d ',
                               [SelectNodeID,
                                SelectNodeFID,
                                TargetNodeID
                               ]);
        end;
        VirtualStringTree1.Sort(dstNode, 0, sdAscending, False);
       end
       else
        exit;
      end;
    dmOnNode: // 成为同级节点
      begin
      end;
    dmNowhere:  
      begin
      end;
    else
  end;

if sql_str<>'' then
  begin
    ExcuteSQL(sql_str);  //保存拖动后节点的上下级关系
  end;
end;

-------------------------------------------------VirtualStringTree树拖动操作实现结束-------------------------------------------------------------------------

六、实现VirtualStringTree(TVirtualStringTree)树节点字体颜色分多颜色显示 oncellpaint()事件

客户要求盲区或掉线的字体要显示红色,速度值要显示灰色

代码如下:

procedure TfrmMain.vtActiveVehiclePaintText(Sender: TBaseVirtualTree;
  const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType);

begin

p_node_data := TGPSClientVTVUtils.ExtractActiveVehicleNodeData(vtActiveVehicle, Node);  //获取节点data
  if not Assigned(p_node_data) then
    Exit;
  vInfo    := PGPSClientVehicleInfoColumn(vtActiveVehicle.Header.Columns[Column].Data);  
  if vInfo <> nil then
  begin
   if (Pos('盲区',p_node_data^.CarInfo.GpsInfo.GpsState)>0) and   //判断节点值满足某条件
         (vInfo^.FieldInfo.FieldName = 'GPS_Speed') then                //判断是哪个列,如果是整个节点都变颜色可不需此条件
      TargetCanvas.Font.Color := clMedGray;                               //速度列变灰色
   if (Pos('盲区',p_node_data^.CarInfo.GpsInfo.GpsState)>0) and
         (vInfo^.FieldInfo.FieldName = 'GPS_Precision') then
      TargetCanvas.Font.Color := clred;                                    //精度列变红色

if (SecondsBetween(Now(), p_node_data^.CarInfo.GpsInfo.GPSTime)>=7200) and      //盲区条件

(vInfo^.FieldInfo.FieldName = 'GPS_Speed') then            //速度列

TargetCanvas.Font.Color := clMedGray;    
   if (SecondsBetween(Now(), p_node_data^.CarInfo.GpsInfo.GPSTime)>=7200) and
         (vInfo^.FieldInfo.FieldName = 'GPS_RealStatus') then
      TargetCanvas.Font.Color := clred;
  end;

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

终于总结完了,希望对大家有所帮助,欢迎提出宝贵意见,以便改进!

delphi中VirtualStringTree树使用方法之终结篇!的更多相关文章

  1. Delphi中Frame的使用方法(2)

    Frame在写代码时和一般组件有什么不同呢?比如(1)中的客户信息的frame,如果想重写客户编辑按钮的click事件,会发生什么呢: procedure TBusOnSiteManager.Fram ...

  2. Delphi 中的MD5实现方法(转)

    在Delphi自带的Indy控件中其实是提供了MD2,MD4,MD5对象的,我们可以直接使用它们来完成MD5的签名算法.而不需要再去找其它的DLL或是Pas了. 在Uses单元中引用 IdHashMe ...

  3. delphi中TTreeView的使用方法

    [学习万一老师博客摘要] TTreeView 与两个重要的类相关:TTreeNodes.TTreeNode . TTreeNodes即是TTreeView 的Items属性,TTreeNodes是TT ...

  4. delphi中设置系统时间方法

    procedure TMainFrm.Timer1Timer(Sender: TObject); var   systemtime:Tsystemtime;   dt:TDateTime; begin ...

  5. Delphi中Frame的使用方法(1)

    Frame是组件面板上的第一个组件,但不是每个人都知道怎么用它,因为它不像Button和Label一样简单明了.实际上,Frame按钮只是打开一个Frame的列表,如果你没有创建任何的Frame,自然 ...

  6. Delphi中close与Terminate方法的区别

    在有多个Form窗体时可以体现出来.用close是只关闭本窗体,而用Application.terminate是关闭整个程序,包括所有窗体.(1)当Close是一个主窗体时,程序会退出.Close会发 ...

  7. Delphi中字符串补齐方法

    函数功能:当Str不满Len长度时,在Str前自动填充PadStr以补足长度,例子如下: Str:原字符串 Len:补多长 PadStr:用什么补齐,比如‘0’ function PadString( ...

  8. 二十三、详述 IntelliJ IDEA 中恢复代码的方法「进阶篇」

    咱们已经了解了如何将代码恢复至某一版本,但是通过Local History恢复代码有的时候并不方便,例如咱们将项目中的代码进行了多处修改,这时通过Local History恢复代码就显得很麻烦,因为它 ...

  9. Delphi中根据分类数据生成树形结构的最优方法

    一. 引言:    TreeView控件适合于表示具有多层次关系的数据.它以简洁的界面,表现形式清晰.形象,操作简单而深受用户喜爱.而且用它可以实现ListView.ListBox所无法实现的很多功能 ...

随机推荐

  1. eclipse的使用一

    The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files ...

  2. js闭包原理

    一.定义 官方解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ****定义在函数中的函数,并且可在外部访问得到.(正常情况下我们是无法 ...

  3. Json数据报错

    在Json数据解析的时候报错,如下图: 这里的id,code,A1等等,都应该使用双引号,即:"id","code","A1",假如id后面 ...

  4. Linux mpstat字段解析

    mpstat是Multiprocessor Statistics的缩写,是实时系统监控工具.其报告与CPU的一些统计信息,这些信息存放在/proc/stat文件中.在多CPUs系统里,其不但能查看所有 ...

  5. 通过NSString初始化OC类

    NSString *className = self.classNames[indexPath.section][indexPath.row];    BaseMapViewController *s ...

  6. Apache commons-codec笔记

  7. Lattice Diamond 学习之编译、检查和设置约束

    在新建工程以及完成代码的输入之后.则就要进行编译,并检测错误. 一. Generate Hierarchy(产生层次结构). 1. 点击Generate Hierarchy 图标或者Design -- ...

  8. Python 实现发送、抄送邮件功能

    发送邮件 问题 在web.py中,如何发送邮件? 解法 在web.py中使用web.sendmail()发送邮件. web.sendmail('cookbook@webpy.org', 'user@e ...

  9. 阿里云 SWAP

    https://yq.aliyun.com/articles/52098 https://www.kejianet.cn/aliyun-swap/

  10. Tomcat7配置数据源

    http://www.cnblogs.com/ITtangtang/archive/2012/05/21/2511749.html