線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:4489
推到 Plurk!
推到 Facebook!

Treeview的問題

答題得分者是:timhuang
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-04-03 13:42:31 IP:218.14.xxx.xxx 未訂閱
請教個大大,我想把資料庫的Matetype(數據表)里數據讀到Treeview里,Matetype的結構為:Typecode(類別號),type(類別名稱),Parentcode(有無父節點),Havechild(有無子節點)。並且數據表里有三類數據(大類,中類,小類)我想用遞歸運算,但是只能出現一個節點,如下: 產品分類 ---酒 ---白酒 我的程式如下: procedure TForm1.viewtree(i:Integer); begin Treeview1.Items.AddObject(nil,'產品分類',nil); with ADOQuery1 do begin Close; SQL.Clear; SQL.Add('SELECT * FROM MateType WHERE Parentcode=''' inttostr(i) ''''); Open; First; while not eof do begin if FieldByName('Parentcode').AsInteger=i then begin Treeview1.Items.AddChildObject(Treeview1.items[i],FieldByName('Type').AsString,pchar(FieldByName('Typecode').AsString));//line12 if FieldByName('Havechild').AsInteger<>0 then begin viewtree(FieldByName('Typecode').AsInteger); end; end; next; end; end; end; 我知道是程式的line12這行的Treeview1.items[i]的問題,但是不知道怎麽表示才能使程式有效運行。 我是菜鳥,請多指點! 不勝感激!!
andersonhsieh
版主


發表:33
回覆:531
積分:439
註冊:2002-06-10

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-04-03 14:06:34 IP:211.20.xxx.xxx 未訂閱
引言: 請教個大大,我想把資料庫的Matetype(數據表)里數據讀到Treeview里,Matetype的結構為:Typecode(類別號),type(類別名稱),Parentcode(有無父節點),Havechild(有無子節點)。並且數據表里有三類數據(大類,中類,小類)我想用遞歸運算,但是只能出現一個節點,如下: 產品分類 ---酒 ---白酒 我的程式如下: procedure TForm1.viewtree(i:Integer); begin Treeview1.Items.AddObject(nil,'產品分類',nil); with ADOQuery1 do begin Close; SQL.Clear; SQL.Add('SELECT * FROM MateType WHERE Parentcode=''' inttostr(i) ''''); Open; First; while not eof do begin if FieldByName('Parentcode').AsInteger=i then begin Treeview1.Items.AddChildObject(Treeview1.items[i],FieldByName('Type').AsString,pchar(FieldByName('Typecode').AsString));//line12 if FieldByName('Havechild').AsInteger<>0 then begin viewtree(FieldByName('Typecode').AsInteger); end; end; next; end; end; end; 我知道是程式的line12這行的Treeview1.items[i]的問題,但是不知道怎麽表示才能使程式有效運行。 我是菜鳥,請多指點! 不勝感激!!
可否將將你的資料表的結構post上來..... @@~~飛翔在天際的精靈~~@@
------
@@~~飛翔在天際的精靈~~@@
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#3 引用回覆 回覆 發表時間:2003-04-03 14:44:24 IP:218.14.xxx.xxx 未訂閱
表MateType結構: Key-----Name----------DataType----Size----Null 是------TypeCode------decimal-----9-------不可 //商品類別碼 --------Type----------char--------20------不可 //商品類別名稱 --------ParentCode----int---------4-------可 //父類別碼 --------Havechild-----int---------4-------可 //有無子類 其它字段沒有什麽用的。 比如説: 1(類別碼)----酒(名稱)----0(父類為無)----1(有子類) 2 ------------煙 ----------0 --------------1 101-----------白酒---------1---------------1 10101---------米酒---------101-------------0(無子類) 10102---------曲酒---------101 ------------0 數據就是這樣的,我想酒和煙為大類,酒類下有白酒,白酒下有子節點米酒和曲酒,我的意思就是這樣。 謝謝! 我是菜鳥,請多指點! 不勝感激!! 發表人 - yezi_ji 於 2003/04/04 09:12:35
timhuang
尊榮會員


發表:78
回覆:1815
積分:1608
註冊:2002-07-15

發送簡訊給我
#4 引用回覆 回覆 發表時間:2003-04-04 13:56:29 IP:211.76.xxx.xxx 未訂閱
Hi, 約略的寫法如下:    
procedure TForm1.Button2Click(Sender: TObject);
begin
  AddChildNode(  nil, 0, 'root' );
end;    function TForm1.AddChildNode(node: TTreeNode; typecode: integer; stype: string): TTreeNode;
var
  Qry: TQuery;
begin
  Result := TreeView1.Items.AddChild(node, stype);
  Qry:= TQuery.Create(self);
  Qry.DatabaseName := Database1.DatabaseName ;
  Qry.Close;
  Qry.SQL.Clear;
  Qry.SQL.Text := 'SELECT * FROM MateType WHERE ParentCode = '   IntToStr(typecode);
  Qry.Open;
  While not Qry.Eof do
  begin
    AddChildNode(Result, Qry.FieldByName('typecode').AsInteger, Qry.FieldByName('type').AsString);
    Qry.Next;
  end;
  Qry.Free;
end;
其中的 AddChildNode 是 遞迴 的呼叫. 請自行試試!!
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#5 引用回覆 回覆 發表時間:2003-04-04 16:23:33 IP:218.14.xxx.xxx 未訂閱
非常感謝,但是我想添加全部的節點,你的程序在每一層只能添加一個節點,而我想在酒下有多個子節點,在子節點下有有多個子節點。可不可以說說一下這個程序的大概想法,主要的是遞歸在什麽點做,有些什麽要求?謝謝! 我是菜鳥,請多指點! 不勝感激!!
Justmade
版主


發表:94
回覆:1934
積分:2030
註冊:2003-03-12

發送簡訊給我
#6 引用回覆 回覆 發表時間:2003-04-04 16:55:47 IP:218.16.xxx.xxx 未訂閱
他的程式就是無限層每層無限子節點啦,你有沒有真的試過。
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#7 引用回覆 回覆 發表時間:2003-04-04 17:42:53 IP:218.14.xxx.xxx 未訂閱
試過了,就是我是用ADOQuery做的查詢,其他的一模一樣,但是只能做一層,就是這樣的:    產品資料 ----酒 --------白酒 -------------米酒 後面就沒有了 我想做的: 產品資料 ------酒 ----------白酒 ---------------米酒 ---------------曲酒 ------煙 ----------國產煙 ---------------硬盒 ---------------軟盒 ----------進口煙 等等. 有了一個方向,但是我還是不知怎麽做。 我是菜鳥,請多指點! 不勝感激!!
Justmade
版主


發表:94
回覆:1934
積分:2030
註冊:2003-03-12

發送簡訊給我
#8 引用回覆 回覆 發表時間:2003-04-04 18:50:54 IP:218.16.xxx.xxx 未訂閱
不同啦,因為每一層一定要有一個獨立的 Query (ADOQuery 亦可) 所以你看他的是即時動態 create 一個新的而不是像你的只得一個。 你只得一個 ADOQuery 的問題是當你做第一層第一個時還未做第二個前,會做第一層第一個的子代,這時你便 Close 了正在做第一層的 Query 另入新 Query 當然每層只得一個啦。    你都沒照別人給你的方法,自己改掉了架構然後說別人的不成,真是的
timhuang
尊榮會員


發表:78
回覆:1815
積分:1608
註冊:2002-07-15

發送簡訊給我
#9 引用回覆 回覆 發表時間:2003-04-04 21:34:47 IP:61.221.xxx.xxx 未訂閱
Hi, 若是改為 ADO 的話, 寫法如下: 記得利用 TADOConnection 與資料庫建立連結.    
procedure TForm1.Button2Click(Sender: TObject);
begin
  AddChildNode(  nil, 0, 'root' );
end;    function TForm1.AddChildNode(node: TTreeNode; typecode: integer; stype: string): TTreeNode;
var
  Qry: ADOQuery;
begin
  Result := TreeView1.Items.AddChild(node, stype);
  Qry:= ADOQuery.Create(self);
  Qry.Connection := ADOConnection1;
  Qry.Close;
  Qry.SQL.Clear;
  Qry.SQL.Text := 'SELECT * FROM MateType WHERE ParentCode = '   IntToStr(typecode);
  Qry.Open;
  While not Qry.Eof do
  begin
    AddChildNode(Result, Qry.FieldByName('typecode').AsInteger, Qry.FieldByName('type').AsString);
    Qry.Next;
  end;
  Qry.Free;
end;
其中的 紅色部分為修改為 ADO 的版本, 請再自行試試!!
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#10 引用回覆 回覆 發表時間:2003-04-05 09:04:28 IP:218.14.xxx.xxx 未訂閱
非常感謝兩位大大,搞定了,不過 
引言: Qry: ADOQuery; 應改為TADOQUery
但是現在又有個問題,就是速度太慢,因爲我有好多節點和子節點,大概要花15秒它才出現,看了一些資料,說可以先只讀取一到兩個子節點的内容,等到用戶選擇時再顯示其他内容,但是我不知到怎麽做,請幫忙。 在這再次感謝兩位,感謝你們的幫忙。 我是菜鳥,請多指點! 不勝感激!!
Justmade
版主


發表:94
回覆:1934
積分:2030
註冊:2003-03-12

發送簡訊給我
#11 引用回覆 回覆 發表時間:2003-04-05 10:00:17 IP:218.16.xxx.xxx 未訂閱
這個就比較複雜了,因為你若不一次 Load 所有資料而等使用者按某一個節點才 Load 該等點的子代,先決條件是你可以在使用者按讓節點時找到該Typecode。可是你現在的做法只是將 Type 記住並沒有將 TypeCode 記住。 有兩個做法: 1. 若你的 Type (類別名稱) 是完全不會重覆的,那你可以用 Node.Text 來 Lookup 回 TypeCode 2. 使用 Node.Data 來儲存資料,因於這是 pointer 來的,所以你要用 pointer 形態如 PChar 等 記得要將程式改為不要不斷重覆呼叫自己。
timhuang
尊榮會員


發表:78
回覆:1815
積分:1608
註冊:2002-07-15

發送簡訊給我
#12 引用回覆 回覆 發表時間:2003-04-05 13:20:07 IP:61.221.xxx.xxx 未訂閱
引言: 但是現在又有個問題,就是速度太慢,因?我有好多節點和子節點,大概要花15秒它才出現,看了一些資料,說可以先只讀取一到兩個子節點的?容,等到用戶選擇時再顯示其他?容,但是我不知到怎?做,請幫忙。 在這再次感謝兩位,感謝你們的幫忙。
修改一下程式, 將原來的全部展開改為只往下展開兩層, 另外利用 node.data 來記錄該節點的下兩層是否已展開過及該節點的 typecode, 以利後續的 onExpanding event 的檢驗來進行動態的往下展開. 為什麼使用往下展兩層, 原因是使有子節點的 node 前面都有+號存在, 以免無法觸發 onExpanding event! 程式碼如下:
    // 在 type 段加入
type
  TmyData = record
    expanded: boolean;
    typecode: integer;
  end;
  pNodeData = ^TmyData;
  
// .....
//
//
procedure TForm1.Button2Click(Sender: TObject);
begin
  AddChildNode(  nil, 0, 'root', 2 );
end;    //
//
//
function TForm1.AddChildNode(node: TTreeNode; typecode: integer; stype: string; ideep: integer): TTreeNode;
var
  Qry: TADOQuery;
  p: pNodeData;
begin
  if ideep <= 0 then
  begin
    Result := nil;
    exit;
  end;
  New(p);
  p^.typecode := typecode;
  p^.expanded := false;
  Result := TreeView1.Items.AddChildObject(node, stype, p);
  Qry:= TADOQuery.Create(self);
  Qry.Connection := ADOConnection1;
  Qry.Close;
  Qry.SQL.Clear;
  Qry.SQL.Text := 'SELECT * FROM MateType WHERE ParentCode = ' + IntToStr(typecode);
  Qry.Open;
  While not Qry.Eof do
  begin
    AddChildNode(Result, Qry.FieldByName('typecode').AsInteger, Qry.FieldByName('type').AsString, ideep - 1);
    Qry.Next;
  end;
  Qry.Free;
end;    //
//
//
function TForm1.AddChildNodeSub(node: TTreeNode; typecode,
  ideep: integer): TTreeNode;
var
  Qry: TADOQuery;
begin
  Qry:= TADOQuery.Create(self);
  Qry.Connection := ADOConnection1;
  Qry.Close;
  Qry.SQL.Clear;
  Qry.SQL.Text := 'SELECT * FROM MateType WHERE ParentCode = ' + IntToStr(typecode);
  Qry.Open;
  While not Qry.Eof do
  begin
    AddChildNode(node, Qry.FieldByName('typecode').AsInteger, Qry.FieldByName('type').AsString, ideep - 1);
    Qry.Next;
  end;
  Qry.Free;
end;    //
//
//
procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);
var
  i: integer;
begin
//  showmessage( inttostr( pNodeData(Node.Data)^.typecode ));
  if not pNodeData(Node.Data)^.expanded then  // 檢驗是否已展開過下兩層
  begin
    pNodeData(Node.Data)^.expanded := true;
    for i:= 0 to node.Count-1 do
      AddChildNodeSub(  node.Item[i], pNodeData(node.Item[i].Data)^.typecode, 2 );
  end;    end;    
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#13 引用回覆 回覆 發表時間:2003-04-05 14:22:48 IP:218.14.xxx.xxx 未訂閱
非常感謝timhuang大大,你的程式很好,我可能要消化一段時間,我自己也寫了一些,但是只能做出一點。 在這再次感謝timhuang和Justmade兩位大大。 感謝你們的指點。 我是菜鳥,請多指點! 不勝感激!!
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#14 引用回覆 回覆 發表時間:2003-04-05 15:15:09 IP:218.14.xxx.xxx 未訂閱
請問timhuang:AddChildNodeSub這個函數是幹什麽用的?Node.Data裏面到底是什麽内容,怎麽使用的? 謝謝! 我是菜鳥,請多指點! 不勝感激!!
timhuang
尊榮會員


發表:78
回覆:1815
積分:1608
註冊:2002-07-15

發送簡訊給我
#15 引用回覆 回覆 發表時間:2003-04-05 16:53:02 IP:61.221.xxx.xxx 未訂閱
引言: 請問timhuang:AddChildNodeSub這個函數是幹什麽用的?Node.Data裏面到底是什麽内容,怎麽使用的? 謝謝!
AddChildNodeSub 的用途與原來 AddChildNode 的不同處在於 AddChildNode 會新增傳入的節點, 而 AddChildNodeSub 則不會加入傳入的節點, 僅處理其子節點而已, 為避免程式過度複雜, 所以將 AddChildNodeSub 由 AddChildNode 分離出來, 其實是可以共用的, 只是要注意避開不產生傳入節點的產生即可!! 至於 Node.Data 本身是一個 pointer , 其目的在於儲存資料, 為什麼要利用 pointer 來作為儲存資料的 member data 呢? 因為 pointer 可以指向自定的任何型別的資料, 如此例就是利用 TmyData 這個資料來儲存著該節點的兩個資料, typecode, expanded, 利用 record 來作為 Node.Data 指向的資料, 如此一來, 任何一個節點的資訊都可以放在 Node.Data 所指向的資料了. 這是 Node.Data 的主要功能, 你也可以想想看要放什麼資料進去都可以. 用法就如同我舉的例子一般, 宣告一個 record (TmyData), 裡面放著你要存的資料, 接著宣告該資料的指標 pNodeData = ^TmyData, 其意義是說 pNodeData 是一種指向 TmyData 的指標 (pointer), 記得是指標, 不是實體資料, 所以在使用前要先使用 New 來配置記憶體給該 TmyData, 宣告 p 為 pNodeData, 再利用 New(p) 就可以配置記憶體, 使 p 指標所指向的位置就是該記憶體的位置, 使用其資料的方式就是 p^.typecode, 及 p^.expanded, 接下來我將原來的 AddChild 改為 AddChildObject 其不同處在於: function AddChild(Node: TTreeNode; const S: string): TTreeNode; function AddChildObject(Node: TTreeNode; const S: string; Ptr: Pointer): TTreeNode; AddChildObject 多給了 pointer 進去, 所以 AddChild 後再設定 Data 與直接給定其 Data 是一樣的, 以下的程式碼用來說明這兩種的結果是相同的:
  // case 1
  p: pNodeData;  
  New(p);
  Result := xxx.AddChild(n1, '1234');
  Result.Data := p;
  
  // case 2
  p: pNodeData;
  New(p)
  Result := xxx.AddChildObject(n1, '1234', p);  
其結果都是新增節點後, 並將 p 設定給 Result.Data 之中. 這樣說明就應該比較清楚了!!
yezi_ji
一般會員


發表:16
回覆:31
積分:9
註冊:2003-03-22

發送簡訊給我
#16 引用回覆 回覆 發表時間:2003-04-05 17:45:37 IP:218.14.xxx.xxx 未訂閱
非常感謝,我明白了。聽君一席話,勝讀十年書。謝謝! 我是菜鳥,請多指點! 不勝感激!!
superm
一般會員


發表:1
回覆:2
積分:0
註冊:2003-05-11

發送簡訊給我
#17 引用回覆 回覆 發表時間:2003-09-07 09:13:52 IP:61.64.xxx.xxx 未訂閱
我是初學者 感謝.我找了好久終於由這篇文章成功完成了dbtree的結構 可是最近有一些問題.可取得刪除的位置.可是刪資料時都是只有一筆.要如何才可以批次刪 還請各位高手指點 程式碼如下: procedure Tform1.delClick(Sender: TObject); var node:TTreenode; begin delnode(Treeview1.Selected); end; procedure Tform1.addClick(Sender: TObject); var Qry: TADOQuery; node : TTreeNode; index:string; begin node:=treeview1.selected; treeview1.Items.addchild(node,'未命名'); index:= inttostr(pNodeData(Node.Data)^.typecode); with adotable1 do// 在資料庫裏添加記錄 begin append; fieldbyname('type').asstring:='未命名'; fieldbyname('Parentcode').asstring:=index; post; end; end; function TForm1.delnode(node1:TTreenode):TTreenode; var childnode:TTreenode; index:string; Qry: TADOQuery; begin childnode:=node1.GetLastChild; //按倒序取得子點,因刪除節點,列表會產生變化 while childnode<>nil do childnode:=delnode(childnode); //如子項不為空,進行遞迴調用 index:=inttostr(pNodeData(node1.data).typecode); //得該節點對應指標在數據庫刪除相應指標; showmessage(index);{可得到刪除點的資料,可是資料庫不會寫,用下面的方法只可刪一節點就有錯誤} result:=node1.parent.GetPrevChild(node1); //定位到刪除點的上一節點節點 node1.delete; //刪除樹節點 Qry:= TADOQuery.Create(self); Qry.Connection := ADOConn; Qry.Close; Qry.SQL.Clear; Qry.SQL.Text := 'delete FROM MateType WHERE typeCode = ' index; Qry.Open; Qry.Next; Qry.Free; end; 請高手指導.感激不盡
airomeo
一般會員


發表:1
回覆:4
積分:1
註冊:2005-03-08

發送簡訊給我
#18 引用回覆 回覆 發表時間:2005-08-10 13:39:22 IP:203.66.xxx.xxx 未訂閱
請問一下,若將adoquery改為tclientdataset那要如何修改呢...因為我遇到的困難也是只有展一階的資料..    而tclientdataset是接middle-tier的資料    ps.我是使用3-tier 架構去實作...先行謝過各位了    
引言: Hi, 若是改為 ADO 的話, 寫法如下: 記得利用 TADOConnection 與資料庫建立連結.
procedure TForm1.Button2Click(Sender: TObject);
begin
  AddChildNode(  nil, 0, 'root' );
end;    function TForm1.AddChildNode(node: TTreeNode; typecode: integer; stype: string): TTreeNode;
var
  Qry: ADOQuery;
begin
  Result := TreeView1.Items.AddChild(node, stype);
  Qry:= ADOQuery.Create(self);
  Qry.Connection := ADOConnection1;
  Qry.Close;
  Qry.SQL.Clear;
  Qry.SQL.Text := 'SELECT * FROM MateType WHERE ParentCode = '   IntToStr(typecode);
  Qry.Open;
  While not Qry.Eof do
  begin
    AddChildNode(Result, Qry.FieldByName('typecode').AsInteger, Qry.FieldByName('type').AsString);
    Qry.Next;
  end;
  Qry.Free;
end;
其中的 紅色部分為修改為 ADO 的版本, 請再自行試試!!
esp_pzj
初階會員


發表:32
回覆:70
積分:40
註冊:2007-02-09

發送簡訊給我
#19 引用回覆 回覆 發表時間:2007-04-04 17:27:55 IP:210.60.xxx.xxx 訂閱
請問一下 如何把   TypeCode 值 設到每一個節點 的 SelectedIndex 內
------
學藝不精 謝多多指教
boss1215
一般會員


發表:6
回覆:10
積分:3
註冊:2009-03-09

發送簡訊給我
#20 引用回覆 回覆 發表時間:2009-03-23 15:40:19 IP:211.72.xxx.xxx 訂閱
..推薦!
------
Wa
系統時間:2024-05-08 2:54:30
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!