# TreeView [v1.0.44+]

## 目录

- 入门和简单示例
- 选项参数的选项和样式
- TreeView 的内置函数
- G-标签通知
- 备注
- 示例

## 入门和简单示例

Tree-View 通过缩进父项目下的子项目来显示出层级关系. 最常见的例子是资源管理器的驱动器和文件夹树.

TreeView 通常看起来像这样:

创建 TreeView 的语法为:

    Gui, Add, TreeView, Options

这是一个创建和显示简单项目层次结构的可运行脚本:

    Gui, Add, TreeView
    P1 := TV_Add("First parent")
    P1C1 := TV_Add("Parent 1's first child", P1)  ; 指定 P1 为此项目的父项目.
    P2 := TV_Add("Second parent")
    P2C1 := TV_Add("Parent 2's first child", P2)
    P2C2 := TV_Add("Parent 2's second child", P2)
    P2C2C1 := TV_Add("Child 2's first child", P2C2)

    Gui, Show  ; 显示窗口及其 TreeView.
    return

    GuiClose:  ; 当用户关闭 TreeView 所在的 GUI 窗口时退出脚本.
    ExitApp

## 选项参数的样式和选项

AltSubmit: 通知脚本有比正常更多的 TreeView 事件类型. 换句话说, g-标签会运行地更频繁. 请参阅 TreeView 通知了解详情.

Background: 指定单词 Background 后面紧跟着颜色名称(请参阅颜色图表) 或 RGB 值(0x 前缀可以省略). 例如: `BackgroundSilver`, `BackgroundFFDD99`. 如果此选项不存在, 则 TreeView 初始默认的背景颜色由 Gui Color 的最后一个参数设置(如果没有, 则使用系统默认的背景颜色). 指定 `BackgroundDefault` 来应用系统的默认背景颜色(通常为白色). 例如, 使用 `GuiControl, +BackgroundDefault, MyTreeView` 可以把 TreeView 恢复为默认的颜色.

Buttons: 指定 `-Buttons`(负 Buttons) 来避免在每个含有子项目的项目左边显示加号或减号按钮.

C: 文本颜色. 指定字母 C 后面紧跟着颜色名称(请参阅颜色图表) 或 RGB 值(0x 前缀可以省略). 例如: `cRed`, `cFF2211`, `c0xFF2211`, `cDefault`.

Checked: 在每项的左边提供一个复选框. 当添加项目时, 在其选项中指定单词 Check 来让复选框初始为选中而不是未选中状态. 用户可以点击复选框或按下空格键来选中或取消选中项目. 要找出 TreeView 中当前选中了哪些项目, 请调用 TV_GetNext() 或 TV_Get().

HScroll: 指定 `-HScroll`(负 HScroll) 来禁用控件中的水平滚动(而且控件将不显示水平滚动条).

ImageList: 这是把图标添加到 TreeView 的方法. 指定单词 ImageList 后紧跟着由之前调用 IL_Create() 返回的 ImageListID. 此选项仅在创建 TreeView 时才有效果(但是, TV_SetImageList() 没有此限制). 这里是一个可运行示例:

    ImageListID := IL_Create(10)  ; 创建初始容量为 10 个图标的图像列表.
    Loop 10  ; 加载一些标准系统图标到图像列表中.
        IL_Add(ImageListID, "shell32.dll", A_Index)
    Gui, Add, TreeView, ImageList%ImageListID%
    TV_Add("Name of Item", 0, "Icon4")  ; 添加项目到 TreeView 并给它分配文件夹图标.
    Gui Show

Lines: 指定 `-Lines`(负 Lines) 来避免显示连接父项目和它们的子项目之间的网状线. 但是, 移除这些线也阻止了顶级项目坐标加号/减号按钮的显示.

ReadOnly: 指定 `-ReadOnly`(负 ReadOnly) 来允许编辑每项的文本/名称. 要编辑某项, 请选择它接着按下 `F2`(请参阅下面的 WantF2 选项). 或者, 您可以对某个项目点击一次来选择它, 至少等半秒, 然后再次点击同一项目进行编辑. 编辑后, 可以对一个项目在其同级项目之间按字母顺序进行重新定位, 请参考下面的例子:

    Gui, Add, TreeView, -ReadOnly gMyTree  ; 有关 gMyTree, 请参阅 TreeView 的 g-标签.
    ; ...
    MyTree:
    if (A_GuiEvent == "e")  ; 用户结束了对项目的编辑 (使用 == 来进行区分大小写的比较).
        TV_Modify(TV_GetParent(A_EventInfo), "Sort")  ; 当一个项目没有父项目时, 此语句也有效.
    return

