至少你应该在表中有个字段表示开始结点啊?
var Node,ChildNode: TTreeNode; with TreeView do
begin
Items.Clear;
Query1.Close;
Query1.Sql.Text := 'SELECT * FROM tbTree WHERE .....'; //取第一层
Query1.Open;
while not Query1.Eof do
begin
Node := Items.AddChild(nil,Query1.FieldByName('TreeName').AsString);
Query2.Close;
Query2.Sql.Text := 'SELECT * FROM tbTree WHERE UpperTreeID='''+Query1.FieldByName('TreeID')+'''';
Query2.Open;
while not Query2.Eof do
begin
DepNode := Items.AddChild(UnitNode,Query2.FieldByName('TreeName').AsString);
Query2.Next;
end;
Query1.Next;
end;
end;
var Node,ChildNode: TTreeNode; with TreeView do
begin
Items.Clear;
Query1.Close;
Query1.Sql.Text := 'SELECT * FROM tbTree WHERE .....'; //取第一层
Query1.Open;
while not Query1.Eof do
begin
Node := Items.AddChild(nil,Query1.FieldByName('TreeName').AsString);
Query2.Close;
Query2.Sql.Text := 'SELECT * FROM tbTree WHERE UpperTreeID='''+Query1.FieldByName('TreeID')+'''';
Query2.Open;
while not Query2.Eof do
begin
DepNode := Items.AddChild(UnitNode,Query2.FieldByName('TreeName').AsString);
Query2.Next;
end;
Query1.Next;
end;
end;
var Node,ChildNode: TTreeNode; with TreeView do
begin
Items.Clear;
Query1.Close;
Query1.Sql.Text := 'SELECT * FROM tbTree WHERE .....'; //取第一层
Query1.Open;
while not Query1.Eof do
begin
Node := Items.AddChild(nil,Query1.FieldByName('TreeName').AsString);
Query2.Close;
Query2.Sql.Text := 'SELECT * FROM tbTree WHERE UpperTreeID='''+Query1.FieldByName('TreeID')+'''';
Query2.Open;
while not Query2.Eof do
begin
ChildNode := Items.AddChild(Node,Query2.FieldByName('TreeName').AsString);
Query2.Next;
end;
Query1.Next;
end;
end;
你这样好像不可以,只能搜索出第二层来,第三层以后就搜索不出了。
laoZheng(阿明):
我现在只是想知道treeview是怎么和数据库联系起来的。
Version 0.82 Aug-10-1997 (C) 1997 Christoph R. Kirchner
!! This component is currently UNDER CONSTRUCTION !!
}
{ Users of this unit must accept this disclaimer of warranty:
"This unit is supplied as is. The author disclaims all warranties,
expressed or implied, including, without limitation, the warranties
of merchantability and of fitness for any purpose.
The author assumes no liability for damages, direct or
consequential, which may result from the use of this unit." This Unit is donated to the public as public domain. This Unit can be freely used and distributed in commercial and
private environments provided this notice is not modified in any way. If you do find this Unit handy and you feel guilty for using such a
great product without paying someone - sorry :-) Please forward any comments or suggestions to Christoph Kirchner at:
[email protected] Maybe you can find an update of this component at my
"Delphi Component Building Site":
http://www.geocities.com/SiliconValley/Heights/7874/delphi.htm Thanks to Maxim Monin for his TDBOutline-component I could start with.
}
interfaceuses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
CommCtrl, Dialogs, BDE, Dbconsts, DB, DBTables, StdCtrls, ComCtrls,
TreeVwEx, ECDataLink, dbTvRecordList;type TCustomDBTreeView = class; TDBTreeOption = (
dtAllowDelete, dtAllowInsert, dtAutoDragMove, dtAutoExpand,
dtAutoShowRoot, dtCancelOnExit, dtConfirmDelete, dtFocusOnEdit,
dtInsertAsChild, dtMouseMoveSelect, dtRebuildFocusedOnly,
dtRootItemReadOnly, dtSynchronizeDataSet);
TDBTreeOptions = set of TDBTreeOption;{ Options: dtAutoDragMove:
The user can move items by dragging them in the DBTreeView.
The parent-field of the record of the moved item will be set to the
ID of the new parent automatically. dtAutoExpand:
The tree will get expanded completely after building. dtAutoShowRoot:
The ShowRoot property specifies whether lines connecting root (top-
level) items are displayed. If dtAutoShowRoot is in Options, the
DBTreeView will set the ShowRoot property itself, depending on the
numbers of root-items: If are more then one of them, the DBTreeView
will set the ShowRoot property to True, otherwise it will set the
ShowRoot property to False.
ShowRoot = True will show lines connecting the root items if the
ShowLines property is True, and If ShowButtons is set to True, a
button will appear to the left of each root item. dtCancelOnExit:
An insertion-operation get canceled if the user leaves the DbTreeView
without changing the inserted record. This avoids empty records in the
table. DtCancelOnExit is similar to dgCancelOnExit of TDBGrid. dtConfirmDelete:
The user gets asked if he really want to delete the current record
after he pressed the Del-key. If the current record has children, the
user gets asked if he want to delete them first. dtFocusOnEdit:
If the dataset changes to edit- or insert-mode, the DbTreeView will
get the focus. This allows to user to edit the tree-node directly
after pressing edit or insert on a navigation-button. Also, if the
dateaset is in edit- or insert-mode and the DbTreeView receives the
focus, the selected node goes into the edit-mode. Please do not set
dtFocusOnEdit if there is TDBEdit, TDBMemo or TDBGrid on the form
connected to the same dataset - the user could get confused too much.
Also, please set dtSynchronizeDataSet too if you use dtFocusOnEdit.
Setting dtFocusOnEdit is recommended if there is - more or less -
only a DbTreeView and a DBNavigator on the form. dtInsertAsChild:
The new item that is created by pressing the insert key gets
inserted after the selected node if tveInsertAsChild is false or
it gets inserted as a child of the selected node if tveInsertAsChild
is true. dtMouseMoveSelect:
If the user moves the mouse, the nearest node gets selected.
If the user moves the mouse to the upper or lower border of the
DbTreeView while left button pressed, the DbTreeView will scroll.
This scrolling happens anyway if the user drags a node.
The option dtMouseMoveSelect makes sense if the DbTreeView is shown
in a dropdown-panel. dtRebuildFocusedOnly:
If dtRebuildFocusedOnly is in Options, the DbTreeView will not
rebuild the tree after the dataset changed unless the DbTreeView gets
the focus. This is not set by default. The TDbTreeLookupComboBox uses
this option to avoid needless rebuilds of the DbTreeView in the drop-
down-panel until it gets visible. dtRootItemReadOnly:
If there is a record in the dataset with the ID of RootID, then you
can set it to read-only by setting RootItemReadOnly to true. dtSynchronizeDataSet:
The current selected treenode will always represent the current record
of the DataSet. If not dtSynchronizeDataSet in Options, selecting
treenodes gets faster.
} TDBTVGetNextIDEvent =
function (Sender: TObject; DataSet: TDataSet): string of object; TTVFindTextOption = (tvftCaseInsensitive, tvftPartial);
TTVFindTextOptions = set of TTVFindTextOption; TDBTreeViewState = (
dtvsBuilding, dtvsDatasetInEditMode, dtvsEditAfterReBuild,
dtvsDatasetInInsertMode, dtvsLostFocusWhileDatasetInEditModes,
dtvsChangingDataset, dtvsNeedReBuildAfterPost, dtvsNeedReBuild,
dtvsDataSetNeedsRefresh);
TDBTreeViewStates = set of TDBTreeViewState;
private
FTreeView: TCustomDBTreeView;
protected
procedure DatasetRefreshed; override;
procedure ActiveChanged; override;
procedure DataSetChanged; override;
procedure DataSetScrolled(Distance: Integer); override;
procedure RecordChanged(Field: TField); override;
procedure EditingChanged; override;
{ procedure UpdateData; override; }
procedure DoBeforePost(DataSet: TDataSet); override;
procedure DoAfterPost(DataSet: TDataSet); override;
procedure DoAfterCancel(DataSet: TDataSet); override;
procedure DoBeforeDelete(DataSet: TDataSet); override;
procedure DoAfterDelete(DataSet: TDataSet); override;
procedure DoBeforeEdit(DataSet: TDataSet); override;
procedure DoBeforeInsert(DataSet: TDataSet); override;
public
constructor Create(ATreeView: TCustomDBTreeView);
end; TTreeIDNode = class(TTreeNode)
private
FID: string;
public
constructor Create(AOwner: TTreeNodes);
procedure Assign(Source: TPersistent); override;
property ID: string read FID write FID;
end; TCustomDBTreeView = class(TCustomTreeViewEx)
private
FOptions: TDBTreeOptions;
FUserOnEdited: TTVEditedEvent;
FTreeViewLink: TTreeViewLink;
FTableIDField: string;
FTableParentField: string;
FTableTextField: string;
FRootID: string;
FOnClosedLoop: TNotifyEvent;
FOnRootNotFound: TNotifyEvent;
FPrevState: TDataSetState;
FDelRootID: string;
FIDOfDeleted: string;
FState: TDBTreeViewStates;
FTVRecordList: TTVRecordList;
FReBuildTimer: Longint;
FOnGetNextID: TDBTVGetNextIDEvent;
function GetDataSource: TDataSource;
procedure SetDataSource(ADataSource: TDataSource);
procedure SetTableIDField(const Value: string);
procedure SetTableParentField(const Value: string);
procedure SetTableTextField(const Value: string);
function GetDataSet: TDataSet;
procedure SetRootID(ID: string);
procedure SetOptions(Value: TDBTreeOptions);
function CreateTVRecordList: TTVRecordList;
function NeedRebuild: Boolean;
procedure CreateTree(ParentNode: TTreeNode;
const AParent: string; TempRecordList: TTVRecordList);
function AddNewNodeFromDataset(
Node: TTreeNode; AsChild: Boolean): TTreeNode;
function GetDataSetIDNode: TTreeNode;
function GetID(AIndex: Integer): string;
function GetSelectedID: string;
procedure SelectID(const Value: string);
procedure IndexChanged;
procedure AfterEdit(Sender: TObject; Node: TTreeNode; var S: string);
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
procedure WMSetFocus(var Message: TMessage); message WM_SETFOCUS;
procedure WMTimer(var Msg: TWMTimer); message WM_TIMER;
protected
function CreateNode: TTreeNode; override;
function CanEdit(Node: TTreeNode): Boolean; override;
procedure Edit(const Item: TTVItem); override;
procedure Change(Node: TTreeNode); override;
procedure Expand(Node: TTreeNode); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
function DragAllowed(Node: TTreeNode): Boolean; override;
procedure ActiveChanged(Value: Boolean);
procedure DataChanged;
procedure RecordNumberChanged;
procedure RecordChanged(Field: TField);
procedure EditingChanged;
procedure DataSetBeforePost;
procedure DataSetAfterPost;
procedure DataSetAfterCancel;
procedure DataSetBeforeDelete;
procedure DataSetAfterDelete;
procedure DatasetRefreshed;
procedure ClosedLoop;
procedure RootNotFound;
function DataSetLocate(const ID: string): Boolean;
function GetDeleteQuestion(Node: TTreeNode): string; override;
function DoDelete(Node: TTreeNode): Boolean; override;
{ Called by procedure Insert, GetNewID has to calculate the ID of a
new record. It calls OnGetNextID: }
function GetNewID: string; virtual;
procedure BuildTree; virtual;
property TreeViewLink: TTreeViewLink read FTreeViewLink;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure RebuildTree;
procedure BuildTreeIfNeeded;
procedure Insert(AsChild: Boolean); override;
procedure Delete; override;
function MoveNode(Source, Destination: TTreeNode;
Mode: TNodeAttachMode): Boolean; override;
function FindTextID(const S: string; var ID: string;
TVFindTextOptions: TTVFindTextOptions): Boolean;
function TextIDList(const S: string;
TVFindTextOptions: TTVFindTextOptions): TStringList;
{ With GetExpanded you can save all Items[].Expanded in a string
(e.g. to save this in an INI-file)
to restore all Items[].Expanded with SetExpanded: }
function GetExpanded(Separator: Char): string;
procedure SetExpanded(const List: string; Separator: Char);
{ If not dtSynchronizeDataSet in Options, use this procedure to show
the current record of the dataset in the tree: }
procedure SynchronizeSelectedNodeToCurrentRecord;
{ If not dtSynchronizeDataSet in Options, use this procedure to move
the dataset to the selected node of the tree: }
procedure SynchronizeCurrentRecordToSelectedNode;
{ IsRootNode is true if the node has no parent: }
function IsRootNode(Node: TTreeNode): Boolean;
{ IsSingleRootNode is true if the node is the only one without parent: }
function IsSingleRootNode(Node: TTreeNode): Boolean;
{ To get the ID of a Node: }
function IDOfNode(Node: TTreeNode): string;
{ To get the Node that has the ID: }
function GetIDNode(const aID: string): TTreeNode;
{ DataSource.DataSet: }
property DataSet: TDataSet read GetDataSet;
{ The ID of the current selected node,
or set Selected with SelectedID := ID: }
property SelectedID: string read GetSelectedID write SelectID;
{ ID of Items[Index]. Index is 0 to Items.Count -1: }
property IDs[Index: Integer]: string read GetID;
{ possible published: }
property DataSource: TDataSource read GetDataSource write SetDataSource;
property TableIDField: string read FTableIDField write SetTableIDField;
property TableParentField: string
read FTableParentField write SetTableParentField;
property TableTextField: string
read FTableTextField write SetTableTextField;
property RootID: string read FRootID write SetRootID;
property OnClosedLoop: TNotifyEvent read FOnClosedLoop write FOnClosedLoop;
{ Called by the procedure Insert, OnGetNextID has to calculate the ID of
a new record. This is not needed if the type of the ID-field is ftAutoInc.
You have to override GetNewID if you hide the record with the highest
ID and the type of the ID-field is not ftAutoInc:
- DataSet is TQuery and TQuery.SQL uses 'WHERE ...'
- DataSet.MasterSource is set.
If you use a TQuery as Dataset, it is recommended to calculate the
new ID yourself.
If you set the new ID on Dataset.OnNewRecord, please use OnGetNextID
or your own GetNewID bacause the ID is needed before the Dataset gets
into insert-mode.}
property OnGetNextID: TDBTVGetNextIDEvent
read FOnGetNextID write FOnGetNextID;
property OnRootNotFound: TNotifyEvent
read FOnRootNotFound write FOnRootNotFound;
property Options: TDBTreeOptions read FOptions write SetOptions;
创建一个数据库,为简化程序,我只创建两个数据库字段,定义如下:
字段名 类型 长度
text c 10
longid c 6
LongID字段实际上由两段组成,每一段3位,LongID只能表示1000条记录。将LongID定义为索引字段,存为c:\testtree\tree.dbf。编辑该DBF文件,新建一条记录,Text字段设为TOP,LongID字段设为“000”(3个“0”前为三个空格)。 创建演示程序
在Form1上放置TreeView1、Table1、PopupMenu1、Edit1、Edit2。TreeView1的PopupMenu属性设为PopupMenu1;Table1的DataBaseName属性设为c:\testtree,TableName属性设为tree.dbf,IndexFieldNames属性设为LongID;为PopupMenu1加选单项Add1和Del1,Caption分别为Add和Del;Edit1用来输入新节点的Text属性值,Edit2用来输入新节点的3位ID号。存为c:\testtree\treeunit.pas和c:\testtree\testtree.dpr。
在treeunit.pas的Type关键字后加入一行:Pstr:^string;{Pstr为字符串指针} 为Form1的OnCreate事件添加代码: procedure TForm1.FormCreate(Sender: TObject); var p:Pstr;Node:TTreeNode; begin with Table1,Treeview1 do begin open; first; new(p);{为指针p分配内存} p^:=FieldByName(′LongID′).AsString; Node:=Items.AddChildObject(nil,FieldByName(′Text′).AsString,p); if HasSubInDbf(Node) then Items.AddChildObject(Node,′ ′,nil);{有子节点则加一个空子节点} end; end; HasSubInDbf为自定义函数,自变量为Node,检查节点Node有无子节点,有则返回True,反之返回False,并在TForm1的类定义里加入原型声明(其它自定义函数的原型也在TForm1的类定义里声明,不另作解释),函数代码如下: function TForm1.HasSubInDbf(Node:TTreeNode):Boolean; begin with Table1 do begin Table1.FindNearest([copy(Pstr(Node.Data)^,4,3)+′000′]); result:=copy(FieldByName(′LongID′).AsString,1,3)=copy(Pstr(Node.Data)^,4,3);{如数据库里当前记录的LongID字段内容的前3位和节点Node的Data的后3位相同,则Node应该有子节点} end; end; 为TreeView1控件的OnDeletion事件添加代码,需要指出的是,不仅调用Delete方法可以触发OnDeletion事件,而且当树控件本身被释放前,也触发OnDeletion事件,所以,在此处加入dispose(node.data)会很“安全”: procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode); begin Dispose(Node.Data);{释放节点数据内存} end; 为Add1选单项的OnClick事件添加代码如下: procedure TForm1.Add1Click(Sender: TObject); var p:pstr;Tmpstr:string;i:integer; begin try StrToInt(Edit2.Text); Tmpstr:=Edit2.Text;{注:在实用中,必须用更好的方法来产生ID} except; ShowMessage(′重新输入Edit2的内容′); abort; end; with TreeView1 do begin new(p); p^:=copy(Pstr(Selected.Data)^,4,3)+TmpStr; Items.AddChildObject(Selected,Edit1.Text,p); end; with Table1 do{ 在数据库里添加记录 } begin Append; FieldByName(′Text′).AsString:=Edit1.text; FieldByName(′LongID′).AsString:=p^; Post; end; TmpStr:=inttostr(strtoint(TmpStr)+1); for i:=length(TmpStr) to 2 do TmpStr:=′0′+TmpStr; Edit2.Text:=TmpStr; end; 为Del1菜单项的OnClick事件添加代码如下: procedure TForm1.Del1Click(Sender: TObject); var DelList:TStringList;LongID,NSubLongID:string; begin DelList:=TStringList.create; DelList.Sorted:=True; DelList.Add(Pstr(TreeView1.Selected.Data)^); while DelList.Count>0 do begin LongID:=DelList.Strings[0]; DelList.Delete(0); Table1.SetKey; Table1.FieldByName(′LongID′).AsString:=LongID; if Table1.GotoKey then Table1.Delete; if HasSubInDbf(TreeView1.Selected) then begin NSubLongID:=Table1.FieldByName(′LongID′).AsString; while (copy(NSubLongID,1,3)=copy(LongID,4,3))and(not Table1.Eof) do begin dellist.Add(NSubLongId); Table1.Next; NSubLongId:=Table1.FieldByName(′LongID′).AsString; end; end; end; DelList.Free; TreeView1.Items.Delete(TreeView1.Selected); end; 为TreeView1的OnExpanding事件添加代码: procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var TmpNode:TTreeNode;NSubLongID:String;p:Pstr;bm:TBookMark; begin with Table1,TreeView1 do begin Items.BeginUpdate; SetKey; FieldByName(′LongID′).AsString:=Pstr(Node.Data)^; if not GotoKey then Items.Delete(Node) else begin TmpNode:=Node.GetFirstChild; if (TmpNode.Text=′ ′)and(TmpNode.Data=nil) then begin TmpNode.Delete; if HasSubInDbf(Node) then begin NSubLongID:=FieldByName(′LongID′).AsString; while (copy(NSubLongID,1,3)=copy(Pstr(Node.Data)^,4,3))and(not Eof) do begin new(p); p^:=FieldByName(′LongID′).AsString; bm:=GetBookMark; TmpNode:=Items.AddChildObject(Node,FieldByName(′Text′).AsString,p); if HasSubInDbf(TmpNode) then Items.AddChildObject(TmpNode,′ ′,nil); GotoBookMark(bm); FreeBookMark(bm); Next; NSubLongId:=FieldByName(′LongID′).AsString; end; end; end; end; Items.EndUpdate; end; end; 以上简要谈了谈数据库的树状显示的基本方法,另外,编辑树上节点的Text属性的同时对数据库进行修改、同一数据库在多用户同时操作时数据库以及树的一致性、树上节点的拷贝与复制等就不再赘述,读者可自行完善。本文程序在Dlphi4.0、Windows 98下调试通过
////////////////////////////////Begin 树文本
食品
主食品
大米
小麦
玉米
副食品
酱油
食盐
日用品
牙刷
洗衣粉
////////////////////////////////End 树文本用一个文本字段,一条记录来处理,方便、快速,为什么不!
tbTree
( TreeId, (节点编号)
TreeName, (节点名称)
UpperTreeId (上级节点编号)
)
UpperTreeId=NULL 表示根节点
TTreeView.SaveToFile()
TTreeView.LoadFromFile()
TTreeView.SaveToStream()
TTreeView.LoadFromStream()
mTreeView: TTreeView; mTreeNode: TTreeNode;
mParentText: string): Boolean;
const
cFieldNameParent = 'UpperTreeId';
cFieldNameTreeName = 'TreeName';
cFieldNameTreeId = 'TreeId';
cFieldNames = cFieldNameParent + ';' + cFieldNameTreeName + ';' + cFieldNameTreeId;
var
vTreeNode: TTreeNode;
vFieldValues: Variant;
begin
Result := False;
if not Assigned(mDataSet) then Exit;
if not Assigned(mTreeView) then Exit;
if not mDataSet.Active then Exit;
mDataSet.Filtered := False;
mDataSet.Filter := Format('%s=%s', [cFieldNameParent, QuotedStr(mParentText)]);
mDataSet.Filtered := True;
if mDataSet.RecordCount = 0 then Exit;
mDataSet.First;
while not mDataSet.Eof do begin
vTreeNode := mTreeView.Items.AddChild(mTreeNode,
mDataSet.FieldByName(cFieldNameTreeName).AsString);
vFieldValues := mDataSet[cFieldNames];
DataSetToTreeNode(mDataSet, mTreeView, vTreeNode,
mDataSet.FieldByName(cFieldNameTreeID).AsString);
///////Begin 恢复位置
mDataSet.Filtered := False;
mDataSet.Filter := Format('%s=%s', [cFieldNameParent, QuotedStr(mParentText)]);
mDataSet.Filtered := True;
mDataSet.Locate(cFieldNames, vFieldValues, []);
///////End 恢复位置
mDataSet.Next;
end;
Result := True;
end;procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.Items.Clear;
DataSetToTreeNode(Table1, TreeView1, nil, 'NULL');
end;
table1 (NodeId,NodeName,UpperNodeId)
( UpperNodeId=0 为根接点 )测试代码:
treeview1.Items.Clear;
DataSetToTreeNode(ClientDataSet1, treeview1, nil, '0');(注:我用 TClientDataSet 连数据库)我只改你的函数的前面部分):
cFieldNameParent = 'UperGroupId';
cFieldNameTreeName = 'GroupName';
cFieldNameTreeId = 'GroupId'
请您看看我写的代码,我怎么觉得好像没错,可是偏偏就是错了,不能把节点全部搜索出来,第一层能正确地全部搜索出来,可是第二层只能搜索出第一个节点。第二层以后就更别说了。//表结构:
table1
( NodeId, --节点编号
NodeName, --节点名称
UpperNodeId --上级节点编号
)
// 寻找第一层
// TQuery 的创建省略
procedure ListAllNode(TreeView:TTreeview);
var
qry : TQuery;
Temp : TTreeNode;
begin
qry.SQL.Add('select NodeId,NodeName from table1 where UpperNodeId=0');
qry.ExecSQL;
while not qry.Eof do
begin
Temp := TreeView.Items.Add(nil,qry.FieldAsString('NodeId')); // 增加根节点
GetNext(Temp,qry.FieldAsString('NodeId'));
qry.Next;
end;
end;//递归寻找下一个节点
procedure GetNext(Node : TTreeNode;Parent : string);
var
qry : TQuery;
Temp : TTreeNode;
begin
qry.SQL.Text := 'select nodeid,f1 from table1 where ParentNodeId='+parent;
qry.open;
while not qry.Eof do
begin
Temp := TreeView.Items.AddChild(Node,qry.FieldAsString('NodeName'));
GetNext(Temp,qry.FieldAsString('NodeId'));
qry.Next;
end;
end;//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
TreeView1.Items.Clear;
ListAllNode(treeview1);
end;
ID:Integer;
pID:integer;
Name:String;
Parent:TmyRecord;
end;
TMyList=class(TList)
function Find(ID:integer):TmyRecord;
function FindParent(pID:integer):TmyRecord;
function Add(Obj:TmyRecord):integer;overload;
procedure Delete(index:integer);
end;
实现函数
:
procedure TUnitForm.CreateTree(Tree:TTreeView;Data:TMyList);
{创建一个结点的子树}
procedure CreateSubTree(Node:TTreeNode;Data:TmyList);
var
subNode:TTreeNode;
// subNodeN:TTreeNode;
i:integer;
begin
if Data.Count >0 then
begin
i:=0;
while i<= Data.Count -1 do
begin
if TmyRecord(Data.Items[i]).pid=TmyRecord(Node.Data).ID then
begin SubNode:=Tree.Items.AddChildObject(Node,TmyRecord(Data.Items[i]).Name,Data.Items[i]);
SubNode.Data:=Data.Items[i];
Data.Delete(i);
CreateSubTree(SubNode,Data);
continue;
end;
inc(i);
end;
end; end;
var
Node:TTreeNode;
//DataSet:TClientDataSet;
// i:integer;
// flag:boolean;
obj:TMyrecord;
begin
// DataSet:=nil;
//Node:=nil;
// flag:=false;
{ DataSet:=TClientDataSet.Create(nil);
DataSet.Data:=Data;
while Not DataSet.Eof do
begin
if DataSet.FieldByName('UpTreeID').AsInteger=0 then
begin
flag:=true;
break;
end;
end; if flag then
begin
// Node:=TMyNode.Create( Tree.Items.AddFirst(nil,DataSet.FieldByName('Name').AsString)) ;
// Node.ID:=DataSet.FieldByName('ID').AsInteger;
DataSet.First;
CreateSubTree(Node,nil); end;
DataSet.Free; }
obj:=Data.Find(1);
Tree.Items.Clear;
if Obj<>nil then
begin
Node:=Tree.Items.AddObjectFirst(nil,Obj.Name,Obj);
CreateSubTree(Node,Data);
end;
end;
我过去写的一段
Node的Data中存储TMyrecord对象
节点名称 节点编号 上次节点号
aaa 1 -1
bbb 2 1
ccc 3 3只有-1埮在节点编号中出现,那么把aaa读出,然后类似展开
// TQuery 的创建省略
procedure ListAllNode(TreeView:TTreeview);
var
qry : TQuery;
Temp : TTreeNode;
begin
//#你的qry还没有分配资源qry := TQuery.Create; qry.DatabaseName := '???'; qry.SQL.Add('select NodeId,NodeName from table1 where UpperNodeId=0');
qry.ExecSQL; //#qry.Open;
while not qry.Eof do
begin
Temp := TreeView.Items.Add(nil,qry.FieldAsString('NodeId')); // 增加根节点
GetNext(Temp,qry.FieldAsString('NodeId'));
qry.Next;
end;
end;
//#qry.Open;
这两点我改了,可是还是不行,我发现问题好象在:
GetNext(Temp,qry.FieldAsString('NodeId'));
qry.Next;比如对A进行递归查找时,
A
┝x
┝y
B
当查找x的子节点为空后,按理应该返回到y,可是却返回到了B这里了,真是奇怪,您说应该如何修改呢?
( TreeId, (节点编号)
TreeName, (节点名称)
UpperTreeId (上级节点编号)
)分别是什么类型?我提供的函数自己已经调试通过
你把完整代码我看
自己多多调试调试
一定是什么细节没有考虑
一步步的来
首先确定自己通过的模拟数据是否正确
其次确定思路是否正确
之后确定每一步是否正确如果你不能解决,请发一个可以调试的例子发给我看看
[email protected]
明天就放假了,最好今天中午之前
begin
close;
sql.Clear;
sql.Add('select * from t_gcsm order by gcbh');
prepared;
open;
first;
if recordcount<>0 then
begin
for i:=1 to recordcount do
begin
gcm:=trim(fieldbyname('gcm').asstring);
treeNodeparent:=TreeView1.Items.Add(nil,gcm);
with rationdatamodule.demladoquery1 do
begin
close;
sql.clear;
sql.Add('select * from t_deml where gcm=:gcm');
parameters.ParamByName('gcm').value:=gcm;
prepared;
open;
first;
if recordcount<>0 then
begin
for j:=1 to recordcount do
begin
xjmc:=trim(fieldbyname('xjmc').asstring);
treeNodechild:=TreeView1.Items.Addchild(treeNodeparent,xjmc);
next;
end;
end;
end;
next;
end;
end;
end;
改一下字段,如果不行的话,你打我,呵呵
非常感谢大家的帮助,谢谢大家.
特别谢谢zswang(伴水)(* pascal→c *),我一定谨遵您的教诲。