关于树视图控件

树视图控件是一种显示分层项目列表的窗口,例如文档中的标题、索引中的条目或磁盘上的文件和目录。 每个项包含一个标签和一个可选位图图像,且每个项可以有一个与之关联的子项列表。 通过单击某个项目,用户可以展开或折叠相关的子项目列表。

下图显示了一个简单的树形视图控件,其中包含一个根节点、一个展开节点和一个折叠节点。 该控件对选定项目使用一个位图,而对其他项目使用另一个位图。

screen shot showing five nodes in a hierarchy; the text of one node is selected, but nodes are not linked to each other by lines

创建树视图控件后,可以通过向控件发送消息来添加、删除、排列或以其他方式来操作项目。 每条消息都有一个或多个相应的宏,可以使用这些宏来代替显式发送消息。

以下是本节中要讨论的主题。

树视图样式

树视图样式可控制树视图控件外观的各个方面。 在创建树视图控件时设置初始样式。 创建树视图控件后,可以使用 GetWindowLongSetWindowLong 函数来检索和更改样式。

如下图所示,TVS_HASLINES 样式通过绘制将子项目与其父项目连接起来的线条,增强了树视图控件层次结构的图形表示。

screen shot showing the previous arrangement, but with lines joining the nodes; the first line descends from the root node

这种样式本身不会在层次结构的根部绘制线条。 为此,需要结合使用 TVS_HASLINES 和 TVS_LINESATROOT 样式。 结果如下图所示。

screen shot showing the previous arrangement, but with an additional horizontal line leading to the root node

用户可以通过双击父项来展开或折叠父项的子项列表。 具有 TVS_HASBUTTONS 样式的树视图控件会在每个父项目的左侧添加一个按钮。 用户可以单击一次按钮,而无需双击父项目即可展开或折叠子项目。 TVS_HASBUTTONS 不会将按钮添加到层次结构根部的项。 为此,必须结合使用 TVS_HASLINES、TVS_LINESATROOT 和 TVS_HASBUTTONS。 下图展示了这种样式的组合。

screen shot showing the previous arrangement, but with expand/collapse buttons at each vertex of two lines

TVS_CHECKBOXES 样式会在每个项目旁边创建复选框。 如果要使用复选框样式,则必须在创建树视图控件后和填充树之前设置 TVS_CHECKBOXES 样式(使用 SetWindowLong 方法)。 否则,复选框可能会出现未被选中的情况,具体取决于计时问题。 下图显示了复选框样式。

screen shot showing the previous arrangement, but with a checkbox next to each node; two of the checkboxes are selected

TVS_FULLROWSELECT 样式可使选择高亮显示覆盖控件的整个宽度,而不仅仅是覆盖项目本身。 下图显示了此样式。

screen shot showing the original arrangement of five nodes with no lines, but the selection highlight extends the full width of the control

TVS_EDITLABELS 样式使用户可以编辑树视图项目的标签。 有关编辑标签的详细信息,请参阅树视图标签编辑

有关这些样式和其他样式的详细信息,请参阅树视图控件窗口样式

父项目和子项目

树视图控件中的任何项目都可以有一个与之关联的下级项目(称为子项目)列表。 具有一个或多个子项目的项目被称为父项目。 子项目显示在其父项目的下方,并以缩进方式表示其从属于父项目。 没有父项目的项目位于层次结构的顶端,被称为根项目

要向树视图控件添加项目,请向控件发送 TVM_INSERTITEM 消息。 该消息会返回 HTREEITEM 类型的句柄,该句柄可唯一标识项目。 添加项目时,必须为新项目的父项目指定句柄。 如果在 TVINSERTSTRUCT 结构中指定了 NULL 或 TVI_ROOT 值,而不是父项目句柄,则项目将作为根项目进行添加。

在任何给定时间,子项的父项列表的状态都可以为展开或折叠。 在状态为展开时,子项将显示在父项下。 当状态为折叠时,子项不会显示。 当用户双击父项,或如果父项具有 TVS_HASBUTTONS 样式,用户单击与父项关联的按钮时,列表将自动在展开和折叠状态之间切换。 应用程序可以使用 TVM_EXPAND 消息来展开或折叠子项目。

当父项目的子项目列表即将展开或折叠时,树视图控件会向父窗口发送 TVN_ITEMEXPANDING 通知消息。 该通知会让应用程序有机会阻止更改或设置父项目的任何依赖于子项目列表状态的属性。 更改列表状态后,树视图控件会向父窗口发送 TVN_ITEMEXPANDED 通知消息。

