TTreeView TTreeNodes TTreeNode

时间:2022-08-23 05:06:00

TTreeView

填写 TTreeView 的内容一般是这样开始的(下图), 不过我觉得最好习惯用动态建立.


TTreeView   TTreeNodes TTreeNode


打个比方: 譬如 TreeView 是一个军营的"营部"! 这里会有营长、连长、排长、班长、战士等等.

我们把它们分成三种对象: 营部、营长(总当家)、营长的下属.

在这里的对应关系是:
TTreeView - 营部
TTreeNodes - 营长
TTreeNode - 营长的下属

上面的例子中的"营长"不是具体的哪个元素, 他代表着全营, 很多事情都是他说了算;
其他成员都是"营长"的兵.

换成 Delphi 的说法:TTreeView 不过是一个包含元素的框架, 最重要的是里面的元素;
每一个元素都是一个 TTreeNode 对象;
所有对象的集合也是一个对象: TTreeNodes, TTreeNodes 就像那个"营长"是个幕后管理者, 它对属下的所有成员有生杀大权.

学习 TreeView 要面对的不仅仅是 TTreeView 类, 更重要的是 TTreeNodes 和 TTreeNode 类.

TreeView1.Items 属性就是 TreeView1 的 TTreeNodes.

先做这样一个例子: 按照同样的编制, 再建一个营房.
准备工作: 在窗体上再添加一个 TreeView2 和一个按钮.
先看运行效果图:


TTreeView   TTreeNodes TTreeNode


代码:


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
TreeView2: TTreeView;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Caption := '转移';
Button2.Caption := '回防';
end; {转移: TreeView1 → TreeView2}
procedure TForm1.Button1Click(Sender: TObject);
begin
if Boolean(TreeView1.Items.Count) then {如果其中元素总数不是 0}
begin
TreeView2.Items := TreeView1.Items;
TreeView1.Items.Clear;
end;
end; {回防: TreeView2 → TreeView1}
procedure TForm1.Button2Click(Sender: TObject);
var
Nodes: TTreeNodes; {尽早习惯使用 TTreeNodes}
begin
Nodes := TreeView2.Items;
if Boolean(Nodes.Count) then
begin
TreeView1.Items := Nodes;
TreeView2.Items.Clear;
end;
end; end.
=================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode



