谁有转换table-->tree的算法(100分)

  • 主题发起人 主题发起人 jumphigh
  • 开始时间 开始时间
J

jumphigh

Unregistered / Unconfirmed
GUEST, unregistred user!
大概的界面可以做成象“资源管理器”差不多,左右分栏,左边显示一棵树,右边是一个表。
用到一个表 customer,包含如下字段
cust_id (自动编号)
cust_name 姓名
cust_sex 性别
cust_phone 联系电话
cust_rootid 根ID
cust_parentid 父ID

系统初始话后,左边只有一个节点,也就是根节点,以后所有在这个根下的节点的cust_rootid等于根的cust_id,节点显示cust_name+cust_phone
双击弹出对话框,显示详细信息(所有字段),并可以修改信息,
右键电击节点,弹出快捷菜单,显示以下几个项目
1,加节点
2,删节点
3,展开所有节点

点击加节点后,弹出对话框,填写信息(每个字段),按确认后增加该节点。节点显示cust_name+cust_phone。
以此类推,每个节点都可以增加节点。

右边栏就显示这个表的详细信息

最后要实现一个打印全部或从一个节点开始的全部树型(只要显示cust_name+cust_phone)就行了。



左边treeview的存储结构是很好解决
我就是用table实现了

可是我如果要在每次启动时,将table的数据调入treeview怎么办
应该是一个遍历的算法,可是我不会

另外我table是用sql sever建的

 
呵呵,我也想知道:)

不过好像电子书库就是用这样的算法,没仔细看它是特定的文件型数据库(很牛)
 
可以递归调用SQL。


不一定要在启动时装入全部数据吧?
可以在点击节点时再装入数据。这样
就不许要递归了。
 
具体的递归算法怎么写呢!另外sql我也不太熟呀!这是我第一次用sql编东西,还望多多指教
 
其实可以只做一次遍历就行了:
(1)从Table的第一条开始,向Treeview中加入根节点和子节点,同时保存根节点到
Buffer;
(2)加入下一个节点是判断Buffer是否有根节点,如果有,直接加入子节点,
否则,加入根节点和子节点;
(3)重复(2)直至结束为止。
对不起,就说到这里,下班了,有事给我写信!


 
什么叫做保存到buffer呀
 
愿闻其详。
 
//一个比较完整的实现,最大的缺点在于速度较慢,因为每次都要找是否有子节点;
//可能的话这个做为一个字段,以此做判断就好多了。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, ComCtrls, StdCtrls;

type
TForm1 = class(TForm)
Query: TADOQuery;
TreeView1: TTreeView;
Button1: TButton;
ADOConnection1: TADOConnection;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure loadTreeFromDB(treNodes:TTreeNodes;node:TTreeNode;id:Longint);
end;

TrecInfo= record
id:Longint;
customer:string;
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure Tform1.loadTreeFromDB(treNodes:TTreeNodes;node:TTreeNode;id:Longint);
var
nodeW:TTreeNode;
recInfo:^TrecInfo;
rs:_recordset;
begin

// if cust_parentid=0 then is root

query.active:=false;
Query.sql.clear;
query.sql.add('select * from customer where cust_parentid='+inttostr(id));
query.active:=True;

if query.RecordCount>0 then
begin
rs:=query.Recordset.Clone(3);
rs.MoveFirst;
while not rs.EOF do
begin
new(recInfo);
recInfo^.id:=rs.Fields['id'].Value;
recInfo^.customer:=rs.Fields['cust_name'].value;
nodew:=treNodes.AddChildObject(node,recInfo^.customer,recInfo);
loadTreeFromDB(treNodes,nodew,recInfo^.id);
rs.MoveNext;
end;
rs.Close;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
treeview1.Items.BeginUpdate;
loadTreeFromDB(treeview1.Items,nil,0);
treeview1.Items.EndUpdate;
end;

end.
 
加上一个RECORD是为了能够记住该节点对应什么记录!
 
慢慢看吧,这是我的一个小程序,整理资料,可以拖放文件我网叶,并保存与数据库中
要源码吗????


unit fabao;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
FileCtrl, StdCtrls, ComCtrls, ToolWin, ExtCtrls, Menus, Buttons, Tabnotbk,
Grids, DBGrids, Db, DBTables, DBCtrls, Mask,DropURLTarget,
ImgList,shellapi, DropSource, DropTarget, ExtDlgs;