R: 行高(创建时). 指定字母 R 后面紧跟着要在控件中留出空间的行数. 例如, `R10` 会设置控件为 10 个项目的高度.

WantF2: 指定 `-WantF2`(负 WantF2) 来禁止使用 `F2` 来编辑当前选择的项目. 仅当 -ReadOnly 也有效时此设置才不会被忽略. 不论此设置如何, g-标签仍会接收到 `F2` 通知.

(未命名的数值样式): 由于上述以外的其他样式很少使用, 所以没有为它们命名. 请参阅 TreeView 样式表了解这些样式.

## TreeView 中的内置函数

所有的 TreeView 函数操作于当前线程的默认 GUI 窗口(这可以使用 `Gui, 2:Default` 进行改变). 如果默认窗口不存在或不含有 TreeView 控件, 则所有函数返回零来表明此问题.

如果窗口中含有多个 TreeView 控件, 则默认情况下函数操作于最近添加的那个. 要改变这种情况, 请指定 `Gui, TreeView, TreeViewName`, 其中 TreeViewName 为 TreeView 的关联变量的名称或 Window Spy 中显示的其 ClassNN 或在 [v1.1.04+] 它的 HWND. 一旦改变后, 所有现有和将来的线程都会使用指定的 TreeView. [v1.1.23+]: A_DefaultTreeView 包含当前设置.

项目函数:

- TV_Add: 添加新的项目到 TreeView.
- TV_Modify: 修改项目的属性和/或名称.
- TV_Delete: 删除指定项目或所有项目.

检索函数:

- TV_GetSelection: 返回选择项目的 ID 号.
- TV_GetCount: 返回控件中项目的总数.
- TV_GetParent: 返回指定项目的父项目的 ID.
- TV_GetChild: 返回指定项目的第一个/最上面的子项目的 ID 号.
- TV_GetPrev: 返回指定项目上面一个的同级项目的 ID 号.
- TV_GetNext: 返回指定项目下面一个项目的 ID 号.
- TV_GetText: 返回指定项目的文本/名称.
- TV_Get: 指定项目含有指定的属性, 则返回它的 ID.

其他函数:

- TV_SetImageList [v1.1.02+]: 设置或替换用于显示图标的 ImageList.

### TV_Add

添加新的项目到 TreeView 并返回其唯一的项目 ID 号(失败时返回 0).

    TV_Add(Name, ParentItemID, Options)

Name 是项目显示的文本, 它可以为文本或数值(包括数值的表达式结果). ParentItemID 为新项目的父项的 ID(省略它或指定 0 来添加顶级项目). 当添加大量项目时, 在添加项目前使用 `GuiControl, -Redraw, MyTreeView` 并且在添加项目后使用 `GuiControl, +Redraw, MyTreeView` 可以提升性能.

#### TV_Add() 和 TV_Modify() 中的选项参数

Options 参数是一个包含零个或多个下表中单词的字符串(不区分大小写). 在单词间使用空格或 tab 分隔. 要移除选项, 请在选项前加上负号. 要添加选项, 在选项前的正号可以省略.

Bold: 用粗体显示项目的名称. 以后要取消项目名称的粗体显示, 请使用 `TV_Modify(ItemID, "-Bold")`. [v1.1.30.01+]: 单词 Bold 后可以可选地紧跟着 0 或 1 以指示起始状态.

Check: 在项目的坐标显示一个复选标记(需要 TreeView 含有复选框). 以后要取消复选它, 请使用 `TV_Modify(ItemID, "-Check")`. 在单词 Check 后可以紧跟着 0 或 1 来表示初始状态. 换句话说, `"Check"` 和 `"Check" . VarContainingOne` 是相同的(这里中间使用的是连接运算符).

