关于TreeView控件结合数据库的使用

经常在网上看到一些人询问Tree的操作,尤其是结合数据库的。本人一直在用TreeView控件,略有一些经验积累,想整理出来与大家共享。目前手头还有些工作要做,可能不能马上让大家见到东西,不过我计划在春节之前完成这个想法。
先简要概括一下这次笔记的主要内容吧。一、把树存储到数据表中;二、从数据表中读出数据并建立树;三、对树结点进行添加、删除、修改;四、树的扩展应用----节点多选。笔记中包括所有树的操作原码以及相关存储过程。
数据表名称:TSysPara 系统参数表
数据表结构:ParaCode Varchar(60) 参数代码
ParaValue Varchar(100) 参数名称
参数代码编码规则:代码3位一节,子结点包含父结点代码。例如,001为根结点,那么它的子结点代码可为001001,001002,........在数据结构中代码长度为3的均为根结点。(还有一种树的数据结构即子结点记录父结点代码,这种方式我还没使用过,不做讨论)

待续...............................
本来打算晚上再写点,可是老婆打电话要我买菜。嫁给我这么个穷小子已经很有勇气了,总不能让老婆再挨饿吧。没办法,先帖几个公共函数吧。
//根据代码在树中定位此代码的节点
Function LocatNode(Tree:TTreeView;NodeCode:string):TTreeNode;
var
PStr,PStr2:^string;
Node:TTreeNode;
begin
result:=nil;
if Tree.Items.GetFirstNode = nil then exit;
Tree.Selected := nil;
Node := Tree.Items.GetFirstNode;
while Node <> nil do
begin
new(pStr);
PStr:=Node.Data;
if (PStr^=NodeCode) then
begin
result:=Node;
Node.Selected :=true;
exit;
end;
Node:=Node.GetNext;
end;
end;
//取结点的全称
Function GetNodeFullName(Node:TTreeNode):string;
begin
Result:='';
if node = nil then exit;
Result:=Node.Text ;
while Node.Parent <> nil do
begin
Result:=Node.Parent.Text +'-'+Result;
Node:=Node.Parent ;
end;
end;

//在树中增加一个节点。节点的集合是从数据表中查询来的,并按ParaCode顺序排列,循环调用此函数就根据数据表中的数据生成了一棵树,你可以试试。在这个函数仲采用递归方法,看的时候应注意。
Procedure AddTreeNode(Tree:TTreeView;NodeCode,NodeName:String;
NodeList:TList;RootNode:TTreeNode = nil);
var
CurNode:TTreeNode;
pNodeCode:^String;
begin
if NodeList.Count < 1 then //树为空时
begin
CurNode:=RootNode;
New(pNodeCode);
pNodeCode^:=NodeCode;
CurNode := Tree.Items.AddChildObject(CurNode,NodeName,pNodeCode);
NodeList.Insert(0,CurNode);
end
else
begin
CurNode := TTreeNode(NodeList[0]);
New(pNodecode);
pNodeCode:=CurNode.Data;
if length(NodeCode)>length(pNodeCode^) then //子结点插入
begin
New(pNodeCode);
pNodeCode^:=NodeCode;
CurNode := Tree.Items.AddChildObject(CurNode,NodeName,pNodeCode);
NodeList.Insert(0,CurNode);
end
else if length(NodeCode)=length(pNodeCode^) then //姐妹结点插入
begin
NodeList.Delete(0);
if NodeList.Count<1 then
CurNode := nil
else
CurNode := TTreeNode(NodeList[0]);
New(pNodeCode);
pNodeCode^:=NodeCode;
CurNode := Tree.Items.AddChildObject(CurNode,NodeName,pNodeCode);
NodeList.Insert(0,CurNode);
end
else if length(NodeCode) begin
NodeList.Delete(0);
AddTreeNode(Tree,NodeCode,NodeName,NodeList); //递归
end;
end
end;
这个函数的调用代码示例
var
vNodeCode,vNodeScript:string;
list:TList;
begin
try
SysParaCds.First;
list:=Tlist.Create;
Tree.Items.Clear;
Tree.Items.BeginUpdate;
while not SysParaCds.Eof do
begin
vNodeCode:=SysParaCds.fieldbyname('ParaCode').AsString;
vNodeScript:=SysParaCds.fieldbyname('ParaValue').AsString;
AddTreeNode(Tree,vNodeCode,vNodeScript,List);
SysParaCds.Next;
end;
Tree.Items.EndUpdate;
finally
list.Free;
end;
end;
2006-1-16 16:26:04
2006-1-17 14:36:09 老天阿,我写了一中午,发表的时候竟然出错了,白干了 ,气死我了

老天阿,我写了一中午,发表的时候竟然出错了,白干了 ,气死我了