type
TmainForm = class(TForm)
StatusBar1: TStatusBar;
Panel1: TPanel;
Panel2: TPanel;
MainMenu1: TMainMenu;
N1: TMenuItem;
N31: TMenuItem;
N27: TMenuItem;
N28: TMenuItem;
N29: TMenuItem;
N30: TMenuItem;
N2: TMenuItem;
N25: TMenuItem;
N26: TMenuItem;
N3: TMenuItem;
N22: TMenuItem;
N23: TMenuItem;
N24: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N18: TMenuItem;
treeview2: TMenuItem;
N21: TMenuItem;
N6: TMenuItem;
N20: TMenuItem;
N19: TMenuItem;
Popup1: TPopupMenu;
ncopy: TMenuItem;
npaste: TMenuItem;
ndel: TMenuItem;
ImageList1: TImageList;
Timer1: TTimer;
FontDialog1: TFontDialog;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Splitter1: TSplitter;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
TabSheet3: TTabSheet;
TreeView1: TTreeView;
DataSource1: TDataSource;
SpeedButton2: TSpeedButton;
Label2: TLabel;
Label3: TLabel;
Edit1: TEdit;
ComboBox1: TComboBox;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
DBGrid2: TDBGrid;
DBMemo1: TDBMemo;
DBGrid1: TDBGrid;
N32: TMenuItem;
nnew: TMenuItem;
Nrename: TMenuItem;
ToolBar1: TToolBar;
new: TToolButton;
del: TToolButton;
refresh: TToolButton;
font: TToolButton;
exit: TToolButton;
help: TToolButton;
find: TToolButton;
open: TToolButton;
BitBtn1: TBitBtn;
ImageList2: TImageList;
TabSheet4: TTabSheet;
GroupBox1: TGroupBox;
CheckBox6: TCheckBox;
CheckBox7: TCheckBox;
CheckBox8: TCheckBox;
ToolButton1: TToolButton;
GroupBox2: TGroupBox;
CheckBox4: TCheckBox;
CheckBox5: TCheckBox;
showtime: TCheckBox;
downmenu: TPopupMenu;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
dragdrop: TMenuItem;
Label1: TLabel;
Edit2: TEdit;
changeshujuku: TBitBtn;
N11: TMenuItem;
N12: TMenuItem;
N13: TMenuItem;
N14: TMenuItem;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
CoolBar1: TCoolBar;
N15: TMenuItem;
N16: TMenuItem;
first: TMenuItem;
putong: TMenuItem;
DropURLTarget1: TDropURLTarget;
ToolButton4: TToolButton;
dropmenu: TPopupMenu;
drophtml: TMenuItem;
dropwenjian: TMenuItem;
DropFileTarget1: TDropFileTarget;
OpenPictureDialog1: TOpenPictureDialog;
OpenDialog2: TOpenDialog;
Query1: TQuery;
createjiedian: TMenuItem;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure refreshClick(Sender: TObject);
procedure TreeView1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure TreeView1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure nnewClick(Sender: TObject);
procedure NrenameClick(Sender: TObject);
procedure exitClick(Sender: TObject);
procedure findClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure findtextClick(Sender: TObject);
procedure PageControl1Change(Sender: TObject);
procedure TreeView1Editing(Sender: TObject; Node: TTreeNode;
var AllowEdit: Boolean);
procedure TreeView1Edited(Sender: TObject; Node: TTreeNode;
var S: String);
procedure ToolButton1Click(Sender: TObject);
procedure showtimeClick(Sender: TObject);
procedure drogClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure DropURLTarget1Drop(Sender: TObject; ShiftState: TShiftState;
Point: TPoint; var Effect: Integer);
procedure changeshujukuClick(Sender: TObject);
procedure getkind(str:string);
procedure htmltotxt(str:string);
procedure dbmemofontClick(Sender: TObject);
procedure treeviewfontClick(Sender: TObject);
procedure saveshow;
procedure N15Click(Sender: TObject);
procedure firstClick(Sender: TObject);
procedure putongClick(Sender: TObject);
procedure drophtmlClick(Sender: TObject);
procedure dropwenjianClick(Sender: TObject);
procedure DropFileTarget1Drop(Sender: TObject; ShiftState: TShiftState;
Point: TPoint; var Effect: Integer);
procedure delClick(Sender: TObject);
procedure N21Click(Sender: TObject);
procedure helpClick(Sender: TObject);
procedure createjiedianClick(Sender: TObject);

private
{ Private declarations }
public
node,oldnode,tempnode:Ttreenode;
fujiedian:integer;
s:string;//存放拖放html转换所取得的txt
g:Tstrings;//存放拖放txt文件所取得的txt;
n:boolean;//是否新建的记录
d:boolean;//是否拖动;
shujukuname:string;//数据库名字;
ini:Tstrings; //配置文件;
wwidth:integer;//窗口宽度;
end;

var
mainForm: TmainForm;
implementation

uses savemodi;

{$R *.DFM}

procedure TmainForm.Timer1Timer(Sender: TObject);
begin
statusbar1.panels[4].text:=timetostr(time);
//statusbar1.Refresh;
end;

procedure TmainForm.FormCreate(Sender: TObject);
begin
fujiedian:=0;
g:=Tstringlist.create;// 初始化
ini:=Tstringlist.create;
query1.databasename:=ExtractFilePath(Application.ExeName);
ini.loadfromfile(query1.databasename+'struct.cfg'); //读配置文件
shujukuname:=ini[0];//第一行为默认数据库
dbmemo1.font.color:=clblue;///文本颜色
dbmemo1.font.size:=10;
refreshclick(self);
//状态栏
statusbar1.canvas.font.style:=[fsbold];
end;

procedure TmainForm.FormDestroy(Sender: TObject);
begin
DropURLTarget1.UnRegister;//取消注册
dropfiletarget1.unregister;//
end;

