如何用TreeView控件生成数据表的层次结构?(100分)

  • 主题发起人 主题发起人 feng701
  • 开始时间 开始时间
F

feng701

Unregistered / Unconfirmed
GUEST, unregistred user!
如下表:
ID NAME PARENT
-1 公司
1 1科 -1
2 2科 -1
3 3科 -1
4 1科A 1
5 1科B 1
6 2科A 2
7 3科A 3
8 1科AA 4
9 1科AB 4
10 1科AAA 8
11 1科AAB 8
12 1科AAAA 10
希望生成如下的Treeview图:
公司-|-1科-------|---1科A----1科AA...
| |---1科B--...
| |---1科C-...
|-2科-....
| |
...

请各位大虾相助。





 
好像不难呀!

先对您的表按照Len(Name)从小到大排序,然后把每条记录依次插入TreeView中的Items,
插入的时候在TreeView中的对应项(TTreeNode类型)里面的ItemID:=ID
插入的位置这样确定:
var newNode, parentNode:TreeNode;

begin

while (there's record in database) do begin
// Get current record in database, and assign to newNode;

parentNode := TreeView1.Items.GetNode(ID);
if (parentNode<>Nil) then
parentNode.AddChild(newNode)
else
TreeView1.Items.AddNode(newNode);

end // while

end.
 
糟糕,原理是这样,不过上面的示例中简直是错误百出,
不好意思,等我马上运行Delphi搞出个能够作为例子的东西
 
谢谢pegasus的回答!
补充说明的是,希望能把类似于此的有层次结构的数据做成目录树,理论上
其层次不受限制。我已苦思了两天无结果。
以上code有启发,但...请给出详细点的code,行吗?
 
好, 我正在制作中,:)
要用到TList做辅助记录ID对应的TreeNode的ItemID.
你的层次不受限制的要求用我的办法是可以实现的,
保证今天完成您的任务还有songguolong 的那个InternetOpenURL的任务,:)
 
不劳Pega大驾了。我抢分来也。嘿嘿..

type pInt = ^integer;

procedure TForm1.Button1Click(Sender: TObject);
var ParentNode,Node : TTreeNode;
id : pInt;
begin
table.open;
table.first;
while not table.eof do
begin
//使用Node.data域来存放id,以用于find;
ParentNode:=FindNodeByID(T1,table.FieldByName('PARENT').asInteger);
new(id);
id^:=table.FieldByName('ID').asInteger;
Node:=T1.items.AddChildObject(ParentNode,table.FieldByName('NAME').asString,id);
table.Next;
end;
table.close;
end;

Function TForm1.FindNodeByID(T:TTreeView; id:integer):TTreeNode;
var i:integer;
begin
result:=nil;
for i:=0 to T.Items.Count-1 do
if integer(T.Items.Data^)=id
then begin
result := T.Items;
exit;
end;
end;

要注意在free TreeView之前要把items.data^先释放掉。不然就会有内存泄漏。

 
OK, that's nice!

补充一点:
要求您的Table中的记录顺序严格按照父节点出现在子节点之前的顺序,
如果故事,还要加上一段排序的代码

 
不知各位注意到没有, 如果数据表很大, 一次全部生成所有点是否太浪费了.
在用户要打开下一层内容时生成, 是否效率要高一点.
 
对,wuyi说的有理。
不过如果不是太大的话好像没有必要。
比如说delphiBBS的离线数据,应该不少了吧,据yysun说已经超过20000条了。
在我的菜羊300A上生成Treeview,一共是两次,40000条的时间是8秒哦。
测试用的是Butterfly Offline Reader. :)
 
曹晓钢的方法是我一年前用的把戏了(参考李维的Delphi3从入门到精通).

现在不用了,现在用类.每一个结点都是一个类,其子项就是这个类的子项类.
优点很多,因为你可以把树的维护交给类来完成,TreeView释放时也不用释放
ID号.我的程序的主界面都与Win98的资源管理器相同.如果要示范源码,说一声.
 
能把范例源码给我一份吗?
please mail to flyhorse@mail.777.net.cn
thank you
 
已经寄出.可能不能完全说明问题.
曹的答案虽然正确,但只能处理两层,不能处理任意层,因为没有递归.
李维的方法可行,但代价太大,但深入一层需要建立一个新的数据集.
我的办法则要简洁一些.

