我想将treeview保存到数据库中,treeview要支持添加,删除,拖放操作。node 的level,index 都在随时变化,怎么样才能保存啊?
我想不出好的方案,只好每次都保存整棵树,能不能每次只修改变动的那个节点?
我想不出好的方案,只好每次都保存整棵树,能不能每次只修改变动的那个节点?
解决方案 »
- 求教,怎么在读记录的时候用数组形式分别读出每个厂家的单个信息和整体信息
- ++++高分!绝对挑战++++从一个1M多的文件中查找字符串,并进行统计,如何做?
- ■■■treeview中使用指针保存数据,如何释放指针?
- 请大家看看,这样的简历能不能找到工作?(请大家都看看,帮个忙,说几句)
- 开发ActiveForm 的简单问题。请指教
- 关于递归判断多层次目录是否为空的算法,为什么返回值总是true
- 求教ActionMainMenubar中菜单分隔线如何实现?
- 我想用UDP来发送一条纪录,请问NMUDP控件能实现吗?
- 怎样使我的程序不出现在Ctrl+Alt+Del对话框里?
- Formula one6.x
- delphi中的sql语句
- 寻控件D7里用的,(控制WINAMP)
才一起保存啊!如果你是因为和数据库对应不上的话,我的做法是:在数据库的表里有一个 id字段(自动增量), 每一次从数据库里去数据来苟建树的时候,
都设置 aNode.Tag := TreeTable.FieldByName('id').AsInteger;如果某个结点被改动了, 就可以通过
TreeTable.Locate('id', aNode.Tag,[loCaseInsensitive]);找到这个结点在数据库里的
对应信息,进行修改!
你可以使用 aNode.StateIndex 来与 TreeTable表里的 id字段进行一一对应!
至于删除和添加只不过时根据主键在数据库中加一条记录。
我可以发一个树窗体给你,我的E_mail: [email protected]
我们决定一个节点的位置,靠的是他的父节点和他自己是老几,有了这2项,就可以精确定位节点.
但是,靠什么来定义父节点呢?如果用Index的话,在保存,读出整棵树时是可以的,但是用户增加,删除了节点后,Index发生了变化,那就会找错老爸.靠Text更不行,会有好几个老爸,
靠Data吧,要留着有别的用途.如果增加别的数据来确定唯一的节点,那会增加了找老爸的难度.
由此可见,上面几位用的都是我在用的笨办法,就是,每修改一个节点,必须保存整棵树,否则你就无法读出!!
如果树的枝叶茂盛,保存整棵树是很大的开销,增加额外得数据来确定父节点的话,开销也很大,每找一个老爸要select一下数据库.怎么找个好的算法平衡一下,很伤脑筋.
还有,如果不到万不得已,不要用递归,这是我老师很多年前教我的,我保存树和读出都不用的,靠的是INDEX.
这个问题不是很容易的,不要小看了.
我实现过权限树,每个用户权限不同,所以要动态建立
不明白你的意思,如果你不用index,如何让数据库内的记录和TreeView的节点对应呢?
父节点代码是什么?子节点代码又是什么,麻烦说清楚一点.
其实用Level也不好,是通过递归得到Level的.
level 0: 01,02
level 1: 0101,0102,0202,0203
level 2:010101,010102
这样很容易定位节点,如果这样编号可以不要父节点号
如此这样编号后,你应该很容易建树了吧
上面有它的添加,删除,与修正的代码,你可能看一下有没有用了,但我现在就是用这个与数
做的
数据库的结构为:
学校,年级,班级,层次编号
//============通过数据表立TREE
procedure TPupilCataLogForm.FlatSpeedButton1Click(Sender: TObject);
begin
Case Levlint of
0:begin
If Flatcombobox1.Text='' then
begin
application.MessageBox('学校名称不能为空','添加提示框',MB_OK);
Exit;
end;
with dm.ADOPup do
begin
append;
Fieldbyname('学校名称').AsString :=Flatcombobox1.Text ;
Fieldbyname('层次编号').AsInteger:=1;
Fieldbyname('录入时间').AsString :=datetostr(date);
post;
end;
PupTree.Items.AddChild(SelectView,Flatcombobox1.Text);
SelectView.GetLastChild.ImageIndex:=1;
SelectView.GetLastChild.SelectedIndex:=2;
Application.MessageBox('添加学校成功','添加提示框',Mb_ok);
end;
1:begin
If Flatcombobox2.Text='' then
begin
application.MessageBox('年级名称不能为空','添加提示框',MB_OK);
Exit;
end;
with dm.ADOPup do
begin
append;
Fieldbyname('学校名称').AsString :=Flatcombobox1.Text ;
Fieldbyname('年级名称').AsString :=Flatcombobox2.Text ;
Fieldbyname('层次编号').AsInteger:=2;
Fieldbyname('录入时间').AsString :=datetostr(date);
post;
end;
PupTree.Items.AddChild(SelectView,Flatcombobox2.Text);
SelectView.GetLastChild.ImageIndex:=3;
SelectView.GetLastChild.SelectedIndex:=4;
Application.MessageBox('添加年级成功','添加提示框',Mb_ok);
end;
2:begin
If Flatcombobox3.Text='' then
begin
application.MessageBox('班级名称不能为空','添加提示框',MB_OK);
Exit;
end;
with dm.ADOPup do
begin
append;
Fieldbyname('学校名称').AsString :=Flatcombobox1.Text ;
Fieldbyname('年级名称').AsString :=Flatcombobox2.Text ;
Fieldbyname('班级名称').AsString :=Flatcombobox3.Text ;
Fieldbyname('层次编号').AsInteger:=3;
Fieldbyname('录入时间').AsString :=datetostr(date);
post;
end;
PupTree.Items.AddChild(SelectView,Flatcombobox3.Text);
SelectView.GetLastChild.ImageIndex:=5;
SelectView.GetLastChild.SelectedIndex:=6;
Application.MessageBox('添加班级成功','添加提示框',Mb_ok);
end;
3:begin
Application.MessageBox('班级层为最下层不能再添加新层,请选上一层','添加提示框',Mb_ok);
exit;
end;
end;
end;
//===========选中的对像
procedure TPupilCataLogForm.FlatSpeedButton5Click(Sender: TObject);
Var
Lev:integer;
PrNexNode:TTreeNode;
nubme:integer;
begin
Schoolname:='';
ClassName:='';
GradeName:='';
SelectView:=PupTree.Selected;
LevlInt:=SelectView.Level ;
//======添加学校 SchoolName ClassName gradeName
If Levlint=0 then
Begin
LevView:=SelectView;
FlatCombobox1.Enabled :=True;
FlatCombobox2.Enabled :=False;
FlatCombobox3.Enabled :=False;
End;
//====学校
if Levlint=1 then
Begin
LevView:=SelectView.Parent ;
Schoolname:=selectview.Text ;
FlatCombobox1.Enabled :=False;
FlatCombobox2.Enabled :=True;
FlatCombobox3.Enabled :=False;
End;
//====年级
if Levlint=2 then
begin
prnexnode:=SelectView.Parent;
SchoolName:=prNexNode.Text;
Levview:=prnexnode;
gradeName:=selectView.Text ;
FlatCombobox1.Enabled :=False;
FlatCombobox2.Enabled :=False;
FlatCombobox3.Enabled :=True;
end;
//====
if Levlint=3 then
begin
prnexnode:=SelectView.Parent;
gradeName:=prNexNode.Text;
prnexnode:=prnexnode.Parent;
SchoolName:=prnexnode.Text ;
Levview:=prnexnode;
ClassName:=selectView.Text ;
FlatCombobox1.Enabled :=False;
FlatCombobox2.Enabled :=False;
FlatCombobox3.Enabled :=False;
end;
Flatcombobox1.Text :=SchoolName;
Flatcombobox2.Text :=GradeName;
Flatcombobox3.Text :=ClassName;
end;
{=========根据数据表生成目录=========}
procedure TPupilCataLogForm.FlatSpeedButton4Click(Sender: TObject);
var
tempTreeNode:TTreeNode;
tempText:string;
childtext:string;
NonceTreeNode:TTreeNode;
NonceText:string;
i,j:integer;
begin //========建立学校目录
With dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('Select * from 学生资料结构表 where 层次编号=:LelVes');
Parameters.ParamByName('LelVes').Value:=1;
open;
first;
While not(eof) do
begin
tempTreeNode:=PupTree.Items[0];
ChildText:=Fieldbyname('学校名称').AsString; PupTree.Items.AddChild(tempTreeNode,ChildText);
TemptreeNode.GetLastChild.ImageIndex:=1;
TemptreeNode.GetLastChild.SelectedIndex:=2;
Next;
end;
//PupTree.Items.Add (puptree.Items[0] ,'ddd');
end; //========建立年级目录
For i:=0 to PupTree.Items[0].Count-1 do
begin
NonceTreeNode:=PupTree.Items[0].Item[i];
NonceText:=NonceTreeNode.Text;
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('select * from 学生资料结构表 where 层次编号=:LelVes and 学校名称=:SChoolName');
parameters.ParamByName('LelVes').Value:=2;
parameters.ParamByName('SchoolName').Value:=NonceText;
open;
first;
while not(Eof) do
begin
ChildText:=Fieldbyname('年级名称').AsString ;
PupTree.Items.AddChild(NonceTreeNode,ChildText);
NonceTreeNode.GetLastChild.ImageIndex:=3;
NonceTreeNode.GetLastChild.SelectedIndex:=4;
Next;
end;
end; end; //===========建立班级目录
For i:=0 to PupTree.Items[0].Count-1 do
begin
NonceTreeNode:=PupTree.Items[0].Item[i]; //学校目录
NonceText:=NonceTreeNode.Text; //学校名称
For j:=0 to NonceTreeNode.Count-1 do
begin
tempTreeNode:=NonceTreeNode.Item[j];//年纪目录
tempText:=tempTreeNode.Text ; //年纪名称 with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('select * from 学生资料结构表 where 层次编号=:LelVes and 学校名称=:SChoolName and 年级名称=:PGrade');
parameters.ParamByName('LelVes').Value:=3;
parameters.ParamByName('SchoolName').Value:=NonceText;
parameters.ParamByName('PGrade').Value:=tempText; open;
first;
while not(Eof) do
begin
ChildText:=Fieldbyname('班级名称').AsString ;
PupTree.Items.AddChild(tempTreeNode,ChildText);
TemptreeNode.GetLastChild.ImageIndex:=5;
TemptreeNode.GetLastChild.SelectedIndex:=6;
Next;
end;
end;
end;
end;end;
//======选中
procedure TPupilCataLogForm.PupTreeMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);begin
FlatSpeedButton5click(self);
end;
procedure TPupilCataLogForm.FlatSpeedButton7Click(Sender: TObject);
Var
st:string;
begin
try
Case Levlint of
0:begin
application.MessageBox('这是一个总目录不能删除','删除对话框',MB_OK);
exit;
end;
1:try st:='你确认要删除'+Flatcombobox1.Text+'学校的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'删除对话框',MB_OKCANCEL)=2 then exit;
PupTree.Items.Delete(SelectView);
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('DELETE from 学生资料结构表 where 学校名称=:SChoolName');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('学校删除成功','删除对话框',MB_OK);
except
end;
2:begin
st:='你确认要删除'+Flatcombobox2.Text+' 年级的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'删除对话框',MB_OKCANCEL)=2 then exit;
PupTree.Items.Delete(SelectView);
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('DELETE from 学生资料结构表 where 学校名称=:SChoolName and 年级名称=:PGrade');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
parameters.ParamByName('PGrade').Value:=Flatcombobox2.Text;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('年级删除成功','删除对话框',MB_OK);
end;
3:begin
st:='你确认要删除'+Flatcombobox3.Text+'班级的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'删除对话框',MB_OKCANCEL)=2 then exit;
PupTree.Items.Delete(SelectView);
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('DELETE from 学生资料结构表 where 学校名称=:SChoolName and 年级名称=:PGrade and 班级名称=:className');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
parameters.ParamByName('PGrade').Value:=Flatcombobox2.Text;
parameters.ParamByName('className').Value:=Flatcombobox3.Text;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('班级删除成功','删除对话框',MB_OK);
end;
end;
except
end;
end;
//======修正
procedure TPupilCataLogForm.FlatSpeedButton6Click(Sender: TObject);
var
st:string;
begin
Case Levlint of
0:begin
application.MessageBox('这是一个总目录不能删除','删除对话框',MB_OK);
exit;
end;
1:begin
st:='你确认要修正'+Flatcombobox1.Text+'学校的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'修正对话框',MB_OKCANCEL)=2 then exit;
st:=Flatcombobox1.Text;
IF not(InputQuery('请输入修正的信息','修正对话框',St)) then Exit;
SelectView.Text:=st;
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('UPDATE 学生资料结构表 set 学校名称=:UPst where 学校名称=:SChoolName');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
parameters.ParamByName('Upst').Value:=st;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('学校修正成功','修正对话框',MB_OK);
end;
2:begin
st:='你确认要修正'+Flatcombobox2.Text+'年级的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'修正对话框',MB_OKCANCEL)=2 then exit;
st:=Flatcombobox2.Text;
IF not(InputQuery('请输入修正的信息','修正对话框',St)) then Exit;
SelectView.Text:=st;
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('UPDATE 学生资料结构表 set 年级名称=:UPst where 学校名称=:SChoolName and 年级名称=:PGrade');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
parameters.ParamByName('Upst').Value:=st;
parameters.ParamByName('PGrade').Value:=Flatcombobox2.Text;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('年级修正成功','修正对话框',MB_OK);
end;
3:begin
st:='你确认要修正'+Flatcombobox3.Text+'班级的信息与他下面的全部信息吗?';
if application.MessageBox(pchar(st),'修正对话框',MB_OKCANCEL)=2 then exit;
st:=Flatcombobox3.Text;
IF not(InputQuery('请输入修正的信息','修正对话框',St)) then Exit;
SelectView.Text:=st;
with dm.ADOQuePup do
begin
sql.Clear ;
sql.Add('UPDATE 学生资料结构表 set 班级名称=:UPst where 学校名称=:SChoolName and 年级名称=:PGrade and 班级名称=:className');
parameters.ParamByName('SchoolName').Value:=Flatcombobox1.Text;
parameters.ParamByName('Upst').Value:=st;
parameters.ParamByName('PGrade').Value:=Flatcombobox2.Text;
parameters.ParamByName('className').Value:=Flatcombobox3.Text;
ExecSQL;
end;
dm.ADOPup.Refresh ;
application.MessageBox('班级修正成功','修正对话框',MB_OK);
end;
end;
end;
LevlInt:integer;
LevView:TTreeNode;
SchoolName:string;
gradeName:string;
ClassName:string;
我给个比较实用的方法,支持无限级,效率也高,
下面给出关键生成树源代码,不过是BCB的。1。数据库表的结构:采用节点ID ,节点父ID(PID)及其他字段,ID,PID的类型无所谓,
数字,字符都行,也不需要有规律,只要保证ID字段为主键即可,比如可以用GUID
来作ID。2。TTreeNode的Data指向一个结构,结构内有一个域记录节点的ID,其他的域根据需要
可自己定义。3。从数据库生成树,这是要好好考虑的,要作到效率高,最重要的是“避免反复查询
数据库或遍历数据集”,最好是一次就能生成整棵树。这也是可以做到的,请看代码:
*/struct NODEDATA //节点关联的数据
{
int id;
int pid;
AnsiString name;
AnsiString memo;
};void __fastcall FillTree()
{
struct NODEDATA *ndata=NULL;
TStringList *ss=NULL; TTreeNode *aNode=NULL;
TTreeNode *bNode=NULL;
TTreeNode *pNode=NULL; Query1->Close();
Query1->SQL->Text="select id,pid,name,memo from tree_tab";
Query1->SQL->Open(); try
{
ss=new TStringList();
TreeView->Items->BeginUpdate();//禁止刷新,提高速度
//遍历记录集
for(Query1->First(); !Query1->Eof; Query1->Next())
{
ndata=new NodeData();
ndata->id = Query1->FieldByName("id")->AsInteger;
ndata->pid = Query1->FieldByName("id")->AsInteger;
ndata->name = Query1->FieldByName("name")->AsString;
ndata->memo = Query1->FieldByName("memo")->AsString;
//生成一个根节点加入TreeView
aNode=TreeView1->Items->AddObject(NULL,ndata->name,ndata);
//记录id-TreeNode对应关系,便于下面查找
ss->AddObject(AnsiString(ndata->id),aNode);
}
Query1->Close();
ndata=NULL;
//调整树,根据节点的pid,把节点移到相应的TreeNode下
int idx;
for(aNode=TreeView1->Items->GetFirstNode(); aNode!=NULL ;)
{
ndata = (NODEDATA*)(aNode->Data);
if(-1==(idx=ss->IndexOf(AnsiString(ndata->pid))))
{
aNode=aNode->getNextSibling();
continue;
}
else
{
pNode = (TTreeNode*)(ss->Objects[idx]);
bNode=aNode;
aNode=aNode->getNextSibling();
bNode->MoveTo(pNode,naAddChild);
}
}
}
__finally
{
delete ss; ss=NULL;
Query1->Close();
TreeView->Items->EndUpdate();
}
}
id,pid等数据,组成sql语句更新数据库即可......
myy()的方法你也可以试试。
(01)aaa
(0101)aaa.bbb
(0102)aaa.ccc
(010201)aaa.ccc.ddd
(0103)aaa.eee
(02)fff
(0201)fff.ggg
(03)hhh
(0301)hhh.iii
(030101)hhh.iii.jjj
然后用treeview导入就会自动生成树了
我们讨论的是在数据库中动态修改节点后如何保存,如果要保存整棵树的话,没什么技术问题,大家都用的办法都一样,根本不用管什么父节点,什么子节点,有SaveToFile方法.你可以打开用Treeview.SaveTOFile方法保存的文件,和你的结构几乎一样(你的01,0102这些编号是多余的,没有必要).
如果我一次只修改了一个节点(我指的修改不仅仅是修改了Text那么简单,包括拖放操作),照你的办法就只能保存整棵树了,我的意思是只改一个节点,还要保证能顺利读出.要注意的是,数据库和文本文件不同,你读出的第一个未必就是根节点,第二个也不见得是第一个的子节点.
大家给出的方案只有myy的是可行的,但他多了2个变量,树一大了,也很可观.更好的办法寻求中...................希望有高手指点.
再要做到“数据库中动态修改节点后如何保存”,还很难吗?上面已经说了:
treeview的添加,删除,拖放操作也不难,每次都通过被操作的TreeNode->Data取出
id,pid等数据,组成sql语句更新数据库即可......何况我给出的表数据中节点其实只与父节点相关的,即使一次拖放一棵子树,
也只需要改一条记录。
>>但他多了2个变量,树一大了,也很可观...哪里多了2个变量?struct NODEDATA 中吗?你可以只放ID,PID嘛,其他的都是局部
变量,只是临时的而已。
不知道你的树有多大?我曾经做的是5万多节点,只是创建时稍慢(也与数据库有关)。如果你真的很在乎效率,那就不要一次建全部节点,而是每次在用户“展开节点”时,
再建下级节点。