子项列表展开后,则将相对于父项缩进。 可以使用 TVM_SETINDENT 消息来设置缩进量,或者使用 TVM_GETINDENT 消息来获取当前缩进量。

树视图控件会使用从创建树视图控件的进程堆中分配的内存。 树视图中项目的最大数量取决于堆中可用的内存量。

项目标签

在将项目添加到树视图控件时,通常都要指定项目标签的文本。 TVM_INSERTITEM 消息包含一个 TVITEM 结构,该结构定义了项目的属性,包括一个包含标签文本的字符串。

树视图控件分配内存用于存储每个项目;项目标签的文本占用了内存的很大一部分。 如果应用程序在树视图控件中维护字符串副本,则可以在 TVITEMpszText 成员中指定 LPSTR_TEXTCALLBACK 值,而不是向树视图传递实际字符串,从而减少控件的内存需求。 使用 LPSTR_TEXTCALLBACK 会导致树视图控件在需要重新绘制项目时,从父窗口获取项目的标签文本。 要检索文本,树视图控件会发送 TVN_GETDISPINFO 通知消息,其中包括 NMTVDISPINFO 结构的地址。 父窗口必须填充所包含结构的相应成员。

树视图标签编辑

用户可以直接编辑具有 TVS_EDITLABELS 样式的树视图控件中的项目的标签。 用户通过单击具有焦点的项的标签开始编辑。 应用程序使用 TVM_EDITLABEL 消息开始编辑。 当编辑开始、取消或完成时,树视图控件都会通知父窗口。 在完成编辑后,父窗口将负责更新项目的标签(如适用)。

在标签编辑开始时,树视图控件会向其父窗口发送 TVN_BEGINLABELEDIT 通知消息。 通过处理此通知,应用程序可以允许编辑某些标签,并阻止编辑其他标签。 返回 0 则允许编辑,返回非 0 则阻止编辑。

在取消或完成标签编辑时,树视图控件会向其父窗口发送一条 TVN_ENDLABELEDIT 通知消息。 lParam 参数是 NMTVDISPINFO 结构的地址。 item 参数是一个 TVITEM 结构,用于标识项目并包含已编辑的文本。 如果父窗口想保留新标签,则由父窗口负责更新项目的标签。 如果取消编辑,TVITEMpszText 成员为 0。

在标签编辑期间,通常是在为了响应 TVN_BEGINLABELEDIT 通知消息时,可以使用 TVM_GETEDITCONTROL 消息来检索用于标签编辑的编辑控件句柄。 可以向编辑控件发送 EM_SETLIMITTEXT 消息,以限制用户可输入的文本量,或者对编辑控件进行子类化,以拦截并丢弃无效字符。 但请注意,编辑控件仅在发送 TVN_BEGINLABELEDIT 之后显示。

树视图控件项目位置

在使用 TVM_INSERTITEM 消息将项目添加到树视图控件时,将设置项目的初始位置。 该消息包括一个 TVINSERTSTRUCT 结构,其中指定了父项目句柄和要在其后插入新项目的项目句柄。 第二个句柄必须标识给定父项目的子项目或以下值之一: TVI_FIRST、TVI_LAST 或 TVI_SORT。

在指定 TVI_FIRST 或 TVI_LAST 时,树视图控件会将新项目置于给定父项目的子项目列表的开始或结束位置。 在指定 TVI_SORT 时,树视图控件会根据项目标签文本,按字母顺序将新项目插入子项目列表。

可以使用 TVM_SORTCHILDREN 消息来按字母顺序对父项目的子项目列表进行排列。 该消息包含一个参数,用于指定是否也按字母顺序对从给定的父项目下降序排列的各级子项目进行排序。

通过 TVM_SORTCHILDRENCB 消息,可以根据定义的标准对子项目进行排序。 在使用此消息时,可以指定一个应用程序定义的回调函数,以便树视图控件在需要决定两个子项目的相对顺序时调用该函数。 回调函数会接收两个 32 位应用程序定义的值,用于比较项目,以及在发送 TVM_SORTCHILDRENCB 时指定的第三个 32 位值。

树视图控件项状态概述