TSchoolObject = class(TObject)
private
FOwner: TSchoolBranchObject;
FID: Integer;
FName: string;
FLevel: Integer;
FValid, FSelected: Boolean;
procedure SetName(const Value: string);
procedure SetValid(Value: Boolean);
procedure SetSelected(Value: Boolean);
function GetIndexOf: Integer;
protected
procedure BeginReport(AView: TRichView); virtual;
procedure EndReport(AView: TRichView); virtual;
public
constructor Create(AOwner: TSchoolBranchObject; AID: Integer;
const AName: string; ALevel: Integer; AValid: Boolean);
function ListItem(AView: TExtListView): TListItem; virtual; //列出本项
procedure ReportItem(AView: TRichView); virtual; //简单报告
procedure DetailItem(AView: TRichView); virtual; //详细报告
property ID: Integer read FID;
property Name: string read FName write SetName;
property Owner: TSchoolBranchObject read FOwner;
property LevelIndex: Integer read FLevel;
property Valid: Boolean read FValid write SetValid;
property Selected: Boolean read FSelected write SetSelected;
property IndexOf: Integer read GetIndexOf;
end;

TSchoolBranchObject = class(TSchoolObject)
private
FChildren: TList;
FNode: TTreeNode;
FSelCount: Integer;
function GetCount: Integer;
function GetItems(Index: Integer): TSchoolObject;
protected
procedure CreateHeader(AView: TExtListView); virtual; abstract;
function GetPathName: string; virtual; abstract;
public
constructor Create(AOwner: TSchoolBranchObject; AID: Integer;
const AName: string; ALevel: Integer; AValid: Boolean);
destructor Destroy; override;
procedure Add(AObj: TSchoolObject);
procedure Delete(Index: Integer);
procedure Clear;
procedure ClearSelected;
procedure IncSelected;
procedure DecSelected;
function SeekChild(AID: Integer): TSchoolObject;
function CreateNode(ParentNode: TTreeNode): TTreeNode; virtual; //生成本结点
procedure CreateNodes; virtual; //生成子结点
function CreateView(AView: TExtListView): string; virtual; //生成列表
function IndexOf(AObject: TSchoolObject): Integer;
property Count: Integer read GetCount;
property Items[index: Integer]: TSchoolObject read GetItems; default;
property Node: TTreeNode read FNode write FNode;
property SelCount: Integer read FSelCount;
end;

....

//TSchoolObject 对象

constructor TSchoolObject.Create(AOwner: TSchoolBranchObject; AID: Integer;
const AName: string; ALevel: Integer; AValid: Boolean);
begin
FOwner := AOwner;
FID := AID;
FName := AName;
FLevel := ALevel;
FValid := AValid;
FSelected := False;
end;

procedure TSchoolObject.SetName(const Value: string);
begin
if FName <> Value then
FName := Value;
end;

procedure TSchoolObject.SetValid(Value: Boolean);
begin
if FValid <> Value then
FValid := Value;
end;

procedure TSchoolObject.SetSelected(Value: Boolean);
begin
if FSelected <> Value then begin
FSelected := Value;
if FSelected then
FOwner.IncSelected
else
FOwner.DecSelected;
end;
end;

function TSchoolObject.GetIndexOf: Integer;
begin
Result := FOwner.IndexOf(Self);
end;

procedure TSchoolObject.BeginReport(AView: TRichView);
begin
AView.Clear;
AView.AddFromNewLine(' ', 1);
AView.AddFromNewLine(FOwner.Name, 1);
AView.AddGradBreak;
AView.AddFromNewLine(' ', 0);
AView.AddFromNewLine(Name, 1);
end;

procedure TSchoolObject.EndReport(AView: TRichView);
begin
AView.FormatIt;
AView.Refresh;
end;

function TSchoolObject.ListItem(AView: TExtListView): TListItem;
begin
Result := AView.Items.Add;
with Result do begin
Caption := Name;
ImageIndex := LevelIndex;
Data := Self;
end;
end;

procedure TSchoolObject.ReportItem(AView: TRichView);
begin
BeginReport(AView);
EndReport(AView);
end;

procedure TSchoolObject.DetailItem(AView: TRichView);
begin
AView.FormatIt;
AView.Refresh;
end;

//TSchoolBranchObject 对象

constructor TSchoolBranchObject.Create(AOwner: TSchoolBranchObject;
AID: Integer; const AName: string; ALevel: Integer; AValid: Boolean);
begin
inherited Create(AOwner, AID, AName, ALevel, AValid);
FChildren := TList.Create;
FSelCount := 0;
end;

destructor TSchoolBranchObject.Destroy;
begin
Clear;
FChildren.Free;
inherited Destroy;
end;

function TSchoolBranchObject.GetCount: Integer;
begin
Result := FChildren.Count;
end;

function TSchoolBranchObject.GetItems(Index: Integer): TSchoolObject;
begin
Result := FChildren[Index];
end;

