unit DBTree;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, ComCtrls, DB, FireDAC.Comp.Client;

Type
  PNodeInfo = ^TNodeInfo;
  TNodeInfo = Packed Record
     ID    : Integer;
     PID   : Integer;
     Caption  : String;
   end;

//创建树
//MemTable:内存表; TableName: 对应的表名; TreeView: 所使用的树
procedure MakeTree(MemTable: TFDQuery; TreeView: TTreeView);



//增加树的节点
//MemTable:内存表;TreeView: 所使用的树
function AddNode(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;

//增加树的子节点
//MemTable:内存表;TreeView: 所使用的树
function AddChildNode(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;

//增加节点（内部使用的过程）
//MemTable:内存表;TreeView: 所使用的树;bj:是否将焦点移到当前选择的节点
function AddTreeNode(MemTable: TFDQuery; TreeView: TTreeView; bj: boolean = false):TTreeNode;

//删除节点（包括删除本节点下的所有子节点）
//MemTable:内存表;TreeView: 所使用的树
procedure DelTree(MemTable: TFDQuery; TreeView: TTreeView);

//数据库中的游标已经移动，将焦点移到树中对应的节点
//MemTable:内存表;TreeView: 所使用的树
function TreeSelect(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;

function MakeGroup(MemTable: TFDQuery; TreeView: TTreeView; pGroupName, GroupName:string):TTreeNode;

//找出给定的内容，并移动数据库中的游标，同时将焦点移到树中对应的节点
//TreeView: 所使用的树 text: 要查找的内容
function TreeFind(TreeView: TTreeView; text: string): TTreeNode; overload;

function TreeFind(TreeNode:TTreeNode; text: string): TTreeNode; overload;

function TreeFindByID(TreeView: TTreeView; ID: Integer): TTreeNode;

var
  ANodeInfo : PNodeInfo;
implementation

procedure MakeTree(MemTable: TFDQuery; TreeView: TTreeView);
begin
  TreeView.Items.BeginUpdate;
  TreeView.items.clear;
  MemTable.DisableControls;

  MemTable.First;
  while not MemTable.Eof do
  begin
    AddTreeNode(MemTable, TreeView); //依次增加所有的节点
    MemTable.Next;
  end;
  TreeView.AlphaSort(); //排序
  TreeView.Items.EndUpdate;
  MemTable.EnableControls;
  if treeview.Items.Count < 1 then exit;
  treeview.Select(treeview.Items.Item[0]);
  treeview.FullExpand;
end;

function AddTreeNode(MemTable: TFDQuery; TreeView: TTreeView; bj: boolean = false):TTreeNode;
var
  Node: TTreeNode;
begin
  try
    New(ANodeInfo);
    ANodeInfo^.ID:=MemTable.FieldByName('ID').AsInteger;
    ANodeInfo^.PID:=MemTable.FieldByName('PID').AsInteger;
    ANodeInfo^.Caption:=MemTable.FieldByName('Caption').AsString;

    if MemTable.FieldByName('PID').AsInteger = 0 then { ParentID=0，顶层节点 }
      Node := TreeView.Items.AddChildObject(nil, MemTable.FieldByName('CAPTION').AsString, ANodeInfo)
    else
    begin
      //找到当前节点的父类
      Node:=TreeFindByID(TreeView,MemTable.FieldByName('PID').AsInteger);
      Node := TreeView.Items.AddChildObject(Node, MemTable.FieldByName('CAPTION').AsString, ANodeInfo);
      //增加子节点，并将本节点所对应的记录标签数据放到节点所提供的附加数据中
    end;
    Result:=Node;

    if bj then
    begin
      treeview.Select(node);
      treeview.SetFocus;
    end;
  finally
//    Dispose(ANodeInfo);
  end;
end;

procedure DelTree(MemTable: TFDQuery; TreeView: TTreeView);
var
  node: TTreenode;
//删除当前选择的节点下的所有节点（不删除当前选择的节点）
  procedure DelNode(node: TTreenode);
  var
    i: integer;
    childnode: TTreenode;
  begin
    for i := 0 to node.Count - 1 do
    begin
      childnode := node.Item[i]; //取当前节点下的所有节点
      MemTable.Locate('id',PNodeInfo(childnode.Data)^.ID,[]);
      MemTable.Delete; //删除对应的记录
      if node.HasChildren then DelNode(childnode); //有子节点则递归直到所有的数据删除完毕
    end;
  end;
{另一方法：
用一个递归：
procedure doSearch(tn: TTreeNode);
var i: ingeger;　　
begin
　if tn.HasChildren then
　begin
　　for i := tn.Count -1 downto 0 do doSearch(tn[i]);　
　end
　else
　begin
　　　//这里是你对每个节电要做的事情
　end;
end;}

begin
  node := treeview.Selected;
  if node = nil then exit;
  //删除当前选择的节点下的所有节点（不删除当前选择的节点）
  DelNode(node);
  //删除当前选择的节点
  if not MemTable.Locate('id',PNodeInfo(node.Data)^.ID,[]) then
  begin
    //删除失败
    Exit;
  end;
  MemTable.Delete;
  node.Delete;
  TreeView.SetFocus;
end;

function AddChildNode(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;
var
  id: integer;
begin
  id := MemTable.FieldByName('ID').AsInteger; //记下当前的节点编号
  MemTable.Append;
  MemTable.FieldByName('PID').AsInteger := id; //新增加的子节点的父节点编号即为id
  MemTable.FieldByName('Caption').AsString := '新子节点';
  MemTable.post;
  Result:=AddTreeNode(MemTable, TreeView, true);
end;

function AddNode(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;
var
  pid: integer;
begin
  pid := MemTable.FieldByName('PID').AsInteger; //记下当前节点的父节点编号
  MemTable.Append;
  MemTable.FieldByName('PID').AsInteger := pid; //新增加的子节点的父节点编号即为pid
  MemTable.FieldByName('caption').AsString := '新节点';
  MemTable.post;
  Result:=AddTreeNode(MemTable, TreeView, true);
end;

function TreeSelect(MemTable: TFDQuery; TreeView: TTreeView):TTreeNode;
var
  Node: TTreeNode;
begin
  Result:=nil;
  Node := TreeFindByID(TreeView,MemTable.FieldByName('ID').AsInteger);
  if Node<>nil then
  begin
    Result:=Node;
    treeview.Selected := Node;
    treeview.SetFocus;
  end;
end;

function TreeFind(TreeView: TTreeView; text: string): TTreeNode;
var
  i: integer;
begin
  Result:=nil;
  for i := 0 to treeview.Items.Count - 1 do
  begin
    if treeview.Items.Item[i].Text = text then
    begin
      Result:=treeview.Items.Item[i];
      exit;
    end;
  end;
end;

function TreeFindByID(TreeView: TTreeView; ID: Integer): TTreeNode;
var
  i:integer;
begin
  Result:=nil;
  for i := 0 to treeview.Items.Count - 1 do
  begin
    if PNodeInfo(treeview.Items[i].Data)^.ID = ID then
    begin
      Result:= treeview.Items[i];
      exit;
    end;
  end;
end;

function TreeFind(TreeNode:TTreeNode; text: string): TTreeNode;//根据某个节点得到所有的子节点
var
  vNode:TTreeNode;
begin
  Result:=nil;
  vNode:=TreeNode.getFirstChild;
  While vNode<>nil do
  begin
    if vNode.Text=text then
    begin
      Result:=vNode;
      Break;
    end
    else
      vNode:=TreeNode.GetNextChild(vNode);
  end;
end;

function MakeGroup(MemTable: TFDQuery; TreeView: TTreeView; pGroupName, GroupName:string):TTreeNode;
var
  pTreeNode,aTreeNode:TTreeNode;
begin
  //默认等于根结点
  pTreeNode:=TreeView.Items.GetFirstNode;
  //取得上级节点
  if pGroupName<>'' then
  begin
    aTreeNode:=TreeFind(TreeView,pGroupName);
    if aTreeNode<>nil then pTreeNode:=aTreeNode; //找到上级节点
  end;
  //找到节点
  if GroupName<>'' then
  begin
    aTreeNode:=TreeFind(pTreeNode,GroupName);    //从找到的上级节点下面找
    if aTreeNode<>nil then
    begin
      Result:=aTreeNode; //找到节点
      Exit;
    end;
  end;

  //分组或上级分组不存在，则新增分组
  if PNodeInfo(pTreeNode.Data)^.Caption<>pGroupName then //在目录下创建上一级节点
  begin
    MemTable.Append;
    MemTable.FieldByName('PID').AsInteger := 1;
    MemTable.FieldByName('caption').AsString := pGroupName;
    MemTable.post;
    pTreeNode:=AddTreeNode(MemTable, TreeView, true);
  end;

  MemTable.Append;
  MemTable.FieldByName('PID').AsInteger := PNodeInfo(pTreeNode.Data)^.ID;
  MemTable.FieldByName('caption').AsString := GroupName;
  MemTable.post;
  Result:=AddTreeNode(MemTable, TreeView, true);
end;

end.

