PowerBuilder中的TreeView 控件为树状游览,类似于WINDOWS的资源管理器,其特点是信息项呈树状层次结构,能更清晰地表现主、细目关系 ,操作非常方便。在应用中可将其与DataWindow 配合使用, 一个提供信息的分类体系,一个提供具体信息,达到珠连碧合的奇妙效果。它特别适用于多级信息的分类检索, 是多级菜单所无法比似的,它的表现形式深受程序设计人员和广大用户的喜爱,在许多应用软件中都能看到她的英姿。
在PowerBuilder下,TreeView 控件的应用较其它控件要复杂得多,刚接触它时往往有些不知所措。但如果将它的机理搞清楚,掌握它也不是很难的事。下面我结合长白公司图书分类检索的实例,把TreeView 控 件的使用方法和大家探讨一下。
一、应用TreeView 控件的一般步骤
1、 建立一个应用,并设好与数据库的接口,这是操作数据库的前提。
2、 在应用中建一应用窗口W_1,在其上加入二个名为dw_3和dw_4的datawindow控制对象和一个名为TV_1的TreeView对象。
3、 修改DW_3属性
General:把Datawindow object name填写一个已存在的名为DW_date的datawindow(注意:它与datawindow控制对象是不同的),用于生成树视图项,将其Visible项设为不可见。
4、 修改DW_4属性
General:把Datawindow object name填写一个已存在的名为DW_TS的datawindow对象,用于显示查询出的具体内容。
5、 编辑TV_1的属性
TreeView的树视图项不能直接编辑,必须在Script中编写程序。
Picture:在Picture Name中加入四个不同的图标,用于代表树视图中的两个层次(一、二级)、两种状态(未选、选中)。
General:可根据具体应用设定是否选中,其中:
Delete Items:运行中是否允许删除表项。
Disable PragDrog: 运行中是否允许拖放表项。
Edit Labels: 运行中是否允许单击表项来改变表项的标题。
Show Buttons:是否在表项放显示+-按钮,有示相对的扩展和收缩。
Hide Selection:当该控件失去焦点时,选中项是否以高亮度显示。
Show Line:表项间是否加一竖线。
Lines At Root:所有根层表项是否用竖线连接。
Indentation:子表项相对于父表项的向右缩进度。
6、 编写TV_1的Script
这里是TreeView控件的关键,也是难点。
二、TreeView 控件的信息构成及创建
树视图项TreeViewItem是TreeView 控件的基本信息单位,树视图项的生成一般有二种方法,一种是先生成根层视图项,再在应用中动态生成下级视图项,另一种是把全部树视图项一次和成。两种方法各有优点,请根据具体情况选用,本例中采用后一种方法。
1、 树视图项TreeViewItem的主要属性
Label:String 型,树视图项的显示信息。
Data:Any型,树视图项的内部值。
Level:Integer型,树视图项在树视图中级别。
Children:Boolean 型,它决定该项是否有下一层(如图中书名)。
PictureIndex:Integer 型,该项非选中时所用的图标在图标队列中的编号。
SelectedPictureIndex:Integer型,该项选中时所用的图标在图标队列中的编号。
2、 生成TreeViewItem项用到的函数
InsertItemFirst():将加入项作为第一项
InsertItemLast():将加入项作为最后一项
InsertItem():将加入项插入到指定项的后面
InsertItemSort():按顺序放置。
3、 TreeView的常用事件
Constructor: 该事件在控件创建时触发,可在这里构造TreeViewItem。
Click:单击TreeViewItem项时,执行查询程序。
Double Click: 双击TreeViewItem项时,执行查询程序。
ItemPopulate 事件: 该事件在某TreeViewItem项首次展开时触发, 触发的同时系统会将 该TreeViewItem项的句柄通过参数handle 传递过来。它主要用来生成对应项的下层信息项。主要用于第一种方法。
三、 事件代码
1、树视图控件TV_1的constructor事件代码 wanqi 1999.6.28
integer li_rowcount,li_row
string li_current_dn,li_last_dn,li_current_ei,li_last_ei
//声明二个树视图的实例变量
treeviewitem itvi_level_one,itvi_level_two
//long ii_h_l_one
//long ii_h_l_two
dw_3.settransobject(sqlca)//dw_3
为隐含的数据窗口,存有生成树的数据
li_rowcount=dw_3.retrieve()//行数
dw_3.setsort("lb,pm")
dw_3.sort()
//生成树视图的各级树视图项
for li_row=1 to li_rowcount
li_current_dn=dw_3.object.lb[li_row]
//DW_3对象中"LB类别"
li_current_ei=dw_3.object.pm[li_row]
//DW_3对象中"PM品名"
if isnull (li_current_ei) then
li_current_ei=""
end if
if li_current_dn< >li_last_dn then
//IF LB不与一级视图项重复
//设置一级树视图项
itvi_level_one.label=dw_3.object.LB[li_row]
//视图项的显示信息
itvi_level_one.level=1 //级别
itvi_level_one.data=li_current_dn
//视图项的内部信息
itvi_level_one.pictureindex=1
//没选中时所用的图标序号
itvi_level_one.selectedpictureindex=3
//选中时使用的图标序号
itvi_level_one.children=(li_current_ei< >' ')
//树视图是否有下一级
ii_h_l_one=this.insertitemlast(0,itvi_level_one)
//将项加入到一级树的最后一项
end if
//设置二级树视图项
if li_current_dn< >li_last_ei then
if li_current_ei<>' ' then
itvi_level_two.label=dw_3.object.pm[li_row]
itvi_level_two.level=2
itvi_level_two.data=li_current_dn
itvi_level_two.pictureindex=2
itvi_level_two.selectedpictureindex=4
itvi_level_two.data=li_current_ei
itvi_level_two.children=false
ii_h_l_two=this.insertitemlast
(ii_h_l_one,itvi_level_two)
//将项加入到二级树的最后一项
end if
end if
li_last_dn=li_current_dn//设比较项
li_last_ei=li_current_ei
next
2、tv_1控件的clicked事件代码
string s1
treeviewitem ii
this.getitem(handle,ii)
s1=string(ii.label)
choose case ii.level
case 1
//过滤类别
dw_4.setfilter("lb='"+s1+"'")
dw_4.filter()
case 2
dw_4.setfilter("pm='"+s1+"'")
dw_4.filter()
//过滤书名
end choose
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/oury/archive/2005/02/06/282433.aspx
为PB的TreeView实现同步选择
TreeView控件能够清晰地表示层次关系,在TreeView编程中,常见的一个问题是同步选择(即选择一个节点时,同时选择该节点的所有子节点;不选一个节点,同时去除该节点的祖先节点选择标志)。
要实现这种效果,本来不难,只需要在检测到节点选择状态变化时,遍历节点的祖先或者后代节点进行同步即可。但是PB并没有提供检测节点选择状态变化的事件。
让我们来看看PB中TreeView节点选择状态的表示。当TreeView的CheckBoxes属性为True时,每个节点包含一个复选框。选中复选框时,StatePictureIndex属性为2,未选中则为1。当选择某个节点时,首先触发TreeView的Clicked事件,处理完Clicked事件后再对StatePictureIndex进行设置。显然,如果我们能够将Clicked事件前后节点的StatePictureIndex属性值进行比较,我们就可以判断节点的选择状态是否发生了变化。按照这个思路,疏理PB的事件模型,发现采用Post的调用事件方法,可以实现将一个事件加入控件消息序列,在处理完当前事件后再对新加入的事件进行处理。
至此,我们得到了如下的解决方案:
1. 为TreeView控件添加一个用户事件ue_synchronizechildren(long handle, integer state)
代码如下:
long childitem
treeviewitem tvitem
getitem(handle, tvitem)
tvitem.statepictureindex=state
setitem(handle, tvitem)
childitem=this.finditem(ChildTreeItem!, handle)
do while(childitem<>-1)
this.Event ue_synchronizechildren(childitem, state) //递归遍历后代结点
childitem=this.finditem(NextTreeItem!, childitem)
loop
添加一个用户事件ue_synchronizeparent(long handle, integer state),如下:
long parentitem
treeviewitem tvitem
getitem(handle, tvitem)
tvitem.statepictureindex=state
setitem(handle, tvitem)
parentitem=this.finditem(ParentTreeItem!, handle)
if parentitem<>-1 then
this.Event ue_synchronizeparent(parentitem, state)
end if
2. 添加一个用户事件ue_statechanged(long handle, integer prevstate)检测节点的选择状态, 如果发生了变化则调用ue_synchronizechildren同步后代节点,并根据需要通过ue_synchronizeparent同步祖先节点
代码如下:
treeviewitem tvitem
getitem(handle, tvitem)
if tvitem.statepictureindex=prevstate then
return
else
this.Event ue_synchronizechildren(handle, tvitem.statepictureindex)
if tvitem.statepictureindex=1 then //如需实现文末提及的功能,可在此处添加代码。
this.Event ue_synchronizeparent(handle, tvitem.statepictureindex)
end if
end if
3. 在Clicked事件中,添加如下调用:
treeviewitem tvitem
getitem(handle, tvitem)
post event ue_statechanged(handle, tvitem.statepictureindex)
为了简单起见,上面的代码并没有考虑当选上一个结点时,它的所有兄弟已被选择,因此父结点也应该被选择的情况,如有需要,请读者自己完成。
本文通过树的深度优先算法来实现TreeView的查询。
由于PB的TreeView控件没有提供查找项的功能,因此本程序算是对此一缺憾的补缺。
调用语法:findtreeitem(tv_tree, findby, data)
调用参数:
tv_tree: TreeView, 指明在tv_tree树中查找
findby: boolean, 指明查找方式
false - 按标签(Label)查找
true - 按附加数据(data)查找
data: any, 指明要查找的内容
返回值:long型。若找到,返回找到的项的Handle;
若没有找到,返回0;
若出错,返回-1
程序代码如下:
public function long findtreeitem(TreeView tv_tree,
boolean findby, any data);
long ll_hdl[]
integer li_cnt
treeviewitem lt_tvi
li_cnt = 1
ll_hdl[1] = tv_tree.FindItem(RootTreeItem!, 0)
do while li_cnt > 0
if tv_tree.GetItem(ll_hdl[li_cnt], lt_tvi)
= -1 then return -1
if findby then
if lt_tvi.data = data then
return ll_hdl[li_cnt]
end if
else
if lt_tvi.label = string(data) then
return ll_hdl[li_cnt]
end if
end if
if lt_tvi.children then
li_cnt ++
ll_hdl[li_cnt] = tv_tree.FindItem(
ChildTreeItem!, ll_hdl[li_cnt - 1])
else
ll_hdl[li_cnt] = tv_tree.FindItem(
NextTreeItem!, ll_hdl[li_cnt])
end if
do while ll_hdl[li_cnt] <= 0
li_cnt --
if li_cnt = 0 then exit
ll_hdl[li_cnt] = tv_tree.FindItem(
NextTreeItem!, ll_hdl[li_cnt])
loop
loop
return 0
end function