2006-1-17 18:24:10 生成一个新结点并保存到数据库中

//在这里先说明一下结点数据是如何在结点中存储的。我们知道TTreeNode有两个属性:TreeNode.Text 和

TreeNode.data。TreeNode.Text 是结点的显示文本,在这里它的值是ParaValue;TreeNode.Data是一个指针,可以指向变量也可以指向对象,

这这里它指向的是一个变量,变量的值是ParaCode;
下面先看一组函数;
//插入树中新结点
function InsertNode(ParentNode:TTreeNode;NodeScript:string;SubNodeCode:string=''):TTreeNode;
var
vNodeScript,vNodeCode:string;
pStr:^String;
begin
Result:=nil ;
vNodeScript:=NodeScript;
VNodeScript:=StringReplace(NodeScript,' ','',[rfReplaceAll]); //消除空格,防止歧义
VNodeScript:=StringReplace(NodeScript,' ','',[rfReplaceAll]);
if vNodeScript='' then
Raise Exception.Create('节点描述不能为空!');
if vNodeScript='其它' then
vNodeScript:='其他';
if FindNode(ParentNode,vNodeScript) <>nil then
Raise Exception.Create('该层中已存在相同名称的子节点!');

if vNodeScript='其他' then
begin
if ParentNode = nil then
begin
vNodeCode:='999' //默认值
end
else
begin
new(pStr);
pStr:=ParentNode.Data;
vNodeCode:=pStr^+'999'; //默认值
end;
end
else
begin
if (SubNodeCode <> '') then
begin
if (SubNodeCode<='000') or (SubNodeCode>'999') then //检查编码的合法性
Raise Exception.Create('编码错误!');
new(pStr);
if ParentNode<>nil then
pStr:=ParentNode.Data ;
vNodeCode:=pStr^+SubNodeCode;
end
else
begin
vNodeCode:=NewCode(ParentNode); //生成新编码
end;
end;
if (LocatNode(vNodeCode)<>nil) then //检查编码是否已存在
Raise Exception.Create('编码已存在!');
new(pStr);
pStr^:=vNodeCode;
Result := Tree.Items.AddChildObject(ParentNode,vNodeScript,pStr);

end;

// 查找指定名称的子结点
Function FindNode(ParentNode:TTreeNode;NodeScript:string):TTreeNode;
var
Node:TTreeNode;
begin
result:=nil;
if ParentNode =nil then
Node := Tree.Items.GetFirstNode
else
Node:=ParentNode.getFirstChild;
while Node <> nil do
begin
if Node.Text = NodeScript then
begin
Result:=Node;
break;
end;
Node:=Node.getNextSibling;
end;
end;
//生成新编码
Function NewCode(ParentNode:TTreeNode):string;
var
MaxNodeCode,SubCode:String;
begin
MaxNodeCode:=GetMaxNodeCode(ParentNode); //取最大节点编码
SubCode:=copy(MaxNodeCode,length(MaxNodeCode)-2,3);
SubCode:=inttostr(strtoint(SubCode)+1);
while length(SubCode)<3 do
SubCode:='0'+SubCode;
result:=copy(MaxNodeCode,1,length(MaxNodeCode)-3)+SubCode;
end;
//取最大节点编码
Function GetMaxNodeCode(ParentNode:TTreeNode):string;
var
Node:TTreeNode;
pStr:^string;
begin
new(pStr);
if Tree.Items.Count = 0 then //空树
begin
Result:='000';
exit;
end;
if parentnode=nil then //根节点
begin
Node := Tree.Items.GetFirstNode;
end
else
begin
Node:=ParentNode.GetFirstChild;
if (Node = nil) then //节点存在但无子节点
begin
pStr:=ParentNode.Data;
result:=pStr^+'000';
exit;
end;
end;
//正常情况下
pStr:=Node.Data;
if Node.Text <> '其他' then //'其他'节点自动略过
result:=pStr^;
while Node.getNextSibling <>nil do //遍历树取最大编码 (除去‘其他’节点)
begin
pStr:=Node.getNextSibling.Data;
if Node.getNextSibling.Text = '其他' then
begin
Node:=Node.getNextSibling;
Continue;
end;
if result result:=pStr^;
Node:=Node.getNextSibling;
end;
end;
//向数据库中保存数据的存储过程
create procedure I_SysParameter @ParaCode varchar(30),@ParaValue varchar(100)
as
begin
if LTrim(RTrim(@ParaCode)) = ''
begin
Raiserror('错误:参数代码为空!',16,1)
Return
end
if LTrim(RTrim(@ParaValue)) = ''
begin
Raiserror('错误:参数值为空!',16,1)
Return
end
insert T_SysPara (ParaCode,ParaValue) values (@ParaCode,@ParaValue)
--select 'ok' operation
end
//程序调用代码
//添加根结点
procedure TSysParameterForm.BitBtn1Click(Sender: TObject);
var
TreeNode:TTreeNode;
p:^string;
DefaultCursor:TCursor;
begin
inherited;
Edit1.SetFocus;
Edit1.SelectAll;
if trim(Edit1.Text )='' then
begin
application.MessageBox('根结点名称为空,不能添加!',MsgCaption,mb_ok+mb_IconError);
exit;
end;
DefaultCursor :=Screen.Cursor ;
Screen.Cursor :=crHourGlass;
try
TreeNode:=ParTree.InsertNode(nil,trim(Edit1.Text)); //生成新结点
p:=TreeNode.Data;
setlength(Parameters,2) ; //Parameters是TWidestringDYNArray类型的数组
Parameters[0]:=p^ ;
Parameters[1]:=TreeNode.Text ;
dm.CallOperation2(5010,Parameters); //这是我的一个三层调用,调用它的最终结果是执行存储过程I_SysParameter,把数据保存到数

