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; ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 终于总结完了,希望对大家有所帮助,欢迎提出宝贵意见,以便改进! |