Expand: 展开此项以让其子项目显示出来(如果有). 以后要折叠项目, 请使用 `TV_Modify(ItemID, "-Expand")`. 如果没有子项目, 那么 TV_Modify() 返回 0 而不是它的项目 ID. 与之相比, TV_Add() 标记此项目为展开的以防以后要添加子项目. 与下面的 "Select" 不同, 展开一个项目不会自动展开其父项目. 最后, 在单词 Expand 后可以紧跟着 0 或 1 来表示初始状态. 换句话说, `"Expand"` 和 `"Expand" . VarContainingOne` 是相同的.

First | Sort | N: 这些选项仅适用于 TV_Add(). 它们指定新项目相对于其同级项目的位置(此处 同级项目 是同一级别的其他任何项目). 如果这些选项都不存在, 则新项目被添加到同级项目的最后一个/底部. 否则, 请指定 First 来添加项目到同级项目的第一个/顶部, 或指定 Sort 来按字母顺序插入新项目到同级项目中间. 如果指定一个纯整数(N), 则假定它为同级项目的 ID 号, 新项目会被插入到它的后面(如果整数 N 是唯一使用的选项, 则它不需要括在引号中).

Icon: 指定单词 Icon 后紧跟着此项目图标的编号, 项目图标显示在项目名称的左边. 如果此选项不存在, 则使用图像列表中的首个图标. 要显示空白图标, 请指定一个大于图像列表中图标数目的数字. 如果控件没有图像列表, 则既不显示图标也不为图标保留空间.

Select: 选择项目. 因为一次只能选择一个项目, 此时任何原来选择的项目会自动取消选择. 此外, 如果有必要此选项会展开其父项目以显示新选择的项目. 要找出当前选择的项目, 请使用 TV_GetSelection().

Sort: 对于 TV_Modify(), 此选项按字母顺序排列指定项目的子项目. 要对所有顶级项目进行排序, 请使用 `TV_Modify(0, "Sort")`. 如果不含子项目, 则返回 0 而不是所修改项目的 ID.

Vis: 通过滚动 TreeView 和/或展开其父项目(必要时) 来确保此项完全可见.

VisFirst: 和上面一样, 不过在可行时它还会滚动 TreeView 使得此项显示在最上面. 此选项与 TV_Modify() 一起使用比与 TV_Add() 通常更有效.

### TV_Modify

修改项目的属性和/或名称.

    TV_Modify(ItemID , Options, NewName)

成功时返回项目自己的 ID 而失败(或部分失败) 时返回 0. 如果只使用首个参数, 则选择指定的项目. 如果省略 NewName, 则保持当前名称不变. 要了解 Options, 请参阅上面的列表.

### TV_Delete

删除指定项目或所有项目.

    TV_Delete(ItemID)

如果省略 ItemID, 则删除 TreeView 中所有项目. 否则, 仅删除指定的 ItemID 表示的项目. 成功时返回 1 而失败时返回 0.

### TV_GetSelection

返回选择项目的 ID 号.

    TV_GetSelection()

### TV_GetCount

返回控件中项目的总数.

    TV_GetCount()

此函数总是即时的, 因为控件会跟踪此计数.

### TV_GetParent

返回指定项目的父项目的 ID.

    TV_GetParent(ItemID)

顶级项目没有父项目, 因此返回 0.

### TV_GetChild

返回指定项目的第一个/最上面的子项目的 ID 号(如果没有则为 0).

    TV_GetChild(ParentItemID)

### TV_GetPrev

返回指定项目上面一个的同级项目的 ID 号(如果没有则为 0).

    TV_GetPrev(ItemID)

### TV_GetNext

返回指定项目下面一个项目的 ID 号(如果没有则为 0).

    TV_GetNext(ItemID, ItemType)

它包含了下列模式:

- 省略所有参数时, 它返回 TreeView 中第一个/最上面的项目的 ID 号(如果没有则为 0).
- 如果只是 ItemID 存在, 则它返回指定项目下面一个同级项目的 ID 号(如果没有则为 0). 如果 ItemID 为 0, 则它返回 TreeView 中第一个/最上面的项目的 ID 号(如果没有则为 0).
- 当 ItemType 为 "Full" 或 "F", 则获取下一个项目, 不论它是否为指定项目的同级项目. 这使得脚本可以容易地逐项遍历整个数. 例如:

      ItemID := 0  ; 这样使得首次循环从树的顶部开始搜索.
      Loop
      {
          ItemID := TV_GetNext(ItemID, "Full")  ; 把 "Full" 替换为 "Checked" 来找出所有含复选标记的项目.
          if not ItemID  ; 没有更多项目了.
              break
          TV_GetText(ItemText, ItemID)
          MsgBox The next Item is %ItemID%, whose text is "%ItemText%".
      }

