应用语言学研究什么:vfp高级教程 TreeView 控件

来源:百度文库 编辑:中财网 时间:2024/04/28 05:27:00

VFP 5.0的新的ActiveX控件TreeView是一个有力的、可视的、吸引人的工具, 你可以将它使用于许多应用程序。 但是,其使用的复杂和技术文档的简单使很多人望而却步。本文探索了一些使用TreeView的有用的技术。
VISUAL FoxPro 5.0 包含了很多新的ActiveX (原来的OLE) 控件,这些控件可以为你的应用程序增加很多新功能。 这些控件包括TreeView、ListView、StatusBar、和CommonDialog 控件, 允许你为你的应用程序建立Windows 95 风格的界面。 ActiveX 控件很容易找到: 在表单控件工具栏中的查看类菜单中选择ActiveX 控件, 会出现30 个新的控件。 简单的从工具条拖放一个控件到一个表单中,就象使用VFP自身的控件一样, 给它一个名字, 设置一些属性, 等等… 在附加到VFP 的属性表单, 每一个控件都有一个定制的属性表。 要访问这个属性表, 在控件上右击鼠标并从出现的快捷菜单中选择适当的项。

TreeView 控件

我的意见是, 最重要的新控件是TreeView。 你已经使用过TreeView 控件-它被用于Windows Explorer的左边窗口中, 以及VFP的项目管理和类浏览器。 该控件在视觉上比 VFP 3.0 中的 Outline 控件更吸引人, 且更容易处理。 例如, 你要使节点显示你必须顺序添加节点到 Outline 控件。 在 TreeView 控件中你可以以任意顺序添加节点,因为在添加新节点时你可以指定哪一节点是父节点。
你可以用 TreeView 控件来做许多事。 以下是一些例子:
    1.在库存控件系统中显示一些原始单据清单。
    2.用于向下延伸显示。 例如, 你可以显示一个客户清单并为该客户向下延伸到订单的订货和产品。           另一个示例是区域表及所属销售人员及各销售人员的客户, 等等。
    3.改善组织图表: 分隔主管人员扩展的分支,及其员工。
    4.创建任何其它层次显示信息。
TreeView 控件位于 COMCTL32.OCX (在 \WINDOWS\SYSTEM 中)。 如果你安装了 Visual Basic Control Creation Edition , 会安装一个更新版本的 COMCTL32.OCX ,这个版本有许多问题。 如果你的 COMCTL32.OCX 大于 325K , 你应该从你的 VFP 5.0 CD-ROM 上重新安装一个正确的版本(位于光盘上的 \OS\SYSTEM 目录中)。很多用户诉说在他们的系统中 TreeView 不能正确工作,这也可能是原因之一。
TreeView 控件的帮助文档在 CTRLREF.HLP 中(在 VFP 主目录中)。 这个 TreeView 控件的帮助文件的内容可能是所有 ActiveX 控件中最可怜的帮助文件了(可以说是高深莫测): 它的组织混乱, 内容在多处有误, 且经常是很模糊, 尤其在文档中的如何访问一些方法和属性的地方更是如此。
TreeView 控件允许你处理三个不同类型的对象:TreeView 控件自身, 节点集合和节点对象。 节点集合与表单中的控件集合一样; 它允许你用不同的索引号来访问个别节点对象。 但是, 也可以不通过节点集合来访问不同的节点对象。 例如, TreeView 的SelectedItem 属性是一个选定节点的对象引用, 因此象 NodeClick 和 Expanded 这样的方法接受一个节点的对象引用作为参数。
我将分别探讨这三个对象类型的属性, 事件和方法。在示例表单TREEVIEW.SCX 中展示了我在本文中描述的许多技术。

TreeView 控件的方法和事件