树视图控件中的每个项目都有一个当前状态。 每个项目的状态信息包括一组位标志以及图像列表索引,用于显示项目的状态图像和覆盖图像。 位标志用于指明项目是否被选中、禁用、展开等。 在大多数情况下,树视图控件都会自动设置项目的状态,以反映用户的操作,如选择某个项目。 但是,也可以使用 TVM_SETITEM 消息来设置项目的状态,并使用 TVM_GETITEM 消息来检索项目的当前状态。 有关项目状态的完整列表,请参阅树视图控件项目状态

项目的当前状态由 TVITEM 结构的 state 成员指定。 树视图控件可能会更改项目的状态以反映用户的操作,比如选择项目或将焦点设置到项目上。 此外,应用程序可能更改项的状态来禁用或隐藏项,或指定覆盖图像或状态图像。

在指定或更改项目状态时,TVITEMstatemask 成员会指定要设置的状态位,而 state 成员则会包含这些位的新值。

要设置项目的覆盖图像,statemask 必须包括 TVIS_OVERLAYMASK 值,而 state 必须包括通过使用 INDEXTOOVERLAYMASK 宏向左移动 8 位的覆盖图像从 1 开始的索引。 索引可以为 0,以指定没有覆盖图像。

项目图标旁边会显示一个状态图像,表示应用程序定义的状态。 状态图像包含在状态图像列表中,该列表通过发送 TVM_SETIMAGELIST 消息来指定。 要设置项目的状态图像,请在 TVITEM 结构的 statemask 成员中包含 TVIS_STATEIMAGEMASK 值。 结构的 state 成员的第 12 至 15 位指定了要绘制的图像在状态图像列表中的索引。

要设置状态图像索引,请使用 INDEXTOSTATEIMAGEMASK。 此宏会采用索引,并相应地设置第 12 至 15 位。 要表示项目没有状态图像,可将索引设为 0。 此约定意味着状态图像列表中的图像 0 不能用作状态图像。 要隔离 state 成员的第 12 至 15 位,请使用 TVIS_STATEIMAGEMASK 掩模。 有关覆盖图像和状态图像的更多信息,请参阅树视图图像列表

项目选择

当选择从一个项目变为另一个项目时,树视图控件会通过发送 TVN_SELCHANGINGTVN_SELCHANGED 通知消息来通知父窗口。 这两个通知包括指定更改是鼠标单击还是击键的结果的值。 通知还包括有关将获取选择的项和将失去选择的项的信息。 您可以使用此信息设置依赖项的选择状态的项特性。 响应 TVN_SELCHANGING 返回 TRUE 会阻止选择更改,而返回 FALSE 则允许更改。

应用程序可以通过发送 TVM_SELECTITEM 消息来更改选择。

项目信息

树视图控件支持多种消息,可检索控件中项目的相关信息。

TVM_GETITEM 消息可以检索项目的句柄和属性。 项目的属性包括其当前状态、控件图像列表中项目的选定和非选定位映射图像的索引、表示项目是否有子项目的标志、项目的标签字符串地址以及项目的应用程序定义的 32 位值。

TVM_GETNEXTITEM 消息将检索与当前项目有指定关系的树视图项目。 此消息可以检索项目的父项目、下一个或上一个可见项目、第一个子项目等。

TVM_GETITEMRECT 消息将检索树视图项目的边界矩形。 TVM_GETCOUNTTVM_GETVISIBLECOUNT 消息可分别检索树视图控件中的项目计数和在树视图控件窗口中完全可见的项目计数。 可以使用 TVM_ENSUREVISIBLE 消息确保特定项目可见。

树视图图像列表

树视图控件中的每个项目都可以有四个与之相关的位图图像。

  • 选择项目时显示的图像,如打开的文件夹。
  • 未选择项目时显示的图像,如关闭的文件夹。
  • 在选定或未选定图像上透明绘制的覆盖图像。
  • 状态图像,它是显示在所选或未选图像左边的附加图像。 可以使用状态图像(如选中和清除复选框)来表示应用程序定义的项目状态。

默认情况下,树视图控件不会显示项目图像。 要显示项目图片,必须创建图片列表并将其与控件相关联。 有关图像列表的详细信息,请参阅图像列表

树视图控件可以有两个图像列表:一个普通图像列表和一个状态图像列表。 普通图像列表可存储所选图像、非所选图像和覆盖图像。 状态图像列表可存储状态图像。 使用 ImageList_Create 函数来创建图像列表,并使用其他图像列表函数向图像列表中添加位图。 然后,使用 TVM_SETIMAGELIST 消息将图像列表与树视图控件关联起来。 TVM_GETIMAGELIST 消息将检索树视图控件图像列表的一个句柄。 如果需要在列表中添加更多图像,此消息将非常有用。