- 当 ItemType 为 "Check", "Checked" 或 "C", 与上面行为相同, 不过会跳过任何没有复选标记的项目. 这样可以逐个获取 TreeView 中所有含有复选标记的项目.

### TV_GetText

返回指定 ItemID 的文本/名称并保存到 OutputVar.

    TV_GetText(OutputVar, ItemID)

如果文本长度超过 8191, 则仅获取前 8191 个字符. 成功时函数返回项目自己的 ID. 失败时返回 0(且 OutputVar 被置空).

### TV_Get

如果指定项目含有指定的属性, 则返回它的 ID.

    TV_Get(ItemID, Attribute)

如果指定的项目具有指定的属性, 则返回它自己的 ItemID. 否则返回 0. 对于 Attribute, 指定 "E", "Expand" 或 "Expanded" 来判断此项当前是否展开的(即它的子项目是显示的); 指定 "C", "Check" 或 "Checked" 来判断此项是否含有复选标记; 或指定 "B" 或 "Bold" 来判断此项当前是否为粗体.

提示: 因为在 IF 语句中把任何非零值视为 "true", 所以下面两行功能相同: `if TV_Get(ItemID, "Checked") = ItemID` 和 `if TV_Get(ItemID, "Checked")`.

### TV_SetImageList [v1.1.02+]

设置或替换用于显示图标的 ImageList.

    TV_SetImageList(ImageListID , IconType)

ImageListID 是先前调用 IL_Create() 时返回的数字. IconType 一般情况下忽略, 默认为 0. 否则, 对于状态图标指定 2(还没有直接支持状态图标, 不过可以通过 SendMessage 使用). 如果成功, 则 TV_SetImageList() 返回之前与 TreeView 关联的 ImageListID(如果没有则为 0). 任何分离的图像列表一般应使用 IL_Destroy(ImageListID) 进行销毁.

## G-标签通知(主要)

g-标签例如 `gMySubroutine` 可以使用在此控件的选项中. 这使得当用户在控件中执行动作时 MySubroutine 标签会自动运行. 此子程序中可引用内置变量 A_Gui 和 A_GuiControl 来找出产生事件的是哪个窗口和 TreeView. 更重要的是, 它可以参考 A_GuiEvent, 其包含下列字符串或字母的其中一个(考虑到和未来版本的兼容性, 脚本不应假定这些字符串或字母是唯一可能的值):

DoubleClick: 用户双击了一项. 变量 A_EventInfo 包含目标项目 ID.

D: 用户尝试开始拖动一个项目(目前还没有内置对拖动项目的支持). 变量 A_EventInfo 包含目标项目 ID.

d(小写的 D): 和上面相同, 除了指右键拖动而不是左键.

e(小写的 E): 用户完成编辑一个项目(只有当 TreeView 选项中含有 `-ReadOnly` 时用户才可以编辑项目). 变量 A_EventInfo 包含目标项目 ID.

S: 选择了一个新项目, 可以是用户或脚本自己选择的. 变量 A_EventInfo 包含新选择项目的 ID.

## G-标签通知(次要)

如果 TreeView 的选项中含有单词 AltSubmit, 则其 g-标签会运行的更频繁并且 A_GuiEvent 还可能包含下列值:

Normal: 用户左键单击了一个项目. 变量 A_EventInfo 包含目标项目 ID.

RightClick: 用户右键单击了一个项目. 变量 A_EventInfo 包含目标项目 ID. 在大多数情况下, 最好不要通过显示菜单来响应此通过. 而应使用 GuiContextMenu 标签, 因为它还能识别 Appskey. 例如:

    GuiContextMenu:  ; 运行此标签来响应右键点击或按下 Appskey.
    if (A_GuiControl != "MyTreeView")  ; 这个检查是可选的. 让它只为 TreeView 中的点击显示菜单.
        return
    ; 在提供的坐标处显示菜单, A_GuiX 和 A_GuiY. 应该使用这些
    ; 因为即使用户按下 Appskey 它们也会提供正确的坐标:
    Menu, MyContextMenu, Show, %A_GuiX%, %A_GuiY%
    return