据库中,大家只要知道它的调用结果就可以了,对于函数本身可以不去关心。
except
on e:Exception do
begin
Screen.Cursor := DefaultCursor;
application.MessageBox(pchar(e.Message ),MsgCaption,mb_ok+mb_IconError);
exit;
end;
end;
Screen.Cursor := DefaultCursor;
end;
//添加子结点
procedure TSysParameterForm.BitBtn2Click(Sender: TObject);
var
TreeNode:TTreeNode;
p:^string;
DefaultCursor:TCursor;
begin
inherited;
Edit2.SetFocus;
Edit2.SelectAll;
if trim(Edit2.Text )='' then
begin
application.MessageBox('结点名称为空,不能添加!',MsgCaption,mb_ok+mb_IconInformation);
exit;
end;
if ParTree.Selected = nil then
begin
application.MessageBox('请选择父节点!',MsgCaption,mb_ok+mb_IconInformation);
exit;
end;
DefaultCursor :=Screen.Cursor ;
Screen.Cursor :=crHourGlass;
try
TreeNode:=ParTree.InsertNode(ParTree.Selected,trim(Edit2.Text));
p:=TreeNode.Data;
setlength(Parameters,2) ;
Parameters[0]:=p^ ;
Parameters[1]:=TreeNode.Text ;
dm.CallOperation2(5010,Parameters);
except
on e:Exception do
begin
Screen.Cursor := DefaultCursor;
application.MessageBox(pchar(e.Message ),MsgCaption,mb_ok+mb_IconError);
exit;
end;
end;
Screen.Cursor := DefaultCursor;
end;
//以上两个操作差别不大,在我的程序中是分开执行的,懒的修改,全部贴出了。
以上是树新结点的添加操作,代码是我从程序中复制的,有一些删改,如有错误指出后我将修改,明天如果有时间完成结点的修改删除

2006-1-24 13:52:49 明天就回家了,今天完成最后一部分-----结点的修改删除以及结点多选

结点修改
声明一个全局变量
var
NodeText:string;

在树的Editing和Edited事件中
procedure TSysParameterForm.TreeView1Editing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean);
begin
NodeText:=node.Text;
end;
procedure TSysParameterForm.TreeView1Edited(Sender: TObject; Node: TTreeNode; var S: String);
var
p:^string;
DefaultCursor:TCursor;
begin
if length(trim(s))=0 then
begin
application.MessageBox ('参数值不能为空',MsgCaption,mb_ok+mb_IconError);
application.ProcessMessages;
Node.Text := NodeText;
end
else
begin
DefaultCursor :=Screen.Cursor ;
Screen.Cursor :=crHourGlass;
try
//修改数据库中的数据
p:=Node.Data;
setlength(Parameters,2) ;
Parameters[0]:=p^ ;
Parameters[1]:=s ;
dm.CallOperation2(5011,Parameters); //三层调用
except
on e:Exception do
begin
Screen.Cursor := DefaultCursor;
application.MessageBox(pchar(e.Message ),MsgCaption,mb_ok+mb_IconError);
Node.Text := NodeText; //如果错误,恢复原值
exit;
end;
end;
Screen.Cursor := DefaultCursor;
end;
end;
//修改操作调用的存储过程
create procedure U_SysParameter @ParaCode varchar(600),@ParaVarlue varchar(50)
as
update T_SysPara set ParaValue = @ParaVarlue where ParaCode = @ParaCode