procedure TmainForm.refreshClick(Sender: TObject);
var
a:integer;//a为总当前接点数
begin
pagecontrol1.activepage:=tabsheet1;//刷新时页为第一页
combobox1.text:='全部查找';
a:=-1; //因index从0开始的;
try
query1.close;
query1.sql.clear;
query1.SQL.add('select name,kind,txt from'+''''+shujukuname+'''');
query1.sql.add('order by kind');
query1.Open;
query1.DisableControls;
treeview1.items.clear;
query1.First;
ComboBox1.items.add('全部查找');
while not query1.eof do
begin
if a=-1 then
begin
treeview1.items.add(nil,query1.fieldbyname('kind').asstring);
a:=a+1;
ComboBox1.items.add(query1.fieldbyname('kind').asstring);//相应在查找的combobox1中增加条项
treeview1.items.addchild(treeview1.Items[a],query1.fieldbyname('name').asstring);
a:=a+1;
end
else
begin
if query1.fieldbyname('kind').asstring=treeview1.items[a].parent.text then
begin//是否与前一节点相同,如果相同则增加子节点.否则则增加父节点
treeview1.items.addchild(treeview1.Items[a].parent,query1.fieldbyname('name').asstring);
a:=a+1;
end
else
begin
treeview1.items.add(nil,query1.fieldbyname('kind').asstring);
a:=a+1;
ComboBox1.items.add(query1.fieldbyname('kind').asstring);
treeview1.items.addchild(treeview1.Items[a],query1.fieldbyname('name').asstring);
a:=a+1;
end;
end;
query1.next;
end;
query1.enablecontrols;
except
showmessage('打开数据库失败,请检查是否与与系统数据库相同');
end;
end;

procedure TmainForm.TreeView1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if query1.modified then saveshow;
try
node:=treeview1.getnodeat(x,y);
if node.level=1 then
begin
if oldnode=nil then
getkind(node.parent.text);//如果是第一次调用getkind
if oldnode.parent<>node.parent then
getkind(node.parent.text);
query1.Locate('name',node.text,[]);
oldnode:=node;
combobox1.text:= node.Parent.text;//改变combobox1的显示
end;
if node.level=0 then
begin
getkind(node.text);
combobox1.text:=node.text;//改变ocmbobox1的显示
end;
statusbar1.panels[0].text:='当前项目:'+query1.fieldbyname('kind').asstring;
statusbar1.panels[2].text:='记录名称:'+query1.fieldbyname('name').asstring;
except
showmessage('打开记录失败');
end;
end;

procedure TmainForm.TreeView1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
node:=treeview1.getnodeat(x,y);
if (button=mbRight) and (node<>nil) then//mouseup and button=mbright;
if node.level=0 then
begin
ndel.enabled:=false;
ncopy.enabled:=false;
nrename.enabled:=false;
end
else
begin
ndel.enabled:=true;
ncopy.enabled:=true;
nrename.enabled:=true;
nnew.enabled:=false;
end;
end;

procedure TmainForm.nnewClick(Sender: TObject);
begin
if query1.modified then saveshow;
if node<>nil then
begin
n:=true;//新建了记录
node.expand(true);
dbmemo1.lines.clear;
query1.edit;
query1.append;
query1.fieldbyname('name').asstring:='新记录';
if node.level=0 then //在父接点点击’新建‘
begin
query1.fieldbyname('kind').asstring:=node.text;
treeview1.items.addchild(node,'新记录');
node:=node.GetLastChild;//得到最后增加的孩子
end
else
begin //在子接点点击’新建‘
query1.fieldbyname('kind').asstring:=node.parent.text;
treeview1.items.Add(node,'新记录'); //增加接点
node:=node.parent.GetLastChild; //得到最后增加的孩子
end;
end;
end;

procedure TmainForm.NrenameClick(Sender: TObject);
begin
node.EditText;///子接点改名
end;

procedure TmainForm.exitClick(Sender: TObject);
begin
close;
end;

procedure TmainForm.findClick(Sender: TObject);
begin
pagecontrol1.activepage:=tabsheet3;
end;

procedure TmainForm.getkind(str:string);//得到一类的表
begin
oldnode:=node;
query1.close;
query1.sql.clear;
query1.SQL.add('select name,kind,txt from'+''''+shujukuname+'''');
query1.sql.add('where kind='''+str+'''');
//query1.sql.add('order by name');
query1.Open;
query1.edit;
//showmessage('getkind');
end;

procedure TmainForm.FormResize(Sender: TObject);
begin
{statusbar1.panels[2].width:=
statusbar1.width-statusbar1.panels[0].width
-statusbar1.panels[3].width
-statusbar1.panels[1].width;}
end;

procedure TmainForm.findtextClick(Sender: TObject);
var
aa,aa1:string;
begin
aa:='%'+edit1.text+'%';
aa1:=edit1.text+'%';
dbgrid2.datasource:=datasource1;
dbgrid1.datasource:=nil;
if (checkbox1.checked) or (checkbox2.checked) then
begin
query1.close;
query1.sql.clear;
query1.SQL.add('select name,kind,txt from'+''''+shujukuname+'''');
if (checkbox1.checked) and (checkbox2.checked=false) then //匹配记录名
begin
query1.sql.add('where (name like'+''''+aa+''''+'or name like'+''''+aa1+''''+')');
end;
if (checkbox2.checked) and (checkbox1.checked=false) then //匹配txt的内容
begin
query1.sql.add('where (txt like'+''''+aa+''''+'or txt like'+''''+aa1+''''+')');
end;
if (checkbox1.checked) and (checkbox2.checked) then //两项都要匹配
begin
query1.sql.add('where (name like'+''''+aa+''''+'or name like'+''''+aa1+''''+')');
query1.sql.add('and (txt like'+''''+aa+''''+'or txt like'+''''+aa1+''''+')');
end;
if combobox1.text<>'全部查找' then
query1.sql.add('and (kind='+''''+combobox1.text+''''+')');
query1.Open;
end;
end;

procedure TmainForm.PageControl1Change(Sender: TObject);
begin
{if (pagecontrol1.ActivePage=Tabsheet1)and
(pagecontrol1.ActivePage=Tabsheet2) then
begin
dbgrid2.datasource:=nil;
dbgrid1.datasource:=datasource1;
end
else
dbgrid1.datasource:=nil; }
end;

procedure TmainForm.TreeView1Editing(Sender: TObject; Node: TTreeNode;
var AllowEdit: Boolean);
begin
allowedit:=true;
end;

procedure TmainForm.TreeView1Edited(Sender: TObject; Node: TTreeNode;
var S: String);
begin
query1.edit;
//query1.locate('name',node.text,[]);
query1.fieldbyname('name').asstring:=s;
query1.post;
node.text:=s;
statusbar1.panels[2].text:='记录名称:'+query1.fieldbyname('name').asstring;;
end;

procedure TmainForm.ToolButton1Click(Sender: TObject);
begin
pagecontrol1.activepage:=tabsheet4;// 打开设计页
end;

procedure TmainForm.showtimeClick(Sender: TObject);
begin
if showtime.checked then
begin
timer1.enabled:=false;
statusbar1.panels[3].text:='';
statusbar1.panels[2].width:=statusbar1.panels[2].width
+statusbar1.panels[3].width;
end
else
begin
timer1.enabled:=true;
statusbar1.panels[2].width:=statusbar1.panels[2].width
-statusbar1.panels[3].width;
end;
end;

procedure TmainForm.drogClick(Sender: TObject);
begin
if dragdrop.checked=false then
begin
wwidth:=mainform.width;
coolbar1.Hide;
mainform.width:=215;
dragdrop.checked:=true;
end
else
begin
coolbar1.show;
dragdrop.checked:=false;
mainform.width:=wwidth;
end;
end;

procedure TmainForm.DropURLTarget1Drop(Sender: TObject;
ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
begin
node:=treeview1.getnodeat(point.x,point.y);//得到拖放接点
if node<>nil then
begin
if query1.modified then saveshow;//if query modified then save
if node.level=1 then
begin
treeview1.items.add(node,DropURLTarget1.title);
getkind(node.parent.text);
end
else
begin
treeview1.items.addchild(node,DropURLTarget1.Title);
getkind(node.text);
end;

node:=node.GetLastChild;//得到最后增加的孩子
node.parent.expand(true);//扩展父接点
///追加记录
query1.edit;
query1.Append;
query1.fieldbyname('name').asstring:=node.Text;
query1.fieldbyname('kind').asstring:=node.parent.text;
htmltotxt(dropurltarget1.url);//file to txt in dbmemo1;
query1.fieldbyname('txt').asstring:=s;
end;
end;


procedure TmainForm.changeshujukuClick(Sender: TObject);
var
struct:Tstrings;
begin
struct:=Tstringlist.create;
if opendialog2.execute then
begin
edit2.text:=OpenDialog2.FileName;
struct.text:=edit2.text;
shujukuname:=edit2.text;
if FileExists('struct.cfg') then
begin
deletefile('struct.cfg');
showmessage('delete ok');
end;
struct.savetofile('struct.cfg');
struct.loadfromfile('struct.cfg');
showmessage(struct[0]);
refreshClick(self);
end;
end;

procedure TmainForm.htmltotxt(str:string);
var
sels,sele:longint;
begin
s:=str;
//if pos('http',s) then save
if pos('file',s)<>0 then delete(s,1,8);//去掉url中的'file:///';
try
g.loadfromfile(s);
s:=g.text;
screen.Cursor := crHourGlass;
while pos('<br>',s)<>0 do delete(s,pos('<br>',s),4);
while pos('-->',s)<>0 do delete(s,pos('-->',s),3);
while pos('>>',s)<>0 do delete(s,pos('>>',s),2);
while (pos('<' ,s)<>0) and (pos('>',s)<>0) do
begin
sels:=pos('<',s);
sele:=pos('>',s);
//if sele=0 then break;
if sels>sele then break
else
delete(s,sels,sele-sels+1);
end;
dbmemo1.perform(wm_vscroll,sb_top,0);
screen.Cursor := crDefault;
except
showmessage('html文件打开文件失败');
end;
end;
procedure TmainForm.dbmemofontClick(Sender: TObject);
begin
if fontdialog1.execute then
dbmemo1.font:=fontdialog1.Font;
end;

procedure TmainForm.treeviewfontClick(Sender: TObject);
begin
if fontdialog1.execute then
treeview1.font:=fontdialog1.Font;
end;

procedure TmainForm.saveshow;
begin
save:=Tsave.create(self);
save.showmodal;
end;

procedure TmainForm.N15Click(Sender: TObject);
begin
if openpicturedialog1.execute then ///改变背景;
begin
toolbar1.Transparent:=false;
coolbar1.bands[0].bitmap.loadfromfile(openpicturedialog1.FileName);
end;
toolbar1.Transparent:=true;
end;

procedure TmainForm.firstClick(Sender: TObject);
begin
mainform.formstyle:=fsStayOnTop;//窗口ontop
putong.checked:=false;
first.checked:=true;
end;

procedure TmainForm.putongClick(Sender: TObject);
begin
mainform.formstyle:=fsnormal;//普通窗口
putong.checked:=true;
first.checked:=false;
end;



procedure TmainForm.drophtmlClick(Sender: TObject);
begin
dropfiletarget1.unregister;
dropurltarget1.register(treeview1);//注册drop url控件(treeview1)
drophtml.checked:=true;
dropwenjian.checked:=false;
statusbar1.panels[3].text:='拖动: '+'网页';
end;

procedure TmainForm.dropwenjianClick(Sender: TObject);
begin
dropurltarget1.unregister;
dropfiletarget1.register(treeview1);//注册drop 文件控件(treeview1)
drophtml.checked:=false;
dropwenjian.checked:=true;
statusbar1.panels[3].text:='拖动: '+'文件';
end;

procedure TmainForm.DropFileTarget1Drop(Sender: TObject;
ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
var
a:integer;
fname:string;
begin
node:=treeview1.getnodeat(point.x,point.y);//得到拖放接点
if node<>nil then
begin
try
for a:=0 to dropfiletarget1.files.count-1 do
begin
if query1.modified then saveshow;//如果记录修改则保存
d:=true;
g.loadfromfile(dropfiletarget1.files[a]);
fname:=ExtractFilename(DropfileTarget1.Files[a]);
delete(fname,pos('.',fname),sizeof(fname));//删除扩展名
if node.level=1 then
begin
treeview1.items.add(node,fname); //增加子接点
getkind(node.parent.text); //在列表中显示数据
node:=node.parent.GetLastChild;//得到最后增加的孩子
end
else
begin
treeview1.items.addchild(node,fname);
getkind(node.text);
node:=node.GetLastChild;//得到最后增加的孩子
end;
node.parent.expand(true);//扩展父接点
///追加记录
query1.edit;
query1.Append;
query1.fieldbyname('name').asstring:=node.Text;
query1.fieldbyname('kind').asstring:=node.parent.text;
query1.fieldbyname('txt').asstring:=g.text;
end;
except
showmessage('不能打开这个文件');
end;
end;
end;

procedure TmainForm.delClick(Sender: TObject);
begin
try
query1.delete;
if node.Parent.count>1 then
node.delete
else
begin
node.parent.delete;
combobox1.items.delete(combobox1.Items.IndexOf(node.parent.text));
end;
except
showmessage('删除记录失败');
end;
end;

procedure TmainForm.N21Click(Sender: TObject);
begin
query1.edit;
end;

procedure TmainForm.helpClick(Sender: TObject);
begin
if FileExists('struct.ini')then
deletefile('struct.ini');
end;

procedure TmainForm.createjiedianClick(Sender: TObject);
var
createjiedian:string;
begin
createjiedian:= InputBox('项目名', '项目名', '');
if createjiedian<>'' then
begin
node:=nil;
treeview1.items.add(nil,createjiedian);
end;
end;

end.



 
谁发一个作好的例子给我好吗
 
给你一个例子,但没有RootID这项。默认ParentID=0为Root。
要添加、删除等操作,均对数据库进行,然后重新刷新Tree。
 
To jumphigh:

和我三年前做的一个项目要求一模一样,当时要求做成资源管理器那样东东展现Oracle数据库中
表的树状关系,右健菜单可以添加节点,更改数据记录什么的,同时还要考虑数据库的一桎性什么
的,也就是说在树上的很多操作对应数据库中很多触发器什么的.

给你一个建议,这个东东的基本算法就是树的遍历查找等等,找本数据结构的书把上面有关树的基
本算法实现一遍,你就心里有数了,看上去这个办法苯,实际上是最好的办法,总共几十页的书,做一
遍就几个晚上就行了,但学到的东西还是很有用的,将来你会不断的用到这些算法,看别人的代码真的
不如自己动手做作业.树的这块算法只要多注意递归调用时函数的堆栈和参数传递方式,多琢磨琢磨
就行了.(最好先彻底搞清楚Delphi函数的参数传递方式,不然有时错了都不知道怎么错的)

数据结构是编程人员的基本功,有空多补补课吧.磨刀不误砍柴功么.

 
我给你一个方法:
也许你改变一下你的库结构可能会给你带来惊喜!
象身份证编号那样设置用户ID,那样根结点和父结点的相应字段都可以不要了。
检索时结果按照ID排序,建立树形目录时先分解一下ID,决定其层次,然后用一个
TStringList做中介,将你所需要显示在树中的字串增加到TStringList中,记住用tab字符
表示其层次关系,根节点不加,第一层子节点加一个,第二层子节点加两个...
TStringList.savetostream,treeview.loadfromstream或者用stringlist.savetofile
treeview.loadfromfile的方法进行赋值!
有点儿笨,但好使,也不需要去学习令人头痛的数据结构!
 
不要用什么父ID、子ID的,用一个 ID 字段就可以了,只要 ID 按序排列,理论上能生成一
棵树,那么就能够实现的。
仔细看一下就知道,按 ID 排序后,即可从第一条记录向后搜索,根据其 ID 之间的包含
关系,即可确定该结点是子节点、新的枝节点、还是兄弟节点,然后在表中(或者用一个
数组,会快些)跳转,逻辑上稍有难度,自己想想也就作出来了,并不难,这种做法不用
递归。
 
to Crab:
那么ID字段必须是字符串型的了:201, 20101, 20102, ...
我还是喜欢用 ID 和 ParentID 两个字段来实现,用不着 RootID
 
ParentID用字符串保存所有的父节点是最好的办法. SQL Server本身的系统表就是这
方法来解决树的储存的.
 
  在网站建设中,分类算法的应用非常的普遍。在设计一个电子商店时,要涉及到商
品分类;在设计发布系统时,要涉及到栏目或者频道分类;在设计软件下载这样的程序
时,要涉及到软件的分类;如此等等。可以说,分类是一个很普遍的问题。
  我常常面试一些程序员,而且我几乎毫无例外地要问他们一些关于分类算法的问题
。下面的举几个我常常询问的问题。你认为你可以很轻松地回答么^_^.
1、 分类算法常常表现为树的表示和遍历问题。那么,请问:如果用数据库中的一个Ta
ble来表达树型分类,应该有几个字段?
2、 如何快速地从这个Table恢复出一棵树;
3、 如何判断某个分类是否是另一个分类的子类;
4、 如何查找某个分类的所有产品;
5、 如何生成分类所在的路径。
6、 如何新增分类;
  在不限制分类的级数和每级分类的个数时,这些问题并不是可以轻松回答的。本文
试图解决这些问题。
分类的数据结构
  我们知道:分类的数据结构实际上是一棵树。在《数据结构》课程中,大家可能学
过Tree的算法。由于在网站建设中我们大量使用数据库,所以我们将从Tree在数据库中
的存储谈起。
  为简化问题,我们假设每个节点只需要保留Name这一个信息。我们需要为每个节点
编号。编号的方法有很多种。在数据库中常用的就是自动编号。这在Access、SQL Serv
er、Oracle中都是这样。假设编号字段为ID。
  为了表示某个节点ID1是另外一个节点ID2的父节点,我们需要在数据库中再保留一
个字段,说明这个分类是属于哪个节点的儿子。把这个字段取名为FatherID。如这里的
ID2,其FatherID就是ID1。
这样,我们就得到了分类Catalog的数据表定义:
Create Table [Catalog](
[ID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[FatherID] [int] NOT NULL
);
  约定:我们约定用-1作为最上面一层分类的父亲编码。编号为-1的分类。这是一个
虚拟的分类。它在数据库中没有记录。
如何恢复出一棵树
  上面的Catalog定义的最大优势,就在于用它可以轻松地恢复出一棵树-分类树。为
了更清楚地展示算法,我们先考虑一个简单的问题:怎样显示某个分类的下一级分类。
我们知道,要查询某个分类FID的下一级分类,SQL语句非常简单:
select Name from catalog where FatherID=FID
显示这些类别时,我们简单地用<LI>来做到:
<%
REM oConn---数据库连接,调用GetChildren时已经打开
REM FID-----当前分类的编号
Function GetChildren(oConn,FID)
strSQL = "select ID,Name from catalog where FatherID="&amp;FID
set rsCatalog = oConn.Execute(strSQL)
%>
<UL>
<%
Do while not rsCatalog.Eof
%>
<LI><%=rsCatalog("Name")%>
<%
Loop
%>
</UL>
<%
rsCatalog.Close
End Function
%>
  现在我们来看看如何显示FID下的所有分类。这需要用到递归算法。我们只需要在G
etChildren函数中简单地对所有ID进行调用:GetChildren(oConn,Catalog("ID"))就可
以了。
<%
REM oConn---数据库连接,已经打开
REM FID-----当前分类的编号
Function GetChildren(oConn,FID)
strSQL = "select Name from catalog where FatherID="&amp;FID
set rsCatalog = oConn.Execute(strSQL)
%>
<UL>
<%
Do while not rsCatalog.Eof
%>
<LI><%=rsCatalog("Name")%>
<%=GetChildren(oConn,Catalog("ID"))%>
<%
Loop
%>
</UL>
<%
rsCatalog.Close
End Function
%>
  修改后的GetChildren就可以完成显示FID分类的所有子分类的任务。要显示所有的
分类,只需要如此调用就可以了:
<%
REM strConn--连接数据库的字符串,请根据情况修改
set oConn = Server.CreateObject("ADODB.Connection")
oConn.Open strConn
=GetChildren(oConn,-1)
oConn.Close
%>
如何查找某个分类的所有产品;
  现在来解决我们在前面提出的第四个问题。第三个问题留作习题。我们假设产品的
数据表如下定义:
Create Table Product(
[ID] [int] NOT NULL,
[Name] [nvchar] NOT NULL,
[FatherID] [int] NOT NULL
);
  其中,ID是产品的编号,Name是产品的名称,而FatherID是产品所属的分类。
  对第四个问题,很容易想到的办法是:先找到这个分类FID的所有子类,然后查询所
有子类下的所有产品。实现这个算法实际上很复杂。代码大致如下:
<%
Function GetAllID(oConn,FID)
Dim strTemp
If FID=-1 then
  strTemp = ""
 else
  strTemp =","
end if
 strSQL = "select Name from catalog where FatherID="&amp;FID
 set rsCatalog = oConn.Execute(strSQL)
 Do while not rsCatalog.Eof
  strTemp=strTemp&amp;rsCatalog("ID")&amp;GetAllID(oConn,Catalog("ID")) REM 递归调

 Loop
 rsCatalog.Close
 GetAllID = strTemp
End Function
REM strConn--连接数据库的字符串,请根据情况修改
  set oConn = Server.CreateObject("ADODB.Connection")
  oConn.Open strConn
  FID = Request.QueryString("FID")
  strSQL = "select top 100 * from Product where FatherID in ("&amp;GetAllID(oC
onn,FID)&amp;")"
  set rsProduct=oConn.Execute(strSQL)
%>
<UL><%
Do while not rsProduct.EOF
%>
<LI><%=rsProduct("Name")%>
<%
Loop
%>
</UL>
<%rsProduct.Close
oConn.Close
%>
这个算法有很多缺点。试列举几个如下:
  1、 由于我们需要查询FID下的所有分类,当分类非常多时,算法将非常地不经济,
而且,由于要构造一个很大的strSQL,试想如果有1000个分类,这个strSQL将很大,能
否执行就是一个问题。
  2、 我们知道,在SQL中使用In子句的效率是非常低的。这个算法不可避免地要使用
In子句,效率很低。
  我发现80%以上的程序员钟爱这样的算法,并在很多系统中大量地使用。细心的程序
员会发现他们写出了很慢的程序,但苦于找不到原因。他们反复地检查SQL的执行效率,
提高机器的档次,但效率的增加很少。
最根本的问题就出在这个算法本身。算法定了,能够再优化的机会就不多了。我们下面
来介绍一种算法,效率将是上面算法的10倍以上。
分类编码算法
  问题就出在前面我们采用了顺序编码,这是一种最简单的编码方法。大家知道,简
单并不意味着效率。实际上,编码科学是程序员必修的课程。下面,我们通过设计一种
编码算法,使分类的编号ID中同时包含了其父类的信息。一个五级分类的例子如下:
  此例中,用32(4+7+7+7+7)位整数来编码,其中,第一级分类有4位,可以表达16种
分类。第二级到第五级分类分别有7位,可以表达128个子分类。
  显然,如果我们得到一个编码为 1092787200 的分类,我们就知道:由于其编码为

0100 0001001 0001010 0111000 0000000
  所以它是第四级分类。其父类的二进制编码是0100 0001001 0001010 0000000 000
0000,十进制编号为1092780032。依次我们还可以知道,其父类的父类编码是0100 000
1001 0000000 0000000 0000000,其父类的父类的父类编码是0100 0000000 0000000 0
000000 0000000。(我是不是太罗嗦了J,但这一点很重要。再回头看看我们前面提到的
第五个问题。哈哈,这不就已经得到了分类1092787200所在的分类路径了吗?)。
现在我们在一般的情况下来讨论类别编码问题。设类别的层次为k,第i层的编码位数为
Ni, 那么总的编码位数为N(N1+N2+..+Nk)。我们就得到任何一个类别的编码形式如下:

2^(N-(N1+N2+…+Ni))*j + 父类编码
其中,i表示第i层,j表示当前层的第j个分类。
这样我们就把任何分类的编码分成了两个部分,其中一部分是它的层编码,一部分是它
的父类编码。
由下面公式定一的k个编码我们称为特征码:(因为i可以取k个值,所以有k个)
2^N-2^(N-(N1+N2+…+Ni))
  对于任何给定的类别ID,如果我们把ID和k个特征码"相与",得到的非0编码,就是
其所有父类的编码!
位编码算法
对任何顺序编码的Catalog表,我们可以设计一个位编码算法,将所有的类别编码规格化
为位编码。在具体实现时,我们先创建一个临时表:
Create TempCatalog(
[OldID] [int] NOT NULL,
[NewID] [int] NOT NULL,
[OldFatherID] [int] NOT NULL,
[NewFatherID] [int] NOT NULL
);
  在这个表中,我们保留所有原来的类别编号OldID和其父类编号OldFatherID,以及
重新计算的满足位编码要求的相应编号NewID、NewFatherID。
程序如下:
<%
REM oConn---数据库连接,已经打开
REM OldFather---原来的父类编号
REM NewFather---新的父类编号
REM N---编码总位数
REM Ni--每一级的编码位数数组
REM Level--当前的级数
sub FormatAllID(oConn,OldFather,NewFather,N,Nm,Ni byref,Level)
strSQL = "select CatalogID , FatherID from Catalog where FatherID=" &amp; OldFat
her
set rsCatalog=oConn.Execute( strSQL )
j = 1
do while not rsCatalog.EOF
i = 2 ^(N - Nm) * j
if Level then i= i + NewFather
OldCatalog = rsCatalog("CatalogID")
NewCatalog = i
REM 写入临时表
strSQL = "Insert into TempCatalog (OldCatalogID , NewCatalogID , OldFatherID
, NewFatherID)"
strSQL = strSQL &amp; " values(" &amp; OldCatalog &amp; " , " &amp; NewCatalog &amp; " , " &amp; Old
Father &amp; " , " &amp; NewFather &amp; ")"
Conn.Execute strSQL
REM 递归调用FormatAllID
Nm = Nm + Ni(Level+1)
FormatAllID oConn,OldCatalog , NewCatalog ,N,Nm,Ni,Level + 1
rsCatalog.MoveNext
j = j+1
loop
rsCatalog.Close
end sub
%>
调用这个算法的一个例子如下:
<%
REM 定义编码参数,其中N为总位数,Ni为每一级的位数。
Dim N,Ni(5)
Ni(1) = 4
N = Ni(1)
for i=2 to 5
Ni(i) = 7
N = N + Ni(i)
next
REM 打开数据库,创建临时表
strSQL = "Create TempCatalog( [OldID] [int] NOT NULL, [NewID] [int] NOT NULL
, [OldFatherID] [int] NOT NULL, [NewFatherID] [int] NOT NULL);"
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open Application("strConn")
Conn.Execute strSQL
REM 调用规格化例程
FormatAllID Conn,-1,-1,N,Ni(1),Ni,0
REM ------------------------------------------------------------------------

REM 在此处更新所有相关表的类别编码为新的编码即可。
REM ------------------------------------------------------------------------

REM 关闭数据库
strSQL= "drop table TempCatalog;"
Conn.Execute strSQL
Conn.Close
%>
第四个问题
  现在我们回头看看第四个问题:怎样得到某个分类下的所有产品。由于采用了位编
码,现在问题变得很简单。我们很容易推算:某个产品属于某个类别的条件是Product.
FatherID&amp;(Catalog.ID的特征码)=Catalog.ID。其中"&amp;"代表位与算法。这在SQL Ser
ver中是直接支持的。
  举例来说:产品所属的类别为:1092787200,而当前类别为1092780032。当前类别
对应的特征值为:4294950912,由于1092787200&amp;4294950912=8537400,所以这个产品属
于分类8537400。
我们前面已经给出了计算特征码的公式。特征码并不多,而且很容易计算,可以考虑在
Global.asa中Application_OnStart时间触发时计算出来,存放在Application("Mark")
数组中。
  当然,有了特征码,我们还可以得到更加有效率的算法。我们知道,虽然我们采用
了位编码,实际上还是一种顺序编码的方法。表现出第I级的分类编码肯定比第I+1级分
类的编码要小。根据这个特点,我们还可以由FID得到两个特征码,其中一个是本级位特
征码FID0,一个是上级位特征码FID1。而产品属于某个分类FID的充分必要条件是:
Product.FatherID>FID0 and Product.FatherID<FID1
下面的程序显示分类FID下的所有产品。由于数据表Product已经对FatherID进行索引,
故查询速度极快:
<%
REM oConn---数据库连接,已经打开
REM FID---当前分类
REM FIDMark---特征值数组,典型的情况下为Application("Mark")
REM k---数组元素个数,也是分类的级数
Sub GetAllProduct(oConn,FID,FIDMark byref,k)
REM 根据FID计算出特征值FID0,FID1
for i=k to 1
if (FID and FIDMark = FID ) then exit
next
strSQL = "select Name from Product where FatherID>"FIDMark(i)&amp;" and FatherID
<"FIDMark(i-1)
set rsProduct=oConn.Execute(strSQL)%>
<UL><%
Do While Not rsProduct.Eof%>
<LI><%=rsProduct("Name")
Loop%>
</UL><%
rsProduct.Close
End Sub
%>
关于第5个问题、第6个问题,就留作习题吧。有了上面的位编码,一切都应该迎刃而解


--
_=0_0=_

黑白设计 http://go.163.com/~limitless

※ 来源:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.250.5]
--
※ 转载:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.250.5]
--
※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: bbs.szptt.net.cn]
 
我这里有一个现成的程序,暑假我作的,要不要?
 
后退
顶部