E: 用户开始编辑一个项目(只有当 TreeView 选项中含有 -ReadOnly 时用户才可以编辑项目). 变量 A_EventInfo 包含目标项目 ID.

F: TreeView 接收到键盘焦点.

f(小写的 F): TreeView 失去了键盘焦点.

K: 当 TreeView 拥有焦点时用户按下了一个键. A_EventInfo 包含此键的虚拟按键代码, 这是介于 1 和 255 之间的数字. 如果此键是字母键, 则在大多数键盘布局中可以使用 `Chr(A_EventInfo)` 把它转换成相应的字符. 不论 WantF2 选项如何都可以接收到 `F2`. 然而, 不会接收到 `Enter`; 要接收它, 请像下面描述的那样使用一个默认按钮.

+(加号): 展开了一个项目来显示它的子项目. 变量 A_EventInfo 包含目标项目 ID.

-(减号): 折叠一个项目来隐藏它的子项目. 变量 A_EventInfo 包含目标项目 ID.

## 备注

Gui Submit 命令对 TreeView 控件没有效果. 因此, 脚本可以使用 TreeView 的关联变量(如果有) 来保存其他数据而不用担心它会被覆盖.

当 TreeView 拥有焦点时如果要检测到用户按下的 `Enter`, 请使用默认按钮(如果需要可以隐藏它). 例如:

    Gui, Add, Button, Hidden Default, OK
    ...
    ButtonOK:
    GuiControlGet, FocusedControl, FocusV
    if (FocusedControl != "MyTreeView")
        return
    MsgBox % "Enter was pressed. The selected item ID is " . TV_GetSelection()
    return

使用键盘除了在项与项之间导航外, 用户还可以通过输入一个项目名称的前几个字符来进行增量搜索. 这使得选择对象跳转到最近匹配的项目.

尽管 TreeView 中的每个项目可以存储任意长度的文本, 但仅显示开始的 260 个字符.

尽管理论上 TreeView 中的项目数可以多达 65536, 然而接近此数目时添加项的性能将显著降低. 通过使用 TV_Add() 中描述的重绘提示可以稍微减轻这种情况.

与 ListView 不同, 当 TreeView 销毁时它的图像列表不会自动被销毁. 因此, 如果 TreeView 中使用的图像列表以后不再用于其他地方, 则在销毁 TreeView 所在的窗口后脚本应调用 IL_Destroy(ImageListID) 来销毁此图像列表. 然而, 如果脚本很快将退出, 这样做是没必要的, 因为那时所有的图像列表会自动被销毁.

脚本可以在每个窗口中创建多个 TreeView. 要对非默认的 TreeView 进行操作, 请参阅内置函数.

要执行一些操作, 例如调整大小, 隐藏或改变 TreeView 的字体, 请使用 GuiControl.

Tree View eXtension(TVX) 扩展了 TreeView 的功能, 增加对移动, 插入和删除的支持. 演示的例子请参阅 www.autohotkey.com/forum/topic19021.html

## 相关

ListView, 其他控件类型, Gui, GuiContextMenu, GuiControl, GuiControlGet, TreeView 样式表

## 示例