select 'ok' operation
//删除操作
procedure TSysParameterForm.N1Click(Sender: TObject);
var
p:^string;
begin
inherited;
if ParTree.Selected = nil then exit;
if Application.MessageBox(IsDelData,MsgCaption,mb_yesno+mb_IconQuestion) = idno then exit;
p:=ParTree.Selected.Data;
ParTree.Items.Delete(ParTree.Selected); //删除结点
try
SetLength(Parameters,1);
Parameters[0]:=p^;
dm.CallOperation2(5012,Parameters); //三层调用,删除数据库中的数据
except
on e:exception do
begin
application.MessageBox(pchar(e.Message),MsgCaption,mb_ok+mb_ICONError);
exit;
end;
end;
end;
//删除结点的存储过程
Create Procedure D_SysParameter @ParCode varchar(600)
as
Delete T_SysPara where ParaCode like @ParaCode+'%' --包括所有子结点
Select 'ok' operation

//以上基本完成了树的添加、修改、删除登记本操作,下面讲述一下数结点的多选,
树结点的多选应用的比较多,用起来也比较灵活。一个树结点共有三种状态:未选择,全选择和部分选择。未选择:本结点以及本结点的所有子结点都处于未选择状态;全选择:本结点以及本结点的所有子结点都处于选择状态;部分选择:本结点一部分子结点处于未选择状态。结点可以通过它的stateImages和stateIndex属性以图片变换的方式来表示这三种状态,当一个接点的状态发生变化时这个结点的所有父结点和所有子结点的状态都有可能发生变化。先看两个状态同步函数:
//改变所有子结点的状态
Procedure TSysParameterForm.ChangeNodeChild(Node:TTreeNode;StIndex:integer);
var
i:integer;
begin
For i:=0 to Node.Count-1 do
begin
Node.Item[i].StateIndex:=StIndex; //
if Node.Item[i].Count>0 then
ChangeNodeChild(Node.Item[i],StIndex); //递归
end;
end;
//改变所有父结点的状态
Procedure TSysParameterForm.ChangeNodeParent(TreeView:TTreeView;Node:TTreeNode;StIndex:integer);
var
i,j:integer;
isAll,isChecked:Boolean;
ParentNode:TTreeNode;
begin
isAll:=True;
isChecked:=False;
For i:=0 to TreeView.Items.Count-1 do
begin
if TreeView.Items.Item[i] = Node then
begin
ParentNode:=TreeView.Items.Item[i].Parent;
if ParentNode<>nil then
begin
if ((ParentNode.StateIndex<>2) and (ParentNode.StateIndex<>3))
and (StIndex<>2) then Break; //父节点未被选中 且选中节点被取消选则 跳出
For j:=0 to ParentNode.Count-1 do //判断状态
begin
if (parentNode.Item[j].StateIndex=1) or (parentNode.Item[j].StateIndex=3) then
isAll:=False;
if (ParentNode.Item[j].StateIndex=2) or (parentNode.Item[j].StateIndex=3) then
isChecked:=True;
end; //For j:=0 to ParentNode.Count-1 do
if isAll then
parentNode.StateIndex:=2
else begin
if isChecked then
ParentNode.StateIndex:=3
else
ParentNode.StateIndex:=1;
end;
ChangeNodeParent(TreeView,parentNode,StIndex); //递归
end; //if ParentNode<>nil then
end; //if TreeView.Items.Item[i] = Node then
end; //For i:=0 to TreeView.Items.Count-1 do
end;
//在点击TreeView时
procedure TSysParameterForm.TreeView1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
MyHitTest : THitTests;
node: TTreeNode;
begin
node := TreeView1.GetNodeAt(x,y); //被点击的节点
MyHitTest := TreeView1.GetHitTestInfoAt(X,Y); //点击点
if htOnStateIcon in MyHitTest then //是否在图标位置
begin
//修改树的状态
if node.StateIndex = 2 then
begin
node.StateIndex := 1;
NodeChild(Node,1);
NodeParent(TreeView1,Node,1);
end
else begin
node.StateIndex := 2;
NodeChild(Node,2);
NodeParent(TreeView1,Node,2);
end;
end;
end;
//这样在点击树结点图标的时候相关结点的状态将联动
//多选的一个应用,根据选结果生成sql语句的条件
Function TSysParameterForm.TreeValue(Node:TTreeNode):string;
var
i:integer;
Sql:String;
P:^String;
begin
if Node.StateIndex = 2 then
begin
p:=Node.Data;
if Node.Count>0 then
Sql:=Sql+' Or ParaCode like '''+P^+'%'''
else
Sql:=Sql+' Or ParaCode='''+P^+'''';
end
else if Node.StateIndex=3 then
Sql:=Sql+ TreeValue(Node.GetNext); //递归
Result:=Sql;
end;
//总算把要写的东西写完了,感觉写的比较乱,大家凑合着看吧。马上春节了,祝大家春节快乐,明年多挣钱!!!

[@more@]

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/8683736/viewspace-906423/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/8683736/viewspace-906423/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值