zoukankan      html  css  js  c++  java
  • Delphi实现树型结构

    生成树型结构有2种方法:

    1.动态生成树结点

    2.静态生成树结点

    这里暂不讨论动态生成树,先实现静态生成!

    所谓静态生成树结点是指通过遍历数据源的方式一次性把所有树结点全部加载,说起生成树避免不了谈起数据库结构的设计。

    数据库设计的方法有2种:

    1.单编号法

    单编号法是以每个类为统一编号,如其有子类,则顺着该编号向后排。如水果编号为001,则苹果为水果的一类,则应为001001等等,这种方法易于统计,但不易于维护!如:想要将苹果类变为其它的类,且苹果类下有N层,那会是一件比较麻烦的事情!

    2.双编号法

    双编号也就是我们经常说的用ID与PARENTID来表示其父子关系,维护起来比较方便,但遍历会稍稍复杂一些!

    无论哪种方法,一般情况下都要创建结构体,并把结构体指针放置到树结点中!因为Treeview的树结点不可能放

    置更多的信息,我们通常要存该结点的名称(中英文)、结点ID、是否为功能结点、父结点、图标、选中结点后的图标、dll的名称等等等...

    以下例子:

    unit Unit1;
    interface
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ComCtrls, DB, ADODB;
    type
      PNodeInfoEx = ^TNodeInfoEx;
      TNodeInfoEx = Packed Record
          NodeID    : Integer;
          ParentID  : Integer;
          NodeType  : Integer;
          ChnNodeTitle : String;
          ImageIndex: SmallInt;
          SelectedIndex: SmallInt;
       end;
      TForm1 = class(TForm)
        tv1: TTreeView;
        btn1: TButton;
        qry1: TADOQuery;
        procedure btn1Click(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        { Private declarations }
        function StaticBuildTree(TreeView:TTreeView ):Boolean;
        function AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode;
        function FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode;
      public
        { Public declarations }
      end;
    var
      Form1: TForm1;
    implementation
    {$R *.dfm}
     
    function TForm1.StaticBuildTree(TreeView:TTreeView ):Boolean;
    var
      AddNodeInfo : PNodeInfoEx;
    begin
      Result := False;
      qry1.LoadFromFile('c:/AdminixTree.xml');//这里以XML文件做为数据源
      Treeview.Items.BeginUpdate;//记住:在进行批量添加数据时要使用BeginUpdate,来暂时关闭由于添加数据而触发的某些事件(如OnChange事件等)
      Treeview.Items.Clear;//清空Treeview
      try
        try
          if qry1.RecordCount >0 then
            begin
              qry1.First;
              while Not qry1.Eof do
              begin
                New(AddNodeInfo) ;//生成结构体
                AddNodeInfo^.NodeID := qry1.FieldByName('NODE_ID').AsInteger;
                AddNodeInfo^.ParentID := qry1.FieldByName('PARENT_ID').AsInteger;
                AddNodeInfo^.NodeType := qry1.FieldByName('NodeType').AsInteger;
                AddNodeInfo^.ChnNodeTitle := qry1.FieldByName('ChnNodeTitle').AsString;
                AddNodeInfo^.ImageIndex := qry1.FieldByName('ImageIndex').AsInteger;
                AddNodeInfo^.SelectedIndex := qry1.FieldByName('SelectedIndex').AsInteger;
                AddTreeItem(Treeview,AddNodeInfo);//把结构体的指针存到Treeview中
                qry1.Next;
              end;
            end;
        except
          Application.MessageBox('生成树结点失败',MB_ICONSTOP+MB_OK);
          raise;//向上级抛异常
        end;
        qry1.Close;
        Result := True;
      finally
        Treeview.Items.EndUpdate;
      end;
    end;
    //在加入结点时,应先判断加入的是父结点还是子结点,判断的依据是在已存在的树结点中是否存在该结点的ParentID
    function TForm1.AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode;
    var
        ParentNode: TTreeNode;
    begin
        ParentNode := FindTreeItem(Treeview,AddNodeInfo^.ParentID);
        If ParentNode <> nil then
            Result := Treeview.Items.AddChildObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo))
        else
            Result := Treeview.Items.AddObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo));
        if Result<>nil then
        begin
            Result.ImageIndex    := AddNodeInfo.ImageIndex;
            Result.SelectedIndex := AddNodeInfo.SelectedIndex;
        end;
    end;
    //这里是判断是否存在其父结点
    function TForm1.FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode;
    var
        i : Integer;
    begin
      Result := nil;
      for i := 0 to Treeview.Items.Count-1 do
      begin
          if CurNodeID=PNodeInfoEx(Treeview.Items[i].Data)^.NodeID then
          begin
              Result := Treeview.Items[i];
              Exit;
          end;
      end;
    end;
    //生成树结构
    procedure TForm1.btn1Click(Sender: TObject);
    begin
       StaticBuildTree (tv1)
    end;
    //在窗体释放时一定要把树结点中的结构体指针给释放掉,对于在Dispose时为什么要进行强制转型后释放,以前有专门的讲解,在此不在累述
    procedure TForm1.FormDestroy(Sender: TObject);
    var
        i : Integer;
    begin
      for i := 0 to tv1.Items.Count-1 do
      begin
           Dispose( PNodeInfoEx(tv1.Items[i].Data)  )
      end;
    end;
    end.
    //如何访问树结点?
    procedure TForm1.tv1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    var
      pNode:TTreeNode;
    begin
      pNode:=tv1.GetNodeAt(x,y);
      if (pNode<>nil) and  (Button=mbleft) then
       ShowMessage(PNodeInfoEx(pNode.Data)^.ChnNodeTitle);
    end;
  • 相关阅读:
    C++ 学习笔记
    面向对象
    多线程
    Spring-扫描注解原理,注解自动扫描原理分析
    Eclipse 中报错的阅读顺序
    Eclipse 常用技巧及常见问题解决
    JAVA高级复习-自定义泛型类、泛型接口的注意点
    JAVA高级复习-泛型的使用
    IntelliJ IDEA学习笔记连载一IntelliJ IDEA中创建Maven工程
    JAVA高级复习-多线程的创建方式二
  • 原文地址:https://www.cnblogs.com/jijm123/p/12786590.html
Copyright © 2011-2022 走看看