下面是一个比页面顶部的脚本更复杂的可工作的脚本. 它创建并显示一个 TreeView, 其中包含所有用户开始菜单中的所有文件夹. 当用户选择一个文件夹时, 它的内容会显示在右边的 ListView 中(类似于 Windows 资源管理器). 此外, StatusBar 控件会显示当前选择文件夹的信息.

    ; 下面的文件夹为 TreeView 的根文件夹. 请注意, 如果指定整个驱动器例如 C:\
    ; 那么可能需要很长加载时间:
    TreeRoot := A_StartMenuCommon
    TreeViewWidth := 280
    ListViewWidth := A_ScreenWidth - TreeViewWidth - 30

    ; 让用户可以最大化或拖动调整窗口大小:
    Gui +Resize

    ; 创建图像列表并在其中放入一些标准的系统图标:
    ImageListID := IL_Create(5)
    Loop 5
        IL_Add(ImageListID, "shell32.dll", A_Index)
    ; 创建 TreeView 和 ListView, 让它们像在 Windows 资源管理器中那样靠在一起:
    Gui, Add, TreeView, vMyTreeView r20 w%TreeViewWidth% gMyTreeView ImageList%ImageListID% ; ImageList
    Gui, Add, ListView, vMyListView r20 w%ListViewWidth% x+10, Name|Modified

    ; 设置 ListView 的列宽(可选的):
    Col2Width := 70  ; 缩小到只显示 YYYYMMDD 部分.
    LV_ModifyCol(1, ListViewWidth - Col2Width - 30)  ; 允许垂直滚动条.
    LV_ModifyCol(2, Col2Width)

    ; 创建状态栏, 显示文件夹数及其总大小的信息:
    Gui, Add, StatusBar
    SB_SetParts(60, 85)  ; 在状态栏中创建三个部分(第三部分占用所有剩余宽度).

    ; 添加文件夹及其子文件夹到树中. 如果加载需要很长时间, 则显示提示信息:
    SplashTextOn, 200, 25, TreeView and StatusBar Example, Loading the tree...
    AddSubFoldersToTree(TreeRoot)
    SplashTextOff

    ; 显示窗口并返回. 每当用户执行符合条件的动作时, 操作系统会通知脚本:
    Gui, Show,, %TreeRoot%  ; 在标题栏中显示源目录(TreeRoot).
    return

    AddSubFoldersToTree(Folder, ParentItemID = 0)
    {
        ; 此函数添加指定文件夹中所有子文件夹到 TreeView.
        ; 它还可以调用自己来递归获取到任意深度的内嵌文件夹.
        Loop %Folder%\*.*, 2  ; 获取所有文件夹的子文件夹.
            AddSubFoldersToTree(A_LoopFileFullPath, TV_Add(A_LoopFileName, ParentItemID, "Icon4"))
    }

    MyTreeView:  ; 此子程序处理用户的操作(例如点击).
    if (A_GuiEvent != "S")  ; 即除了 "选择树中的新项目" 以外的其他操作.
        return  ; 什么都不做.
    ; 否则, 把选择的文件夹中的内容放入 ListView.
    ; 首先确定选择的文件夹的完整路径:
    TV_GetText(SelectedItemText, A_EventInfo)
    ParentID := A_EventInfo
    Loop  ; 建立到选择的文件夹的完整路径.
    {
        ParentID := TV_GetParent(ParentID)
        if not ParentID  ; 没有更高层的项目了.
            break
        TV_GetText(ParentText, ParentID)
        SelectedItemText := ParentText "\" SelectedItemText
    }
    SelectedFullPath := TreeRoot "\" SelectedItemText

    ; 把文件放入 ListView:
    LV_Delete()  ; 清除所有行.
    GuiControl, -Redraw, MyListView  ; 加载过程中禁用重绘来提升性能.
    FileCount := 0  ; 在下面循环之前初始化.
    TotalSize := 0
    Loop %SelectedFullPath%\*.*  ; 为了简化, 这里省略了文件夹, 所以只在 ListView 中显示文件.
    {
        LV_Add("", A_LoopFileName, A_LoopFileTimeModified)
        FileCount += 1
        TotalSize += A_LoopFileSize
    }
    GuiControl, +Redraw, MyListView

    ; 更新状态栏的三个部分, 让它们显示当前选择的文件夹的信息:
    SB_SetText(FileCount . " files", 1)
    SB_SetText(Round(TotalSize / 1024, 1) . " KB", 2)
    SB_SetText(SelectedFullPath, 3)
    return

    GuiSize:  ; 当用户改变窗口大小时扩展/收缩 ListView 和 TreeView.
    if (A_EventInfo = 1)  ; 窗口被最小化了. 无需进行操作.
        return
    ; 否则, 窗口的大小被调整过或被最大化了. 调整控件大小以适应.
    GuiControl, Move, MyTreeView, % "H" . (A_GuiHeight - 30)  ; -30 用于状态栏和边距.
    GuiControl, Move, MyListView, % "H" . (A_GuiHeight - 30) . " W" . (A_GuiWidth - TreeViewWidth - 30)
    return

    GuiClose:  ; 当用户关闭 TreeView 所在的 GUI 窗口时退出脚本.
    ExitApp

dftg