TreeView控件的使用2007-05-09说在前面好多网友来信要求我们写一些 ActiveX 控件的文章,其实对此我们早有计划。记得去年我们筹划 BOE 工作时的时候,小李就写好了一篇关于 ListView 的文章,十分不幸的是小李的机器遭黑客攻击,这篇文章丢失了;这阵子小李又要应付一场考试,没有空闲撰写文章。于是我就勉为其难,代替小李完成 TreeView 控件的介绍,以后有关 ActiveX 的内容仍有小李负责。ActiveX 是扩展应用程序功能的重要手段,但市面上有关 Visual FoxPro 与 ActiveX 控件的介绍很少,好像ActiveX 与Visual FoxPro 无缘,其实绝大多数的 ActiveX 控件都可以在 Visual FoxPro 中运行。我们可以通过学习 VB 的演示程序了解他们(只要有范例程序,一般就会有 VB 的代码),在对象化程序代码方面 VB 的语法与 Visual FoxPro 是很相似的,VB 的代码稍加转换就是 Visual FoxPro 的代码了。将数据写入 TreeView 控件中看看我们的演示数据

这个数据库中有两个表格:一个是 Department 表,表示公司的“部门情况”;另一个是 Employee 表,反映“雇员”的情况。每一位员工对应一个部门,每一个部门有若干个雇员,这些雇员中有一位是部门主管(employee.manger=.t.),这两个表通过 depar_id 的字段关联。根据以上信息,我们希望在 TreeView 中列出下图的样子。

Imagelist 控件TreeView 中可以有图标,这些图表必须存放 Imagelist 控件中。Imagelist 控件是个不可视控件,它的的操作很简单,加入此控件到表单,设它的name属性为Imagelist(这一步做不做都一样,我们是为以后讲解方便)。打开它的属性设定窗口(右击该控件->弹出快捷菜单->选择“ImageListCtrl Properties”),如下图:

使用“Insert Picture...”按钮依次加入图片,按“确认”后退出。TreeView 控件的初始设定在表单中加入 TreeView 控件,设它的 name 属性为 TreeView(这一步做不做都一样,我们是为以后讲解方便)。接着我们就要设定它的一些基础属性了,在此控件的 Init 事件中加入如下代码:This.ImageList=ThisForm.imagelist &&设定图片的提供者为Imagelist控件
This.LabelEdit=1 &&不可自动编辑标签,设定为0的化为可以自动编辑标签
This.LineStyle=1 &&根线风格LineStyle 属性是指两个节点间的连线风格,有“树线”(0)和“根线”(1)两种风格。“树线”是指在兄弟节点与父节点之间连线,“根线”是指在兄弟节点与父节点之间连线之外,在各个根节点间也显示连线。加入数据到 TreeView 控件中在表单中添加一个过程:DrawTree,它的核心代码如下:LOCAL XNODE,XNODE1
WITH This.treeview
SELECT DEPARTMENT
SCAN ALL
XNODES=.NODES.ADD(,,"DEP"+ALLTR(STR(DEPARTMENT.DEPART_ID,5)),;
ALLTR(DEPARTMENT.DESC),1)
XNODES.EXPANDEDIMAGE=2 &&该节点展开时,该节点用的图标。
SELECT EMPLOYEE
SCAN FOR EMPLOYEE.DEPART_ID=DEPARTMENT.DEPART_ID
IF MANGER
XNODE1=.NODES.ADD(XNODES,4,"EMP"+ALLTR(STR(EMPLOYEE.EMP_ID,5)),;
ALLTR(EMPLOYEE.LAST_NAME)+" "+ALLTR(EMPLOYEE.FIRST_NAME),3,4)
XNODE1.BOLD=.T. &&部门负责人用粗体显示。
ELSE
.NODES.ADD(XNODES,4,"EMP"+ALLTR(STR(EMPLOYEE.EMP_ID,5)),;
ALLTR(EMPLOYEE.LAST_NAME)+" "+ALLTR(EMPLOYEE.FIRST_NAME),3,4)
ENDIF
ENDSCAN
XNODES.EXPANDED=.T. &&展开该节点
ENDSCAN
ENDWITH这里有一个十分重要的方法:ADD(orelative,irelationship,ckey,ctext,iimage,iselectedimage),它的作用是:添加一个节点到 TreeView 空间中。orelative:可选参数。指定已经存在的节点对象,它与 relationship 参数联合使用可以设定新添加控件与原有控件的关系。省略该参数表明新添加的节点是根级节点。
irelationship:可选参数。它与 relative 参数联合使用可以设定新添加控件与原有控件的关系,默认是下一个兄弟节点。
ckey:可选参数。设定该参数将便于对 TreeView 各节点的控制,单在每一个 TreeView 控件中,不可以有两个节点使用同一个 key。key是字符型的属性,但经验告诉我们不要以数字作为 key 的开头字符。
ctext:必填参数。在节点中显示的字符串。
iimage:可选参数。节点图标,它的取值来源于imagelist空间的图片索引。
iselectedimage:可选参数。节点被选中时显示的图标,它的取值来源于imagelist空间的图片索引。下面我们再来看一下irelationship参数的取值情况:0:第一个。新添加的节点放在 orelative 节点的所有同级节点的前面(兄弟关系)。
1:最后一个。新添加的节点放在 orelative 节点的所有同级节点的最后(兄弟关系)。
2:下一个。新添加的节点放在 orelative 节点的后面(兄弟关系)。
3:前一个。新添加的节点放在 orelative 节点的前面(兄弟关系)。
4:子节点。新添加的节点放在 orelative 节点的下级(父子关系)。 在上面的代码中,我们执行了两个 scan 循环。外面的循环是针对department表的,这里很重要的是记录下每一个部门节点对象,并以它作为产生员工节点的依据:XNODES=.NODES.ADD(,,"DEP"+ALLTR(STR(DEPARTMENT.DEPART_ID,5)),;
ALLTR(DEPARTMENT.DESC),1)内循环是针对employee表的,它把属于于某一部门节点的雇员信息写入部门节点之下,作为其子节点:.NODES.ADD(XNODES,4,"EMP"+ALLTR(STR(EMPLOYEE.EMP_ID,5)),;
ALLTR(EMPLOYEE.LAST_NAME)+" "+ALLTR(EMPLOYEE.FIRST_NAME),3,4)大家看到,XNODES 是外循环产生的“部门”节点,以它作为“orelative”参数就可以设定雇员节点与部门节点的“父子”关系了!上面的代码中,我为每个节点都指定了“ckey”参数,您可以省略,但我建议别这么做——后面还有许多功能的实现要靠它了!我对部门节点ckey的设定是这样的:“DEP”+部门编号;雇员节点ckey的设定是这样的:“EMP”+雇员编号。更多层次的节点怎么办?在这个实例中,我们只有两层节点,一定有人要问:更多层次的节点怎么办?我想如果有更多层次的节点,起编程原理与本例是一样的:记录下上层节点对象,在生产下层节点时使用。Visual FoxPro 的循环嵌套好像是6层,怎样“巧妙”的“突破”这个极限就留给大家思考了。不过我想:三、四层的节点在一般的数据库应用中是足够了(“会计科目”一般有个5、6级就够了)!什么时候执行DrawTree方法产生节点的代码被我放在了表单的过程中,那么什么时候执行它何时呢?我想在表单的init事件中调用它最合适,原因在于,表单的init事件较所有控件的init事件都晚,这样我们就可以在调用drawtree方法时不必关心“是否有控件还没有被建立”,就可以结合drawtree对其他控件进行设定。所以在表单的init事件中写入如下代码:This.drawtree还有几招NodeClick事件在示例中我们希望实现这个功能:选中雇员节点,表格employee的指针就指到该雇员的记录上。在 TreeView 控件的NodeClick事件中写入如下代码:LPARAMETERS node &&接受一个节点对象
IF LEFT(NODE.KEY,3)="DEP" &&如果是部门节点就不处理!
RETURN
ELSE
LOCATE FOR EMP_ID=VAL(RIGHT(NODE.KEY,LEN(NODE.KEY)-3)) &&记录定位
ThisForm.Navigator1.setcontrol &&在后文解释
THISFORM.REFRESH
ENDIF这一段代码很简单,我就不做详细的解释了。这里的核心问题就是:节点的ckey的应用!!!右击弹出菜单建立一个快捷菜单,如下图:

下面是菜单对应的 menu1.mpr 程序供大家参考:para obj &&得到对表单对象的引用
DEFINE POPUP 快捷菜单 SHORTCUT RELATIVE FROM MROW(),MCOL()
DEFINE BAR 1 OF 快捷菜单 PROMPT "刷新 TreeView 控件"
ON SELECTION BAR 1 OF 快捷菜单 ;
DO _0db019owf ;
IN LOCFILE("TREEVIEWMENU1" ,"MPX;MPR|FXP;PRG" ,"WHERE is MENU1 ")
ACTIVATE POPUP 快捷菜单
PROCEDURE _0db019owf
obj.treeview.nodes.clear &&清理去所有节点
obj.drawtree &&重新产生节点
接着我们在 TreeView 控件的 mousedown 事件中写入如下代码:LPARAMETERS button, shift, x, y
if button=2 && 确定鼠标右键单击
do menu1.mpr with thisform
endif将TreeView结合到程序中俗话说,独木不成林。TreeView虽好,但总不能是光杆司令吧!我们的实例中就介绍了将TreeView于其他控件结合在一起的问题。

在这个界面中,有一个“导航条”控件是我们介绍过的,详细情况请参看《编写简单导航条》一文。我认为就这个就这个界面来讲,所有控价基本上形成三个阵营,只有协调好他们就完成了任务。这三个阵营是:TreeView控件
导航条控件
绑定型数据显示控件其中TreeView 与绑定型数据显示控件的谐调已经在TreeView的NodeClick事件中解决;导航条控件与绑定型数据显示控件的协调由导航条控件自己解决,我们不必关心!所以这个实例中有两个问题是我们接着要讨论的:TreeView的NodeClick事件中移动表格指针对导航条控件各按钮的“可用、不可用”的影响;导航条控件对表格指针的移动对TreeView的影响。第一个问题很简单,我在设计导航条时就考虑了类似的问题,并把“自动检测表格指针,确定各个按钮的可用、不可用”的功能封装在了Setcontrol方法中了。所以我们只要在TreeView的NodeClick事件中写入:ThisForm.Navigator1.setcontrol第二个问题也不难解决,在表单中为导航条的每一个按钮的Click事件加入如下代码:dodefault() &&先执行默认的导航条“类”的代码,完成正常的导航功能!
ThisForm.treeview.nodes("EMP"+ALLTR(STR(EMPLOYEE.EMP_ID,5))).selected=.t.
*还是通过节点的ckey选定节点!!!
ThisForm.treeview.setfocus更多的资料本文介绍了 TreeView 控件的一点皮毛,希望您能满意。要得到更详细的信息,请参看北京希望电子出版社出版的《Microsoft Visual Basic 6.0 Control Reference 控件参考手册》(上、下,合计110元人民币,书店应该有卖)。这是微软写的,很权威、很详细,我绝对不是在推销牟利。我们认为,您应该有一本这样的手册,就像 《Visual FoxPro 函数、命令手册》一样,它是必备的!