在Visual FoxPro中使用TreeView控件
如果用户在设计程序时,需要用一种树形结构生动形象地显示具有不同层次的数据,那么TreeView控件将是最合适的选择。TreeView控件可以将用户选定的数据,也可以是从数据库中检索出来的数据,供用户自由的选择、展开或折迭收起。TreeView控件主要用以显示层次数据之间的关系。TreeView控件的特点包括以下几个方面:
1、将相互间有联系的数据用图形与文字方式以树形描绘,以树形节点(Node对象)的形式展开或收起数据;
2、每一个节点可以用图标和文本标签来描述;
3、标签可以设置为是否允许修改的属性;
4、对层次深度和节点数目无限制,只受系统资源的限制。
另外,使用TreeView控件对管理信息量很大的数据来说,是一个很好的方式,因为用户能从中简单快速的选择到所需要的数据。Windows资源管理器就是TreeView控件、ImageList控件与ListView控件配合应用的一个例子。
TreeView控件是Microsoft
Visual Studio 中的一个控件,它是Mscomctl.ocx文件中的一组ActiveX控件的一部分。当安装了Visual FoxPro或Visual
Basic后就可以在Windows的System目录中找到这个文件。为了在发布的应用程序中使用TreeView控件,必须将Mscomctl.ocx文件与应用程序一起做成安装盘发布。这个控件在Visual
FoxPro 5.0中的版本是5.0,但是该控件的5.0版本不支持6.0版本的一些属性、方法和事件,所以要尽量使用6.0版本。如果没有Visual FoxPro
6.0也可以,只要找到Mscomctl.ocx这个文件并将其注册,就可以在Visual FoxPro
5.0中使用该控件的6.0版本。
一个TreeView控件由若干Node对象组成,一个Node对象就是一个节点。一个TreeView控件只能有一个根节点(Root)。一个节点有若干子节点(Child),但必须有(除根节点以外)也只能有一个父节点(Parent)。创建TreeView控件之后,可以通过设置属性与调用方法操作各Node对象,包括添加、删除等。可以编程展开与折迭Node对象显示或隐藏子节点。
在FoxPro主菜单的“工具”菜单的“选项”中“控件页框”,将“ActiveX控件”的“Microsoft
TreeView Control,Version
6.0”选定,然后单击确定,即可将TreeView控件加入“表单控件工具栏”。单击表单控件工具栏的查看类按钮,弹出一个菜单,单击其中的“ActiveX控件”,即可在“表单控件工具栏”看到TreeView控件。将ImageList控件加入“表单控件工具栏”的方法与TreeView控件的方法一样。
TreeView的外观只要看一下Windows资源管理器就很清楚了。要使用TreeView控件与其他普通控件一样,TreeView控件也是由属性、方法和事件控制的(以下假设表单加入的TreeView控件的Name属性是Tree)。设计时在控件上单击鼠标右键会弹出一个快捷菜单,单击“TreeCtrl
Properties”就会出现一个标题为“TreeCtrl属性”的窗口,在这个窗口中可以设置控件的一些属性。但这里有一个“Bug”,如果在这里设置与ImageList控件的关联存不上盘,当时好象存上了,再次打开还是老样子。在后面Image属性的介绍中再详细说明如何解决这个问题。
一、Node对象的属性:(在控件上单击右键,单击弹出式菜单的最后一项即可修改属性。)
1、Key(键值):一个节点的键值必须是唯一的字符型的值,控件用键值来区分节点,如果某两个节点的键值相同将会出错。键值可以由字母、下划线、空格和数字等可打印字符组成,但不能是纯数字的字符串,否则会出错。如用于显示磁盘目录,可以用路径作为键值。ThisForm.Tree.SelectedItem.Key或ThisForm.Tree.Nodes.Item(Node.Index).Key返回控件当前选定的节点的键值。
2、Parent(父节点):一个节点只能有一个父节点,根节点没有父节点。ThisForm.Tree.SelectedItem.Parent.Key返回控件当前选定的节点的父节点的键值。如果当前选定的节点没有父节点,则这行代码就会出错。可用ThisForm.Tree.SelectdeItem.Parent是否为空值(Isnull)或ThisForm.Tree.SelectedItem.Index是否等于1判断该节点是否有父节点。
3、Child(子节点):ThisForm.Tree.SelectedItem.Child.Key返回TreeView控件中当前选定的节点的第一个子节点的键值,如果该节点无子节点,该行代码会出错。可以使用下面这个属性看一下它有没有子节点。
4、Children(子节点数目):ThisForm.Tree.SelectedItem.Children返回TreeView控件中当前选定的节点的子节点的数目。
5、Text(节点标签):该属性返回用户所看到的节点的标签,不同节点的标签允许相同,也可以是空字符串。
6、LabelEdit:该属性为0时双击标签,即可编辑标签。等于1时不可编辑。编辑标签时会触发AfterLabelEdit和BeforeLabelEdit事件。
7、Singlesel:该属性为Ture时,被选定的节点如果有子节点将自动展开显示子节点。
8、Sorted:当该属性为Ture时,节点按字母顺序排列。
9、Expanded:当该属性为Ture时,表明该节点是展开的。
10、Next:返回TreeView控件中同为Node节点的父节点的子节点的下一个兄弟节点的对象。
11、Previous:返回TreeView控件中同为Node节点的父节点的子节点的前一个兄弟节点的对象。
12、Style:返回或设置图形类型(图标、文本、+/-号、直线)以及出现在TreeView控件中每一Node对象上的文本类型。
设置值
说明
0 仅为文本
1 图标和文本
2 +/-和文本
3 +/-、图标和文本
4 直线和文本
5 直线、图标和文本
6
直线、+/-和文本
7 (默认)图标、直线、+/-和文本
13、Count:
Nodes集合中的节点总数。该属性是Nodes集合的属性。
14、Index:增加一个Node对象时就给其分配了索引值,保存在该Node对象的Index属性中。新成员的该属性值也是Nodes集合的Count属性值。
15、Image:对应节点的图标键值。TreeView控件中的图标是另外一个控件ImageList提供的,在这个控件中为每一个图标指定一个唯一的键值,要改变节点的图标只要改变这个属性就可以了。在Visual
FoxPro中TreeView控件有一个Bug,在设计时无法与ImageList相关联(在VB中没有此现象),这个问题可以解决,在表单的Init事件中加入下面三行就可以解决这个Bug(假定加入的ImageLiat控件Name属性为Images)。
This.Tree.ImageList
=
This.Images
This.ListView.SmallIcons=This.Images
This.ListView.Icons=This.Images
二、方法:
1、Add:用于向TreeView控件加入节点
语法:Object.Add (Relative,Relationship,
Key, Text, Image,
Selectedimage)
①Object:对象名称,本例为ThisForm.Tree;
②Relative:可选参数。已存在的Node对象的索引或键值。加入根节点时省略,加入子节点时是父节点的键值。
③Relationship:可选参数。指定Node对象的相对位置关系。
relationship值的设置如下:
常量
值 描述
TvwFirst 0 第一个。该Node节点放在relative命名的所有同级节点的前面
TvwLast 1
最后一个。该Node节点放在relative命名的所有同级节点的最后。后续增加的节点可以在该节点之后
TvwNext 2
(缺省)下一个。该Node节点放置在relative命名的节点之后
TvwPrevious 3
前一个。该Node节点放置在relative命名的节点之前
TvwChild 4
子节点。该Node节点是relative命名节点的子节点
④Key:Node对象的键值。必须是字符串。
⑤Text:Node对象的标签。必须是字符串。
⑥Image:可选参数。与ImageList控件关联的图像索引或键值,也就是标签前面的图标。
⑦Selectedimage:可选参数。当Node对象选中时,所显示的与ImageList控件关联的图像索引或键值。比如:节点未选中时显示关闭的文件夹图标,选中时显示打开的文件夹图标。
2、Remove:从Node集合(Nodes)中删除一个节点。该节点的子节点将被一同删除(如果有的话)。
语法:Object.Nodes.Remove(Key)
①Object:对象名称,本例为ThisForm.Tree。
②Key:Node对象的键值。
3、Nodes(
):用Node对象的索引返回对象的引用。
语法:Object.Nodes(Index)
①Object:对象名称,本例为ThisForm.Tree。
②Index:增加一个Node对象时给其分配的索引值。第一个加入的节点Index为1,第二个为2,依次类推。
4、Nodes(
):用Node对象的键值返回对象的引用。
语法:Object.Nodes(Key)
①Object:对象名称,本例为ThisForm.Tree。
②Key:
Node对象的键值。
三、事件:
1、BeforeLabelEdit:在试图编辑当前选中的Node对象标签时该事件发生。在LabelEdit=1时该事件失效。
2、AfterLabelEdit:在编辑当前选中的Node对象标签后事件发生。在LabelEdit=1时该事件失效。
3、Collapse:将已展开的节点折叠时事件发生。该事件发生在标准的Click事件之前。该事件返回被折叠的Node对象的引用。触发Collapse事件有三种方法:
①设置Node对象的Expanded属性为False.;
②双击展开的Node对象;
③TreeView控件的Style属性为2、3、6或7时,单击“-”图象。
4、Expand:在控件有子节点的Node对象扩展开时事件发生。该事件发生在标准的Click事件之前。该事件返回被展开的Node对象的引用。触发Expanded事件有三种方法:
①设置Node对象的Expanded属性为True.;
②双击带有子节点的Node对象;
③TreeView控件的Style属性为2、3、6或7时,单击“+”图象。
5、NodeClick:单击Node对象时该事件发生。该事件发生在标准的Click事件之前。在单击节点对象之外的控件任何部位,标准的Click事件发生。
四、举例:
1、用表中数据增加节点。该表有_Parent(父节点键值),_Key(本节点键值),Text(节点文本),3个字段。
select
LS
SCAN
IF ALLTRIM(parent) = “0″
Node =
this.nodes.add(,1,ALLTRIM(LS.key),ALLTRIM(LS.Text),,)
*加入根节点
ELSE
Node
=this.nodes.add(ALLTRIM(LS.parent),4,ALLTRIM(LS.key),ALLTRIM(LS.Text),,)
ENDIF
ENDSCAN
2、将所有节点信息存入一个临时表。该例将控件每一个节点的键值(_key)、标签(T_text)和父节点键值(_parent)存入临时表:LS。
O=ThisForm.Tree
*为了减少代码数量,建立一个对象变量来代替很长的对象名称。
Create Cursor LS (_key c(10),T_text
n(40),_parent c(10)) *创建临时表LS
For I=1 to O.Nodes.count()
Insert Into LS
Value(O.Nodes(I).Key,O.Nodes(I).Text,O.Nodes(I)._parent.Key)
Endfor
3、获取一个节点的所有父节点与子节点,并将其存入一个临时表LS1。临时表LS1存入所有节点的父节点键值(_parent)、本节点键值(_key)和本节点标签(T_text)。LS2用来模拟堆栈。
O=ThisForm.Tree1
*建立对象变量既可以减少代码输入又可增加可读性只是速度稍慢
Create Cursor LS1 (_parent c(10),_key
c(10),T_text c(60))
Create Cursor LS2 (_index n(10))
Select
LS2
node_index=o.SelectedItem.Index *Tree1当前选定的节点索引值
*下面代码获取该节点的父节点
Do
while .t.
Insert Into Ls2 Value(o.Nodes(node_index).Index)
If
o.Nodes(node_index).Parent.Index=1
Exit
Endif
node_index=o.Nodes(node_index).Parent.Index
Enddo
*由于上面代码获取的父节点排在子节点的后面,下面这个循环把顺序倒过来,使辈分高的节点排在前面。
Do
while .t.
Go Bottom
node_index=LS2._index
If
node_index=0
Exit
Endif
Insert Into LS1
Value(o.Nodes(node_index).Parent.Key,;
o.Nodes(node_index).Key,;
o.Nodes(node_index).Text)
Delete
Enddo
Select LS2
child_number=o.SelectedItem.Children
If
Child_number>0
node_index=o.SelectedItem.Index
Do while
node_index#0
Insert Into LS2 Value(o.Nodes(node_index).Child.Index)
Insert
Into Ls1
Value(o.Nodes(node_index).Key,;
o.Nodes(node_index).Child.Key,;
o.Nodes(node_index).Child.Text)
For
i=2 to child_number
node_index=LS2._index
Insert Into LS2
Value(o.Nodes(node_index).Next.Index)
Insert Into LS1
Value(o.Nodes(node_index).Next.Parent.Key,;
o.Nodes(node_index).Next.Key,;
o.Nodes(node_index).Next.Text)
Endfor
Select
LS2
Go Bottom
Do while .t.
node_index=LS2._index
If
node_index=0
Exit
Endif
child_number=o.Nodes(node_index).Children
If
child_number=0
Delete
Go
Bottom
Loop
Else
Delete
Exit
Endif
Enddo
Go
Bottom
Enddo
Endif
Select
Ls2
Use
五、其它:
1、TreeView控件缺少一个检测节点是否存在的函数(不知道是不是我没有发现),当向一个控件加入一个节点时,如果键值重复就会出错。有一个办法“解决”问题,我暂且称之为“赖皮”算法,就是用On
Error Lerror=.T.忽略所有错误,之后再用On Error
解除就会相安无事。见下例。如果没有第一行,当检测的节点不存在时,就会有错误窗口弹出。
On Error Lerror=.T.
If
Isnull(Thisform.Tree.Nodes(Key))
Return(.f.)
Else
Return(.T.)
Endif
On
Error
2、最后简要介绍ImagesList控件的使用方法。
ImageList控件与TreeView控件都在文件Mscomctl.ocx中。在表单运行时该控件不可见,它的任务就是向其他控件提供图标。设计时在控件上单击鼠标右键,会弹出一个快捷菜单,单击“ImageListCtrl
Properties”就会出现一个标题为“ImageListCtrl
属性”的窗口,在这个窗口中可以设置控件的属性。在这里先设置图标的大小,加入图标后大小就无法改变了。加入图标并指定图标的索引或键值后,就可以由其他控件随意调用图标了。5.0版的ImageList控件只能加入图标,6.0版的可以加入图片(当然不能太大),所以尽可能使用高版本。