除了选定图像和非选定图像外,树视图控件的普通图像列表最多还可包含四个覆盖图像。 覆盖图像是通过一个索引来识别的,其设计目的是透明地绘制在选定图像和非选定图像之上。 要为普通图像列表中的图像分配覆盖掩模索引,请调用 ImageList_SetOverlayImage 函数。

默认情况下,所有项目在选中和未选中状态下都显示普通图像列表中的第一个图像。 此外,默认情况下,项目不显示覆盖图像或状态图像。 可以通过发送 TVM_INSERTITEMTVM_SETITEM 消息来更改项目的这些默认行为。 这些消息使用 TVITEM 结构指定项目的图像列表索引。

要指定项目的选定和非选定图像,请在 TVITEM 结构的 mask 成员中设置 TVIF_SELECTEDIMAGE 和 TVIF_IMAGE 位,并在 iSelectImageiImage 成员中指定控件普通图像列表的索引。 或者,也可以在 iSelectImageiImage 中指定 I_IMAGECALLBACK 值,而不是指定索引。 这样,每次要重新绘制项目时,控件都会查询其父窗口的图像列表索引。 控件会发送 TVN_GETDISPINFO 通知消息以检索索引。

要将覆盖图像与项目相关联,请使用 INDEXTOOVERLAYMASK 宏,在项目的 TVITEM 结构的 state 成员中指定覆盖掩模索引。 还必须设置 stateMask 成员中的 TVIS_OVERLAYMASK 位。 覆盖掩模索引从 1 开始;索引为 0 表示没有指定覆盖图像。

状态图像存储在单独的状态图像列表中,并通过索引进行标识。 要指定状态图像列表,请发送 TVM_SETIMAGELIST 消息。 与列表视图控件使用从 1 开始的索引来标识状态图像不同,树视图控件的状态图像是通过从 0 开始的索引来标识的。 但是,索引为 0 表示项目没有状态图像。 因此,图像 0 不能用作状态图像。 有关项目状态和状态图像的进一步讨论,请参阅 树视图项目状态概述

拖放操作

当用户开始拖动一个项目时,树视图控件会通知父窗口。 当用户开始使用鼠标左键拖动项目时,父窗口会收到 TVN_BEGINDRAG 通知消息;当用户开始使用鼠标右键拖动项目时,父窗口会收到 TVN_BEGINRDRAG 通知消息。 可以通过为树视图控件提供 TVS_DISABLEDRAGDROP 样式来阻止树视图控件发送这些通知。

可以通过使用 TVM_CREATEDRAGIMAGE 消息获得要在拖动操作中显示的图像。 树视图控件会根据被拖动项目的标签来创建一个拖动位图。 然后,树视图控件会创建一个图像列表,将位图添加到列表中,并返回图像列表的句柄。

必须提供实际拖动项的代码。 这通常涉及使用图像列表函数的拖动功能,并处理拖动操作开始后发送到父窗口的 WM_MOUSEMOVEWM_LBUTTONUP(或 WM_RBUTTONUP)消息。

如果要将树视图控件中的项目作为拖放操作的目标,那就需要知道鼠标指针何时位于目标项目上。 可以通过 TVM_HITTEST 消息来了解相关信息。 需要指定包含鼠标指针当前坐标的 TVHITTESTINFO 结构的地址。 在返回 SendMessage 函数时,该结构包含一个标志,指示鼠标指针相对于树视图控件的位置。 如果指针位于树视图控件中的一个项目上,则该结构还包含该项目句柄。

可以使用 TVM_SETITEM 消息将状态设置为 TVIS_DROPHILITED 值,从而指示项目是拖放操作的目标。 使用用于指示拖放目标的样式绘制具有此状态的项。

树视图控件通知消息

树视图控件以 WM_NOTIFY 消息的形式向其父窗口发送以下通知消息。

通知 说明
TVN_BEGINDRAG 表示开始拖放操作。
TVN_BEGINLABELEDIT   表示开始就地标签编辑。
TVN_BEGINRDRAG 表示鼠标右键已开始拖放操作。
TVN_DELETEITEM 表示删除特定项。
TVN_ENDLABELEDIT 表示标签编辑结束。
TTN_GETDISPINFO 请求树视图控件显示项目所需的信息。
TVN_ITEMEXPANDED 表示父项目的子项目列表已展开或折叠。
TVN_ITEMEXPANDING 表示父项目的子项目列表即将展开或折叠。
TVN_KEYDOWN 向键盘事件发出信号。
TVN_SELCHANGE 表示选定项目已从一项更改为另一项。
TVN_SELCHANGING 表示选定项目即将从一项更改为另一项。
TVN_SETDISPINFO 通知父窗口必须更新其维护的项目信息。

 