procedure TSchoolBranchObject.Add(AObj: TSchoolObject);
begin
FChildren.Add(AObj);
end;

procedure TSchoolBranchObject.Delete(Index: Integer);
begin
TSchoolObject(FChildren[Index]).Free;
FChildren.Delete(Index);
end;

procedure TSchoolBranchObject.Clear;
begin
while FChildren.Count > 0 do
Delete(FChildren.Count - 1);
end;

procedure TSchoolBranchObject.ClearSelected;
var
I: Integer;
begin
for I := 0 to Count - 1 do
GetItems(I).Selected := False;
end;

procedure TSchoolBranchObject.IncSelected;
begin
Inc(FSelCount);
end;

procedure TSchoolBranchObject.DecSelected;
begin
Dec(FSelCount);
end;

function TSchoolBranchObject.SeekChild(AID: Integer): TSchoolObject;
var
I: Integer;
begin
Result := nil;
for I := 0 to Count - 1 do
if GetItems(I).ID = AID then begin
Result := GetItems(I);
Break;
end;
end;

function TSchoolBranchObject.CreateNode(ParentNode: TTreeNode): TTreeNode;
begin
Result := TTreeView(ParentNode.TreeView).Items.AddChildObject(ParentNode,
Name, Self);
Result.ImageIndex := LevelIndex;
FNode := Result;
CreateNodes;
end;

procedure TSchoolBranchObject.CreateNodes;
var
I: Integer;
begin
if Count > 0 then
if GetItems(0) is TSchoolBranchObject then
for I := 0 to Count - 1 do
TSchoolBranchObject(GetItems(I)).CreateNode(FNode);
end;

function TSchoolBranchObject.CreateView(AView: TExtListView): string;
var
I: Integer;
begin
Result := GetPathName;
CreateHeader(AView);
AView.Items.Clear;
for I := 0 to Count - 1 do
GetItems(I).ListItem(AView);
end;

function TSchoolBranchObject.IndexOf(AObject: TSchoolObject): Integer;
begin
Result := FChildren.IndexOf(AObject);
end;

如果以上代码前言不搭后语也是正常的,这是从我的单元中断章取义.

使用时一次将数据集打开,按顺序填入,然后用以下代码直接建立树:

ATreeView.Items.Clear;
Node := ATreeView.Items.AddChildObject(nil, Caption1, Self);
CreateNodes;
 
to barton:
>曹的答案虽然正确,但只能处理两层,不能处理任意层,因为没有递归.

这个。... 此言差矣。
为什么只能处理两层呢?为什么非要用递归呢?
老兄好像都理解错了吧?
试试看就明白了。

>曹晓钢的方法是我一年前用的把戏了(参考李维的Delphi3从入门到精通).

>现在不用了,现在用类.每一个结点都是一个类,其子项就是这个类的子项类.
>优点很多,因为你可以把树的维护交给类来完成,TreeView释放时也不用释放
>ID号.

对。说得不错。用类是最好的解决方案。但是我一贯遵循够用的原则,决不会在
夏天来买棉袄来御寒。所以我认为我的程序段在feng701的问题上足矣。

如果真的想要用类,请参见本人的Butterfly Offline Reader的源码,我实现了
一个和具体内容无关的TSQLTreeView.每一层的处理条件都是可以自己用SQL来配置
的。可以说是通用的了。

希望有帮助。
 
to 曹晓钢:
Thanks very much!
>要注意在free TreeView之前要把items.data^先释放掉。
不然就会有内存泄漏。

我运行了你的code后,效果非常好,实现了数据层次不受限制的目的。
但你如上说的一句话什么意思?该如何实现,请用code详细说明?

to barton:
你能mail一份源码来吗? feng701@21cn.com
Thanks!

 
我是自己申请了内存来存放id的。
在你用完了这个Treeview,比如在窗口关闭时,要先dispose它。
procedure Tform1.FormClose;
var i:integer;
begin
for i:=0 to T1.items.count do
begin
dispose(T1.items.data);
T1.items.data:=nil;
end;
end;
 
我曾经从RSD 买了一个DBTreeview , 带源代码,
它就是针对ID , Name , Parent 的结构问题, 
速度很快,For D3 的.
(不过从来就没有注意过源代码,拿来就用,主要用在
结构的显示上, 其中很的功能对我来说是浪费).

btw , 有兴趣的话,找曹晓刚要,我记得给过他一份.(主要是
Email发送太慢了). 
 

to barton:
你能mail一份源码来吗? lieutenant@263.net
Thanks!
 
Sorry,以上释放内存的程序段有笔误。
循环应该是
for i:=0 to T1.items.count-1 do
begin
..
end;
 
多人接受答案了。
 
后退
顶部