TreeView 控件响应一些我们觉的VFP 控件的方法和事件: Click, DblClick, Drag, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, Move, Refresh, SetFocus, ShowWhatsThis, 和 ZOrder。 有一个很大的例外: 没有 RightClick 事件。 在稍后,你会看到如何处理这种情况。
除这些方法和事件外,TreeView 控件有一些它自己的方法和事件(帮助文件中列出的 Clear 和 Remove 方法, 实际上是属性节点集合而不是 TreeView 控件自己的):
BeforeLabelEdit 和 AfterLabelEdit 发生在标签被用户修改前和修改后(就象在 Windows 的资源管理器中, 你可以单击选定的节点并修改它的文本(在稍后你可以看到如何废止该自动编辑功能)。 该事件中的代码通常用于在某处保存新的文本, 如一个表中的字段。
Collapse 和 Expand 当用户收缩或扩展一个节点时激发。 它们接受的参数是选定节点的对象。 收缩或扩展一个节点不会使该节点成为活动的节点, 这一点通常会使用户糊涂。 添加以下代码到 Collapse 和 Expand 事件来确保存该节点成为活动的节点(调用了我将在稍解释的 NodeClick(), 它们只在你在这些方法中有一些自定义代码是才是必须的):

lparameters node
Node.Selected = .T.
This.NodeClick(Node)

GetVisibleCount 显示控件中全部可见的节点数。 如果控件可以显示最后的节点,该数可能看起来会很小。
HitTest :如果传递的 X 和 Y 座标上存在节点,它返回一个节点对象引用,否则返回 .NULL. 这里有一个新的问题: HitTest 期待 X 和 Y 座标值是缇(twips) (用于 Visual Basic), 但 VFP 使用象素。 以下代码将转换象素值到缇。TREEVIEW 示例表单的 INIT 中有这些代码, 并有两个自定义属性来保存计算值: nTreeFactorX 和 nTreeFactorY。

local liHWnd, ;
liHDC, ;
liPixelsPerInchX, ;
liPixelsPerInchY

* 定义一些常数。

#define cnLOG_PIXELS_X 88
* 来自 WINGDI.H
#define cnLOG_PIXELS_Y 90
* 来自From WINGDI.H
#define cnTWIPS_PER_INCH 1440
* 每英寸1440 缇

* 定义一些 Windows API 函数

declare integer GetActiveWindow in WIN32API
declare integer GetDC in WIN32API ;
integer iHDC

declare integer GetDeviceCaps in WIN32API ;
integer iHDC, integer iIndex

* 为 VFP 取得图形设备(device context)。
liHWnd = GetActiveWindow()
liHDC = GetDC(liHWnd)


* 取得每英寸象素值。
liPixelsPerInchX = GetDeviceCaps(liHDC, cnLOG_PIXELS_X)
liPixelsPerInchY = GetDeviceCaps(liHDC, cnLOG_PIXELS_Y)

* 取得每象素缇并保存到表单属性中。

with This
.nTreeFactorX = cnTWIPS_PER_INCH/liPixelsPerInchX
.nTreeFactorY = cnTWIPS_PER_INCH/liPixelsPerInchY
endwith


HitTest() 有助于解决一个处理鼠标右击事件。 你可能想在用户在节点上右击时显示弹式菜单, 但在节点上右击时不会使它成为选定节点。 在控件的 MouseDown 事件中用以下代码来处理这一点(因为没有 RightClick 事件,所以这是你能处理右击的唯一办法)。 同时, 调用 NodeClick() 仅当你在该方法中放有代码时才是必须的:

lparameters Button, Shift, X, Y
local loNode
if Button = 2

* 如果这是右鼠标键, 取得鼠标下的节点的引用。
loNode = This.HitTest(X * Thisform.nTreeFactorX, ;
Y * Thisform.nTreeFactorX)
* 如果有可用的节点, 选择它。

if not isnull(loNode)
loNode.Selected = .T.
This.NodeClick(loNode)
endif

* 现在显示右击快捷菜单
else
* 处理必要的鼠标左击
endif

NodeClick: 当用户在节点上单击时激活 (在 Click 事件之前)。 NodeClick 接受一个选定节点的对象引用作为参数。 该方法被典型地用于在一个节点被选定时更新一些东西(例如其它控件的值)。 如果 NodeClick 中的代码执行时花的时间太长, 选定项会被高亮显示但以前的项会被断续线包围。 移动鼠标(即使没有单击鼠标按钮) 会恢复以前项的高亮显示。 要避免这一问题, 可以在 NodeClick 事件中放入以下代码来保证节点被单击而成为选定项:
Node.Selected = .T.
StartLabelEdit: 通常被用于编辑节点的标签。 它用于 LabelEdit 属性被设置为1-手动时。

TreeView 控件属性

如同事件一样, TreeView 控件支持一些其它控件所拥有的属性, 包括 DragIcon, DragMode, Enabled 和 Visible。 许多 TreeView 特定属性可以在设计时在 TreeView 控件上单击鼠标右键调出的 TreeView 控件属性表中设置。 包括 Style, LineStyle, Indentation, PathSeparator 和 HideSelection。

在属性表中可编辑的属性中, 你最需要改变其默认值的是:

Style: 指明 TreeView 控件显示什么。 可以选择是否显示图像,线条, 加号/减号等。
LineStyle: 指明显示根线。 如果没有设置该属性为 1-根线, 无论 Style 是如何设置的,顶级对象上都不会有加号/减号标记。
LabelEdit: 如果你不想让用户修改各节点上的文本, 设置该属性为 1-手动。
ImageList: 一个 ImageList 控件对象引用,它包括了控件中的节点使用的图象; 参见本文 ImageList 一章中关于该简单控件的说明。 不幸的是,该属性不能可视化地进行设置; 你必须以编程方式用代码进行设置,象下面一样在表单的 Init 方法中:
This.oTree.ImageList = This.oImageList.Object

注意你需要在表单的 Init 事件中而不是 TreeView 的 Init 事件中进行上述设置, 因为 TreeView 可能会在 ImageList 之前实例化, 如果这样,试着设置 ImageList 属性为一个尚不存在的对象将会失败。 同时, 注意上述代码中的”object” 关键字; 这是必须的。
Indentation: 子节点缩进多少。
HideSelection: 如果没有关闭这一选项, 当 TreeView 控件失去焦点时,选定节点不会保持高亮度。 这很容易把用户糊涂。
Font: 用于节点文本的字体名字, 字号和字型。该属性可从属性表中修改(注意帮助文件中说明了 ScrollBars 属性, 但没有该属性):
DropHighlight: 该属性用于支持TreeView 控件的 DragOver 事件, 因此可以在鼠标经过一个节点时,高亮显示该节点。 但是, 这只会在 VFP 6 中的版本 6 的 TreeView 控件中才实现了; 在老版本的控件中设置该属性为节点对象时,会引发一条”类型失配” 错误。 在这种情况下, 要达到同样目的,可用 HitTest() 来检查鼠标经过的是哪一节点并设置该节点的 Selected 属性为 .T. TREEVIEW 示例表单在 DragOver 事件中使用了该技术。
Nodes: 控件中的节点集合引用。
SelectedItem: 当前选定节点对象的引用。
节点集合方法

以下是TreeView 控件的节点集合的方法(被指定的 .Nodes 引用, 其中 是 TreeView 控件名):

 

Add: 添加一个新节点并返回它的引用。 它使用以下语法:
.Nodes.Add(Relative, Relationship, Key, Text, Image, ;
SelectedImage)
其中:

Relative: 一个已存在节点的索引或键值。 如果未指定, 新节点放在顶级节点的末端。
Relationship: 新节点放置的相对于第一个参数中指定的节点的位置,可以是以下值:

1. 节点放在相对节点所在级的所有节点的末端。
2. 节点放在相对节点的后面。
3. 节点放在相对节点的前面。
4. 节点成为相对节点的子节点。
Key: 用于指明节点的唯一串。 如果显示内容是从一个表的记录中载入的, 可使用记录的主关键字段值 (如果不是字符型的,还需要转换为字符型)。 否则, 可使用一个转换为串的顺序号。
Text: 控件节点中显示的文本。
Image: 相关 ImageList 控件中的图象索引。
SelectedImage: 当节点选定时,要显示的相关 ImageList 控件中的图象索引。
Clear: 清除全部节点。
Remove: 移去指定索引号的节点。
节点集合属性

以下是节点集合属性:

Count: 节点号。
[]: 一个节点号的对象引用。


节点对象方法

以下是节点对象的方法:

CreateDragImage: 该方法不能用于 VFP ,因为该方法返回一个图象时, DragIcon 需要一个 CUR 文件名。
EnsureVisible: 确保指定节点是可见的。 该方法在必要时卷动 TreeView 控件并扩展所有指定节点的父节点。
节点对象属性

以下是节点对象属性:

Children: 如果节点对象拥有子节点,该值为.T.
Expanded: 如果节点对象已经扩展,该值为.T.
FullPath: 该节点的所有父节点(祖节点,曾祖节点等)的文本串接, 各节点的文本间用控件的 PathSeparator 属性中指定的分隔符分隔。 它非常类似于带路么的文件名。
Image, ExpandedImage 和 SelectedImage: 相关ImageList 控件中的适当的图象号。
Index: 节点集合中的节点对象的索引。
Key: 当节点添加时指定的唯一键值。
Child, FirstSibling, LastSibling, Previous, Parent, Next 和 Root: 指向与指定节点相关的节点对象。
Selected: 如果节点对象是选定的,该值为.T.。 设置该属性为 .T. 会自动高亮显示该节点并设置先前选定的节点的 Selected 属性为 .F.
Text: 控件中显示的文本。
载入TreeView 控件

在载入显示内容的初期相对的比较简单, 可以使用一些的趣的手法。 首先, 让我们看看直截了当的示例。 假如我们想载入一个客户及其订单(TREEVIEW 示例表单就是这样做的)。 以下是代码; 这些代码可以在表单初始化时调用(如, 在表单的Init() 方法或 TreeView 的 Init() 方法中调用表单的一个自定义方法):

with This
* 设置 CUSTOMER 和 ORDERS 表。
select CUSTOMER
set order to CUST_ID in ORDERS
set order to COMPANY
scan
* 从客户表中添加节点。
lcCustomerKey = 'C' + CUST_ID
.oTree.Nodes.Add(, 1, lcCustomerKey, trim(COMPANY), 1)
* 找到该客户的第一个订单并逐个处理。
select ORDERS
seek CUSTOMER.CUST_ID
scan while CUST_ID = CUSTOMER.CUST_ID
.oTree.Nodes.Add(lcCustomerKey, 4, 'O' + ORDER_ID, ;
dtoc(ORDER_DATE) + ' ' + ;
transform(ORDER_AMT, '$99,999.99'), 2)
endscan
select CUSTOMER
endscan
endwith

注意:指定到各节点的键值是字段名加上一个前缀来指明该记录来自哪一个表 (如”C” 表示 CUSTOMER , “O” 表示 ORDER)。 前缀可避免可能客户和订单表中具有相同主关键字值的问题, 并且前缀加主关键字段值允许我们快速地找到与特定节点匹配的记录(使用节点的 Key 属性, 从第一个字符取得表名,并用基本关键字来搜索余下部分的串)。
只展示这些代码是因为必须在表单显示前处理每一个客户的每一个订单。 如果有许多客户或订单, 这会花很长时间。 一个较好的方法是只载入顶级的项 (本例中是客户), 然后在 Expand() 方法中, 检查需要扩展的节点是否已载入了子节点。 如果没有, 则只载入当前节点的子节点。 这就比在一开始时载入全部内容快多了。 这里有一个技巧: 如果一个节点没有载入任何子节点, 就不会有 + 符号出现在该节点上。 因此你需要为每一个项级节点至少添加一个子节点, 那怕是一个”占位”节点。 当该节点第一次扩展时, 移去占位节点, 然后添加真正的子节点内容。
TREEVIEW 示例表单展示了如何使用该技术。 它有一个 lLoadChildren 自定义属性用于确定在表单显示前是否载入全部订单和产品。 试着设置该属性为 .F. 并注意在表单显示前用了多长时间。
你可能需要在一些点上重新载入显示内容。 例如, 如果其它应用程序用户正在添添客户或订单, 你可能需要用新添加的客户和订单刷新显示内容。 要避免解决哪些记录已经显示哪些没有显示的争论, 可以使用节点集合的 Clear() 方法来清除显示内容, 然后重新载入它。 该方法唯一的问题是一些节点可能已经扩展。 清除并重载显示内容会使所有节点以默认的收缩状态显示。 要克服这一点, 可以保存各个已存在节点的 Expanded 和 Key 或 FullPath 属性到一个数组或游标中, 清除并重载显示内容, 然后遍历数组或游标恢复那些节点的Expanded 属性。 TREEVIEW 示例表单的GetExpanded 和 SetExpanded 方法就是做这个工作的。

拖放
我曾在 CompuServe 看到过相关信息, 有很多关于似否可以拖放 TreeView 控件的混乱。一些人认为因为 VFP 没有实现与其它应用程序(如 WORD )间的拖放, 故不能在 ActiveX 控件中实现它。 你可以拖放 ActiveX 控件的理由是虽然它们是非 VFP 应用程序,当你在表单上拖放一个 ActiveX 控件时, VFP 将它放入一个 VFP 容器内, 这就是支持拖放的原因。 作为结果, 对 TreeView 进行拖放(拖出或放进) 时与其它控件没有区别。
TREEVIEW 示例表单实现了拖和放。 你可以从树上拖动一个节点到表单下面的文本框中; 节点中的文本将显示在文本框。 也可以拖动”drag me to tree” 标签并将其放入树中; 一个新的节点将添加到显示树中,其内容为当前的日期时间。