默认树视图控件消息处理

本节介绍树视图控件执行的窗口消息处理。 本文档的其他章节讨论了树视图控件的特定信息,因此这里不再介绍相关信息。

Message 已执行的处理
WM_COMMAND 处理 EN_UPDATEEN_KILLFOCUS 编辑控件通知消息,并将所有其他编辑控件通知转发给父窗口。 没有返回值。
WM_CREATE 分配内存并初始化内部数据结构。 如果成功,则返回 0,否则返回 -1。
WM_DESTROY 释放与控件关联的所有系统资源。 它会返回 0。
WM_ENABLE 启用或禁用控件。
WM_ERASEBKGND 使用树视图控件的当前背景颜色擦除窗口背景。 它将返回 TRUE
WM_GETDLGCODE 返回 DLGC_WANTARROWS 和 DLGC_WANTCHARS 值的组合。
WM_GETFONT 返回当前标签字体的句柄。
WM_HSCROLL 滚动树视图控件。 如果发生滚动,则返回 TRUE;否则返回 FALSE
WM_KEYDOWN 向父窗口发送所有键的 TVN_KEYDOWN 通知信息。 当用户按下 ENTER 键时,发送 NM_RETURN(树视图)通知信息。 当用户按方向键或 PAGE UP、PAGE DOWN、HOME、END 或 BACKSPACE 键时,它会移动脱字符号。 当用户同时按下 CTRL 键和这些键时,它就会滚动树视图控件。 如果键已处理,则返回 TRUE;否则返回 FALSE
WM_KILLFOCUS 重新绘制被聚焦的项目(如有),并向父窗口发送 NM_KILLFOCUS(树视图) 通知消息。
WM_LBUTTONDBLCLK 取消标签编辑,如果双击了项目,则向父窗口发送 NM_DBLCLK(树视图) 通知消息。 如果父窗口返回 0,树视图控件将切换项目的展开状态,并向父窗口发送 TVN_ITEMEXPANDINGTVN_ITEMEXPANDED 通知消息。 没有返回值。
WM_LBUTTONDOWN 如果用户单击了与父项目关联的按钮,则会切换展开状态。 如果用户单击了一个项目标签,则树视图控件就会选择并将焦点设置为该项目。 如果用户在松开鼠标按钮前移动了鼠标,则树视图控件就会开始拖放操作。 没有返回值。
WM_PAINT 绘制树视图控件的无效区域。 它会返回 0。 如果 wParam 参数为非 NULL,则控件会假定该值是设备上下文 (HDC) 的句柄,并使用该设备上下文进行绘制。
WM_RBUTTONDOWN 检查是否已单击某个项目并开始拖动操作。 如果操作已经开始,则会向父窗口发送 TVN_BEGINRDRAG 通知消息,并高亮显示放置目标。 否则,它会向父窗口发送 NM_RCLICK(树视图)通知消息。 没有返回值。
WM_SETFOCUS 重新绘制被聚焦的项目(如有),并向父窗口发送 NM_SETFOCUS 通知消息。
WM_SETFONT 保存指定的字体句柄,并使用新字体重新绘制树视图控件。
WM_SETREDRAW 设置或清除重绘标志。 设置重新绘制标记后,树视图控件将被重新绘制。 它会返回 0。
WM_SIZE 重新计算取决于树视图控件客户区大小的内部变量。 它将返回 TRUE
WM_STYLECHANGED 取消标签编辑并使用新样式来重新绘制树视图控件。 它会返回 0。
WM_SYSCOLORCHANGE 如果设置了重新绘制标记,则使用新颜色来重新绘制树视图控件。 没有返回值。
WM_TIMER 开始编辑项目标签。 如果用户单击聚焦项目的标签,则树视图控件会设置一个计时器,而不是立即进入编辑模式。 如果用户双击标签,则计时器可让树视图避免进入编辑模式。 它会返回 0。
WM_VSCROLL 滚动树视图控件。 如果发生滚动,则返回 TRUE;否则返回 FALSE

 

示例:CustDTv 演示 TreeView 中的自定义绘图 (Q248496)