源码:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} var
i: Integer; {这四个按钮的功能是一模一样的}
procedure TForm1.Button1Click(Sender: TObject);
begin
Self.Text := TreeView1.Items.Item[i].Text;
if i < TreeView1.Items.Count- then Inc(i) else i := ; TreeView1.SetFocus;
TreeView1.Select(TreeView1.Items.Item[i]);
end; {TreeView1.Items.Item[i] 是默认属性, 可以简写为: TreeView1.Items[i]}
procedure TForm1.Button2Click(Sender: TObject);
begin
Text := TreeView1.Items[i].Text;
if i < TreeView1.Items.Count- then Inc(i) else i := ; TreeView1.SetFocus;
TreeView1.Select(TreeView1.Items[i]);
end; {每个元素都是 TTreeNode 类型的}
procedure TForm1.Button3Click(Sender: TObject);
var
node: TTreeNode;
begin
node := TreeView1.Items[i];
Text := node.Text;
if i < TreeView1.Items.Count- then Inc(i) else i := ; TreeView1.SetFocus;
TreeView1.Select(node);
end; {尽量使用 TTreeNodes 与 TTreeNode}
procedure TForm1.Button4Click(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
Nodes := TreeView1.Items;
node := Nodes[i];
Text := node.Text;
if i < Nodes.Count- then Inc(i) else i := ; TreeView1.SetFocus;
TreeView1.Select(node);
end; end.
================================================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} {方法1: 按职务顺序入场}
procedure TForm1.Button1Click(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
Nodes := TreeView1.Items; {添加第一个元素}
node := Nodes.Add(nil, '一连长'); {'一连长'好孤独, 旁边没人} {现在 node 代表"一连长", 继续添加与其平级人员, 当然要参照"一连长"}
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长'); {安排"一连"的下一层: "排长"; 同时让 node 指向"一排长"}
node := Nodes.AddChild(node, '一排长');
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长'); {添加平级用 Add, 添加下级用 AddChild; 继续:}
node := Nodes.AddChild(node, '一班长');
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长'); {还有 AddChildFirst 和 AddFirst 是专门添加第一个的, 假如忘了第一个...}
node := Nodes.AddChild(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4');
Nodes.AddFirst(node, '战士1'); {用 AddFirst 可以让它站在前面}
end; {方法2: 按先后顺序入场}
procedure TForm1.Button2Click(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
Nodes := TreeView1.Items; node := Nodes.Add(nil, '一连长');
node := Nodes.AddChild(node, '一排长');
node := Nodes.AddChild(node, '一班长');
node := Nodes.AddChild(node, '战士1');
Nodes.Add(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4'); node := node.Parent; {让 node 指向上一层}
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长'); node := node.Parent;
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长'); node := node.Parent;
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长');
end; {方法3: 因为没使用 TTreeNodes 和 TTreeNode, 挺麻烦的}
procedure TForm1.Button3Click(Sender: TObject);
begin
TreeView1.Items.Add(nil, '一连长');
TreeView1.Items.Add(TreeView1.Items[], '二连长');
TreeView1.Items.Add(TreeView1.Items[], '三连长'); TreeView1.Items.AddChild(TreeView1.Items[], '一排长');
TreeView1.Items.Add(TreeView1.Items[].Item[], '二排长');
TreeView1.Items.Add(TreeView1.Items[].Item[], '三排长'); TreeView1.Items.AddChild(TreeView1.Items[].Item[], '一班长');
TreeView1.Items.Add(TreeView1.Items[].Item[].Item[], '二班长');
TreeView1.Items.Add(TreeView1.Items[].Item[].Item[], '三班长'); TreeView1.Items.AddChild(TreeView1.Items[].Item[].Item[], '战士1');
TreeView1.Items.Add(TreeView1.Items[].Item[].Item[].Item[], '战士2');
TreeView1.Items.Add(TreeView1.Items[].Item[].Item[].Item[], '战士3');
TreeView1.Items.Add(TreeView1.Items[].Item[].Item[].Item[], '战士4');
end; {方法4: 用 with 简化了"方法3", 也够绕的; 推荐使用前两种方法}
procedure TForm1.Button4Click(Sender: TObject);
begin
with TreeView1 do begin
Items.Add(nil, '一连长');
Items.Add(Items[], '二连长');
Items.Add(Items[], '三连长'); Items.AddChild(Items[], '一排长');
Items.Add(Items[].Item[], '二排长');
Items.Add(Items[].Item[], '三排长'); Items.AddChild(Items[].Item[], '一班长');
Items.Add(Items[].Item[].Item[], '二班长');
Items.Add(Items[].Item[].Item[], '三班长'); Items.AddChild(Items[].Item[].Item[], '战士1');
Items.Add(Items[].Item[].Item[].Item[], '战士2');
Items.Add(Items[].Item[].Item[].Item[], '战士3');
Items.Add(Items[].Item[].Item[].Item[], '战士4');
end;
end; procedure TForm1.Button5Click(Sender: TObject);
begin
TreeView1.Items.Clear;
TButton(Sender).Caption := '清空';
end; end.
=========================================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Grids; type
TForm1 = class(TForm)
TreeView1: TTreeView;
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);
procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
end; var
Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
{初始化 TreeView1}
TreeView1.Align := alLeft;
Nodes := TreeView1.Items;
node := Nodes.Add(nil, '一连长');
node := Nodes.AddChild(node, '一排长');
node := Nodes.AddChild(node, '一班长');
node := Nodes.AddChild(node, '战士1');
Nodes.Add(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4');
node := node.Parent;
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长');
node := node.Parent;
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长');
node := node.Parent;
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长'); {初始化 StringGrid1}
with StringGrid1 do begin
Align := alClient;
FixedRows := ;
ColCount := ;
ColWidths[] := ;
ColWidths[] := ;
DefaultRowHeight := ;
RowCount := ;
Cells[,] := '当前选择';
Cells[,] := '序号';
Cells[,] := '所在级别';
Cells[,] := '在兄弟中排行';
Cells[,] := '下级总数';
Cells[,] := '上级元素';
Cells[,] := '上一个';
Cells[,] := '下一个';
Cells[,] := '上一个兄弟';
Cells[,] := '下一个兄弟';
Cells[,] := '上一个可见';
Cells[,] := '下一个可见';
Cells[,] := '第一个下级';
Cells[,] := '最后一个下级';
Cells[,] := '总数';
end;
end; procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
with StringGrid1 do begin {清除第二列的数据}
StringGrid1.Cols[].Clear; {当前选择}
Cells[,] := Node.Text; {序号; AbsoluteIndex 是绝对序号}
Cells[,] := IntToStr(Node.AbsoluteIndex); {所在级别}
Cells[,] := IntToStr(Node.Level); {在兄弟中排行}
Cells[,] := IntToStr(Node.Index); {下级总数}
Cells[,] := IntToStr(Node.Count); {上级元素}
if Boolean(Node.Parent) then Cells[,] := Node.Parent.Text; {上一个}
if Boolean(Node.GetPrev) then Cells[,] := Node.GetPrev.Text; {下一个}
if Boolean(Node.GetNext) then Cells[,] := Node.GetNext.Text; {上一个兄弟}
if Boolean(Node.getPrevSibling) then Cells[,] := Node.getPrevSibling.Text; {下一个兄弟}
if Boolean(Node.getNextSibling) then Cells[,] := Node.getNextSibling.Text; {上一个可见}
if Boolean(Node.GetPrevVisible) then Cells[,] := Node.GetPrevVisible.Text; {下一个可见}
if Boolean(Node.GetNextVisible) then Cells[,] := Node.GetNextVisible.Text; {第一个下级}
if Node.HasChildren then Cells[,] := Node.getFirstChild.Text; {最后一个下级}
if Node.HasChildren then Cells[,] := Node.GetLastChild.Text; {总数}
Cells[,] := IntToStr(Node.Owner.Count);
end;
end; end.
==================================================================

之前忘了 StringOfChar 函数的存在, 曾经把程序写作:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Grids, ExtCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} {初始化 TreeView1}
procedure TForm1.FormCreate(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
TreeView1.Align := alLeft;
Nodes := TreeView1.Items;
node := Nodes.Add(nil, '一连长');
node := Nodes.AddChild(node, '一排长');
node := Nodes.AddChild(node, '一班长');
node := Nodes.AddChild(node, '战士1');
Nodes.Add(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4');
node := node.Parent;
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长');
node := node.Parent;
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长');
node := node.Parent;
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长');
end; {获取元素名列表}
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
Memo1.Clear;
for i := to TreeView1.Items.Count - do
Memo1.Lines.Add(TreeView1.Items[i].Text);
end; {使用 Level 属性和 Tab 字符格式化列表}
procedure TForm1.Button2Click(Sender: TObject);
var
arr: array[..MAXBYTE] of Char;
i: Integer;
s: string;
begin
Memo1.Clear;
FillChar(arr, Length(arr), #9);
for i := to TreeView1.Items.Count - do
begin
s := Copy(arr, , TreeView1.Items[i].Level);
s := s + TreeView1.Items[i].Text;
Memo1.Lines.Add(s);
end;
end; {上面那个不好看, 这个是用了 4 个空格做 Level 标识}
procedure TForm1.Button3Click(Sender: TObject);
var
arr: array[..MAXBYTE] of Char;
i: Integer;
s: string;
begin
Memo1.Clear;
FillChar(arr, Length(arr), #32);
for i := to TreeView1.Items.Count - do
begin
s := Copy(arr, , TreeView1.Items[i].Level * );
s := s + TreeView1.Items[i].Text;
Memo1.Lines.Add(s);
end;
end; end.
===================================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Grids, ExtCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Memo1: TMemo;
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} {初始化 TreeView1}
procedure TForm1.FormCreate(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
TreeView1.Align := alLeft;
Nodes := TreeView1.Items;
node := Nodes.Add(nil, '一连长');
node := Nodes.AddChild(node, '一排长');
node := Nodes.AddChild(node, '一班长');
node := Nodes.AddChild(node, '战士1');
Nodes.Add(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4');
node := node.Parent;
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长');
node := node.Parent;
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长');
node := node.Parent;
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长');
end; {保存到文件}
procedure TForm1.Button1Click(Sender: TObject);
const
path = 'c:\temp\tree.txt';
begin
TreeView1.SaveToFile(path);
TButton(Sender).Caption := '保存';
end; {从文件中载入}
procedure TForm1.Button2Click(Sender: TObject);
const
path = 'c:\temp\tree.txt';
begin
if FileExists(path) then TreeView1.LoadFromFile(path);
TButton(Sender).Caption := '载入';
end; {从流中读取}
procedure TForm1.Button3Click(Sender: TObject);
var
stream: TStream;
begin
stream := TMemoryStream.Create;
try
Memo1.Lines.SaveToStream(stream);
stream.Position := ;
TreeView1.LoadFromStream(stream);
finally
stream.Free;
end;
end; end.
{注意: 上面例子中的"从流中读取"部分, 对 Memo 中的数据格式有严格要求, 要求必须是树状结构; 结构中可以用"空格"和"Tab"分层.}
============================================================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Grids, ExtCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject);
var
Nodes: TTreeNodes;
node: TTreeNode;
begin
TreeView1.Align := alLeft;
Nodes := TreeView1.Items;
node := Nodes.Add(nil, '一连长');
node := Nodes.AddChild(node, '一排长');
node := Nodes.AddChild(node, '一班长');
node := Nodes.AddChild(node, '战士1');
Nodes.Add(node, '战士2');
Nodes.Add(node, '战士3');
Nodes.Add(node, '战士4');
node := node.Parent;
Nodes.Add(node, '二班长');
Nodes.Add(node, '三班长');
node := node.Parent;
Nodes.Add(node, '二排长');
Nodes.Add(node, '三排长');
node := node.Parent;
Nodes.Add(node, '二连长');
Nodes.Add(node, '三连长');
end; {初始化}
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.Items.Clear;
FormCreate(nil);
TButton(Sender).Caption := '初始化';
end; {删除}
procedure TForm1.Button2Click(Sender: TObject);
var
node: TTreeNode;
begin
if not Boolean(TreeView1.Items) then Exit; {如果 TreeView1 无内容}
if TreeView1.SelectionCount = then Exit; {如果无选择}
node := TreeView1.Selected;
node.Delete; TButton(Sender).Caption := '删除';
end; {删除它的所有下级}
procedure TForm1.Button3Click(Sender: TObject);
var
node: TTreeNode;
begin
if not Boolean(TreeView1.Items) then Exit;
if TreeView1.SelectionCount = then Exit;
node := TreeView1.Selected;
if node.HasChildren then node.DeleteChildren
else ShowMessage(node.Text + ' 没有下级'); TButton(Sender).Caption := '删除下级';
end; {用 TTreeNodes 删除}
procedure TForm1.Button4Click(Sender: TObject);
var
node: TTreeNode;
begin
if not Boolean(TreeView1.Items) then Exit;
if TreeView1.SelectionCount = then Exit;
node := TreeView1.Selected;
TreeView1.Items.Delete(node); TButton(Sender).Caption := '用 TTreeNodes 删除';
end; {插入, 只能通过 TTreeNodes}
procedure TForm1.Button5Click(Sender: TObject);
var
node: TTreeNode;
begin
if not Boolean(TreeView1.Items) then Exit;
if TreeView1.SelectionCount = then Exit;
node := TreeView1.Selected;
TreeView1.Items.Insert(node, '新成员'); TButton(Sender).Caption := '插入';
end; end.
=================================================================================================

本例效果图:

TTreeView   TTreeNodes TTreeNode


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls; type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
end; var
Form1: TForm1; implementation {$R *.dfm} {初始化}
procedure TForm1.FormCreate(Sender: TObject);
var
stream: TMemoryStream;
begin
TreeView1.Align := alLeft;
stream := TMemoryStream.Create;
with TStringList.Create do
begin
Add('2');
Add('3');
Add('1');
Add('4');
Add(#32'2');
Add(#32'3');
Add(#32'1');
Add(#32'4');
Add(#32#32'2');
Add(#32#32'3');
Add(#32#32'1');
Add(#32#32'4');
SaveToStream(stream);
Free;
end;
stream.Position := ;
TreeView1.LoadFromStream(stream);
TreeView1.SortType := stNone;
stream.Free; Button1.Caption := '排序第一层';
Button2.Caption := '全部排序';
Button3.Caption := '指定排序类型';
Button4.Caption := '倒排序';
Button5.Caption := '初始化';
end; {只排序第一层}
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.AlphaSort(False);
end; {全部排序}
procedure TForm1.Button2Click(Sender: TObject);
begin
TreeView1.AlphaSort;
end; {指定排序类型}
procedure TForm1.Button3Click(Sender: TObject);
begin
TreeView1.SortType := stText; {SortType 有四种选择:
stNone, stData, stText, stBoth
stText 表示按显示的标签文本排序.
}
end; {倒排序}
procedure TForm1.Button4Click(Sender: TObject);
function MySort(Node1, Node2: TTreeNode; lParam: Integer): Integer; stdcall;
begin
Result := -AnsiStrIComp(PChar(Node1.Text), PChar(Node2.Text));
end;
begin
TreeView1.CustomSort(@MySort, );
end; {重新初始化}
procedure TForm1.Button5Click(Sender: TObject);
begin
FormCreate(nil);
end; end.