我有一个数据表(MDTasks),它有在树视图中表示的记录。每条记录都有一个唯一字段“ID”和一个字段“Parent_ID”,它指的是父项的ID。我正在尝试列出具有给定ID的记录的任何级别的所有子项。我有以下内容,它在每个级别获得第一个孩子,但不会回到任何级别的兄弟姐妹。我将不胜感激任何帮助。谢谢。
procedure TfmList.GetChildren(TaskID: integer);
var
iChildID: integer;
begin
with MDTasks do
begin
first;
while not EOF do
begin
if FieldByName('Parent_ID').AsInteger = TaskID then
begin
iChildID := FieldByName('ID').AsInteger;
Memo1.Lines.Add(IntToStr(iChildID));
GetChildren(iChildID);
end;
next;
end;
end;
end;
答案 0 :(得分:2)
如果我理解正确的话,下面的代码应该做你想要的。它使用ClientDataSet,因此我的答案是自包含的,因此可以轻松设置测试数据。
它使用对CloneCursor
的调用来对指定父节点ID的子节点进行递归搜索。
procedure TForm1.FormCreate(Sender: TObject);
begin
CDS1.CreateDataSet;
CDS1.InsertRecord([1, -1]);
CDS1.InsertRecord([2, -1]);
CDS1.InsertRecord([3, -1]);
CDS1.InsertRecord([4, 2]); // This and the following rows are all children of ID = 2
CDS1.InsertRecord([5, 2]);
CDS1.InsertRecord([6, 4]);
CDS1.InsertRecord([7, 4]);
CDS1.InsertRecord([8, 7]);
FindChildren(2);
end;
procedure TForm1.FindChildren(ParentID : Integer);
procedure FindChildrenInner(ParentID : Integer);
var
TempCDS : TClientDataSet;
ID : Integer;
begin
TempCDS := TClientDataSet.Create(Nil);
try
TempCDS.CloneCursor(CDS1, False, True);
TempCDS.First;
while not TempCDS.Eof do begin
if TempCDS.FieldByName('Parent_ID').AsInteger = ParentID then begin
ID := TempCDS.FieldByName('ID').AsInteger;
Memo1.Lines.Add(Format('ID: %d, Parent: %d', [ID, ParentID]));
FindChildrenInner(ID);
end;
TempCDS.Next;
end;
finally
TempCDS.Free;
end;
end;
begin
Memo1.Lines.BeginUpdate;
try
Memo1.Lines.Clear;
Assert(CDS1.Locate('ID', ParentID, []));
FindChildrenInner(ParentID);
finally
Memo1.Lines.EndUpdate;
end;
end;
答案 1 :(得分:1)
看一下代码,我会说它让所有的孩子都处于最后一级(不仅仅是第一个)。问题是你"分享"级别之间的数据集(MDTasks
) - 所以当最后一级迭代到表的末尾并返回给调用者时,while not EOF do
计算为false
(现在在父级别)并且循环结束,因此只有第一个孩子的水平高于最后一个孩子。
一种解决方案是完全迭代每个级别,将Parent_ID
s记录到本地数组中,然后使用该数组获得它下面的下一级。
答案 2 :(得分:1)
另一种解决方案可能是在表MDTasks中使用书签。让您的过程在开头设置书签并在结尾处返回此书签,以便MDTasks中的位置/光标不会从GetChildren更改。
答案 3 :(得分:-1)
你的问题是你正在针对相同的数据调用递归函数。您可以使用过滤,例如:
procedure GoGoGo (Id: integer);
var
OldFilter: string;
begin
OldFilter := Table.Filter;
Table.Filter := 'ParentId = ' + IntToStr (Id);
try
Table.First;
while not Table.Eof do begin
Memo.Lines.Add (Format('ID: %d, Parent: %d',
[Table.FieldByName ('I'd).AsInteger,
Table.FieldByName ('ParentID').AsInteger]));
GoGoGo (Table.FieldByName ('Id').AsInteger);
if Table.Locate ('Id', Id, []) then
Table.Next;
end;
finally
Table.Filter := OldFilter;
end;
end;
<...>
Memo.Lines.Clear;
Table.Filtered := TRUE;
Table.First;
GoGoGo (Table.FieldByName ('Id').AsInteger);
没有测试过这段代码,它可能包含错误,但希望很明显。使用相同的想法,你可以使用另一种方法,比如用动态生成的where子句创建新的TQuery等等。我不是因为在一个非常大的树中,在递归函数的每次迭代中创建新对象都不喜欢这样的东西。可能导致堆栈溢出。
gl那里;)