ListView

目录

入门和简单示例

List-View 是由操作系统提供的最精心设计的控件之一. 在其最容易识别的形式中, 它显示一个多行多列组成的表格视图, 最常见的例子是资源管理器的文件和文件夹列表(详细信息视图).

ListView 通常是这样的:

ListView

尽管它可能是精细的, 但 ListView 的基本功能也易于使用. 创建 ListView 的语法为:

LV := GuiObj.Add("ListView", Options, ["ColumnTitle1","ColumnTitle2","..."])

或:

LV := GuiObj.AddListView(Options, ["ColumnTitle1","ColumnTitle2","..."])

这是一个可运行脚本, 它创建并显示一个 ListView 其中包含用户的 "我的文档" 目录中的文件列表:

; 创建窗口:
MyGui := Gui()

; 创建含名称和大小两列的 ListView:
LV := MyGui.Add("ListView", "r20 w700", ["Name","Size (KB)"])

; 每当用户双击某行时, 通知脚本:
LV.OnEvent("DoubleClick", LV_DoubleClick)

; 从文件夹中获取文件名列表并把它们放入 ListView:
Loop Files, A_MyDocuments "\*.*"
    LV.Add(, A_LoopFileName, A_LoopFileSizeKB)

LV.ModifyCol  ; 根据内容自动调整每列的大小.
LV.ModifyCol(2, "Integer")  ; 为了进行排序, 指出列 2 是整数.

; 显示窗口:
MyGui.Show

LV_DoubleClick(LV, RowNumber)
{
    RowText := LV.GetText(RowNumber)  ; 从行的第一个字段中获取文本.
    ToolTip("You double-clicked row number " RowNumber ". Text: '" RowText "'")
}

Options 参数的选项和样式

Background: 指定单词 Background 后面紧跟着颜色名称(请参阅颜色列表) 或 RGB 值(0x 前缀可以省略). 例如: BackgroundSilver, BackgroundFFDD99. 如果此选项不存在, ListView 初始默认为系统默认的背景色. 指定 BackgroundDefault-Background 来应用系统的默认背景颜色(通常为白色). 例如, 使用 LV.Opt("+BackgroundDefault") 可以把 ListView 恢复为默认的颜色.

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

Checked: 在每行的左侧提供一个复选框. 当添加行时, 在其选项中指定单词 Check 来让复选框初始为选中而不是未选中状态. 用户可以点击复选框或按下空格键来选中或取消选中一行.

Count: 指定单词 Count 后面跟着 ListView 最后会包含的总行数. 这不是限制: 超过此数目后仍然可以添加行. 相反, 此选项给控件一个暗示, 允许它一次性的而不是每次添加一行时分配内存, 这样极大地提高了添加行时的性能(这也可以提高排序性能). 要提升更多的性能, 请在添加大量行之前使用 LV.Opt("-Redraw") 和随后的 LV.Opt("+Redraw"). 有关详情, 请参阅 Redraw.

Grid: 提供水平的和垂直的线以形象地显示出行与行和列与列之间的边界.

Hdr: 指定 -Hdr(负 Hdr) 来隐藏标题(包含列标题的顶行). 后面要让它显示, 请使用 LV.Opt("+Hdr").

LV: 指定字符串 LV 后面紧跟着扩展 ListView 样式的数字. 这些样式完全独立于通用扩展样式. 例如, 指定 -E0x200 会移除通用扩展样式 WS_EX_CLIENTEDGE, 这样将会移除控件的默认边框. 与之相比, 指定 -LV0x20 会移除 LVS_EX_FULLROWSELECT 选择整行的效果.

LV0x10: 指定 -LV0x10 阻止用户通过拖动列标题到左边或右边来对它们重新排序. 然而, 通常没必要这么做, 因为列的实际重排不会影响脚本看到的列顺序. 例如, 从脚本的角度看第一列总是列 1, 即使用户实际已经把它移动到其他列的右边.

LV0x20: 指定 -LV0x20 来要求点击一行的第一个字段才可以进行选择(正常情况下, 点击 任意 字段都可以选择). 这样的好处是用户可以更方便地拖动矩形包围一组行来选择它们.

Multi: 指定 -Multi(负 Multi) 来禁止用户一次选择多行.

NoSortHdr: 禁止点击标题. 此时会显示平坦的外观而不是正常的类似按钮的外观. 与其他大多数 ListView 样式不同, 此样式在 ListView 创建后无法改变.

NoSort: 关闭当用户点击标题时发生的自动排序. 然而, 标题的外观看起来仍像按钮(除非指定了上面的 NoSortHdr 选项). 此外, ColClick 事件仍然会被触发, 所以脚本可以用自定义的排序或其他操作来响应.

ReadOnly: 指定 -ReadOnly(负 ReadOnly) 来允许编辑每行首列的文本. 要编辑某行, 请在选择后按下 F2 键(请参阅下面的 WantF2 选项). 或者, 您可以对一行点击一次来选择它, 至少等待半秒钟, 然后再次点击同一行进行编辑.

R: 行高(创建时). 指定字母 R 后面紧跟着要在控件中留出空间的行数. 例如, R10 会设置控件为 10 行的高度. 如果 ListView 是用报表视图以外的视图模式创建的, 那么控件的大小将适合于图标行而不是文本行. 注意: 在 ListView 的行中添加图标将增加每行的高度, 这将使该选项不准确.

Sort: 控件根据第一列的内容按字母顺序进行排序.

SortDesc: 和上面一样, 不过这里是降序排列.

WantF2: 指定 -WantF2(负 WantF2) 阻止 F2编辑当前聚焦的行. 这个设置会被忽略, 除非 -ReadOnly 也有效.

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

视图模式

ListView 有五种视图模式, 其中最常见的是报告视图(这是默认的). 要使用其他某种视图, 请在选项列表中指定它的名称. 还可以在控件创建后改变视图模式; 例如: LV.Opt("+IconSmall").

Icon: 显示大图标视图. 在该视图和除 Report 之外的所有其他视图中, 除第一列之外的其他列中的文本是不可见的. 要在这种模式下显示图标, 必须有一个大图标 ImageList 分配给它.

Tile: 显示大图标视图, 但具有人体工学差异, 例如每项的文本显示在图标的右边而不是下面. Checkbox 在此视图中不起作用.

IconSmall: 显示小图标视图.

List: 以列表格式显示小图标视图, 其中图标显示在列中. 列数取决于控件的宽度和其中最宽的文本项的宽度.

Report: 切换回报告视图, 这是最初的默认模式. 例如: LV.Opt("+Report").

ListViews 的内置方法

除了 GUI 控件的默认方法/属性外, ListView 控件还有以下方法.

当在此页面使用短语 "行号" 时, 它指的是行在 ListView 中的当前位置. 最上面的行是 1, 第二行是 2, 以此类推. 当行被添加后, 它的行号往往会因为其他行的排序, 删除和插入而改变. 因此, 如果要根据行的内容找到特定的行, 通常最好在循环中使用 GetText 方法.

行的方法:

列的方法:

检索方法:

其他方法:

Add

添加新行到列表底部.

RowNumber := LV.Add(Options, Col1, Col2, ...)

参数

Options

类型: 字符串

如果为空或省略, 则默认为无选项. 否则, 请指定下表中的一或多个选项(不区分大小写). 选项间使用空格或 tab 分隔. 要移除某个选项, 请在该选项前加上负号. 要添加选项, 允许添加加号, 但不是必须的.

Check: 在行中显示选中标记(需要 ListView 含有 Checkbox). 以后要取消选中它, 请使用 LV.Modify(RowNumber, "-Check").

Col: 指定单词 Col 后紧跟着列号, 从此列开始往后应用参数 Col1 及以后. 此选项通常和 Modify 方法一起使用来修改行中的个别字段, 而不会影响其左侧的字段.

Focus: 设置键盘焦点到此行(通常和下面的 Select 选项一起使用). 以后要取消它的焦点, 请使用 LV.Modify(RowNumber, "-Focus").

Icon: 指定单词 Icon 后紧跟着这行图标的编号, 这行图标显示在首列的左侧. 如果此选项不存在, 则使用 ImageList 中的首个图标. 要显示空白图标, 请指定 -1 或一个大于图像列表中图标数目的数字. 如果控件没有小图标的图像列表, 则报告视图中既不显示图标也不为其保留空间.

该选项接受基于 1 开始的的图标编号, 但在内部转换为基于 0 开始的索引; 因此, Icon0 对应于常数 I_IMAGECALLBACK, 它通常被定义为 -1, 而 Icon-1 对应于 I_IMAGENONE. 其他超出范围的值也可能导致图标所在位置出现空白.

Select: 选择行. 以后要取消选择, 请使用 LV.Modify(RowNumber, "-Select"). 当选择行时, 通常最好确保至少有一行总含有聚焦属性, 为这样允许 Apps 键在焦点行附近显示其上下文菜单(如果有). 单词 Select 后可以紧跟着 0 或 1 来表示初始状态. 换句话说, both "Select""Select" . VarContainingOne 是一样的(此处的句点是连接运算符). 这种技术也适用于上面的 FocusCheck 选项.

Vis: 必要时通过滚动 ListView 来确保指定行完全可见. 此参数仅对 LV.Modify 有效; 例如: LV.Modify(RowNumber, "Vis").

Col1, Col2, ...

类型: 字符串

新行中的列, 可以是文本或数字(包括数字表达式的结果). 要使任何字段成为空, 请指定 "" 或等价物. 如果字段太少无法填满所有的列, 那么末尾的列将被留空. 如果字段太多, 末尾的字段将被完全忽略.

返回值

类型: 整数

该方法返回新的行号, 如果 ListView 具有 SortSortDesc 样式, 则不一定是最后一行.

Insert

在指定行号插入一个新行.

RowNumber := LV.Insert(RowNumber , Options, Col1, Col2, ...)

参数

RowNumber

类型: 整数

新插入行的行号. 任何位于 RowNumber 或其下方的行都会被向下移动, 以便为新行腾出空间. 如果 RowNumber 大于列表中的行数(甚至大于 2147483647), 新的行将被添加到列表的最后.

Options

类型: 字符串

如果为空或省略, 则默认为无选项. 否则, 请指定上表中一个或多个选项.

Col1, Col2, ...

类型: 字符串

新行中的列, 可以是文本或数字(包括数字表达式的结果). 要使任何字段成为空, 请指定 "" 或等价物. 如果字段太少无法填满所有的列, 那么末尾的列将被留空. 如果字段太多, 末尾的字段将被完全忽略.

返回值

类型: 整数

该方法返回指定的行号.

Modify

修改行的属性和/或文本.

LV.Modify(RowNumber , Options, NewCol1, NewCol2, ...)

参数

RowNumber

类型: 整数

要修改的行. 如果为 0, 那么控件中的 所有 的行都会被修改.

Options

类型: 字符串

如果为空或省略, 则默认为无选项. 否则, 请指定上表中的一个或多个选项. ColN 选项可用于更新特定的列而不影响其他列.

NewCol1, NewCol2, ...

类型: 字符串

指定行的新列, 可以是文本或数字(包括数字表达式的结果). 要使任何字段成为空, 请指定 "" 或等价物. 如果字段太少无法填满所有的列, 那么末尾的列将被留空. 如果字段太多, 末尾的字段将被完全忽略.

备注

如果只存在前两个参数, 则仅修改行的属性而不改变其文本.

Delete

删除指定行或所有行.

LV.Delete(RowNumber)

参数

RowNumber

类型: 整数

如果省略, 则删除 ListView 中的 所有 行. 否则, 请指定要删除的行号.

ModifyCol

修改指定列和标题的属性和/或文本.

LV.ModifyCol(ColumnNumber, Options, ColumnTitle)

参数

ColumnNumber

类型: 整数

如果省略所有参数, 则调整所有列的宽度以适应行的内容. 只有在报告(详细信息) 视图中才有效果.

否则, 请指定要修改的列的列号. 首列的列号为 1(不是 0).

Options

类型: 整数

如果省略, 则默认为 Auto(则调整列的宽度以适应其的内容). 否则, 请指定下表中的一个或多个选项(不区分大小写). 选项间使用空格或 tab 分隔. 要移除选项, 请在选项前加上负号. 要添加选项, 允许使用加号, 但不是必需的.


常规选项

N: 指定 N 为列的新宽度, 单位为像素. 如果是唯一的选择, 这个数字可以不加引号. 例如, 后面两种形式都是有效的: LV.ModifyCol(1, 50) and LV.ModifyCol(1, "50 Integer").

Auto: 调整列宽来适应其内容. 只有在报告(详细信息) 视图中这才有效果.

AutoHdr: 调整列宽来适应其内容和列标题的文本, 以其中较宽的为准. 当应用于最后一列时, 它将被设置为至少和 ListView 中剩余空间的宽度一样. 通常最好仅在行已经添加后才应用此设置, 因为这样允许当调整最后一列的大小时考虑到可能新增加的垂直滚动栏. 只有在报告(详细信息) 视图中这才有效果.

Icon: 指定单词 Icon 后紧跟着图像列表中要显示在列标题文本后的图标的编号. 指定 -Icon(负 icon) 来移除任何现有的图标.

IconRight: 把图标放在列的右边而不是左.


数据类型选项

Float: 为了进行排序, 指明此列包含浮点数(不支持十六进制格式). 浮点数和文本列的排序性能最多可能比整数列的慢 25 倍.

Integer: 为了进行排序, 指明此列包含整数. 要正确排序, 每个整数必须是 32 位的; 即在范围 -2147483648 到 2147483647 内. 任何非整数的值在排序时将被视为零(不过如果这些值以一个数字开头, 则使用这个数字). 数字格式可以为十进制或十六进制(例如 0xF9E0).

Text: 更改列返回到文本模式进行排序, 这是每列初始默认的排序方式. 在排序时只有文本开始的 8190 个字符是有意义的(如果如果使用了 Logical 选项, 此时限制为 4094).


对齐选项

Center: 让文本居于列中央. 要让 Integer 或 Float 列居中, 请把单词 Center 加到 IntegerFloat 后.

Left: 左对齐列文本, 这是每列初始默认的对齐方式. 在较旧的操作系统中, 首列可能会强制进行左对齐.

Right: 右对齐列文本. 对于 Integer 和 Float 列不需要指定此属性, 因为它们默认是右对齐的. 通过指定例如 "Integer Left""Float Center" 的字符串可以覆盖此默认的对齐方式.


排序选项

Case: 让列排序区分大小写(仅影响文本列). 如果 Case, CaseLocaleLogical 选项都省略, 则在排序中大写字母 A-Z 被视为等同于它们相应的小写形式.

CaseLocale: 让列排序基于当前用户的区域设置且不区分大小写(仅影响文本列). 例如, 大多数英语和西欧地区把字母 A-Z 和 ANSI 字母例如 Ä 和 Ü 等同于它们的小写形式. 这种方法也使用 "单词排序", 它把单词中的连字符和撇号例如 "coop" 和 "co-op" 保持在一起.

Desc: 降序排列. 用户首次对此列排序时使用下降的顺序.

Logical:CaseLocale 相同, 除了把文本中的任何数字位序列看成真正的数字而不仅仅字符. 例如, 字符串 "T33" 将被视为大于 "T4". LogicalCase 是互斥的: 只有最近指定的那个才有效.

NoSort: 避免用户点击此列对自动排序产生的影响. 然而, ColClick 事件仍然会被触发, 所以脚本可以用自定义的排序或其他操作来响应. 要禁用所有列的排序而不仅是其中的子集, 请在 ListView 的选项中包含 NoSort.

Sort: 立即对列按升序排列(即使它含有 Desc 选项).

SortDesc: 立即对列按降序排列.

Uni: 单向排序. 此选项防止在相同列进行第二次点击来反转排序方向.

ColumnTitle

类型: 字符串

如果省略, 当前标题保持不变. 否则, 请指定列的新标题.

InsertCol

在指定列号插入一个新列.

ColumnNumber := LV.InsertCol(ColumnNumber, Options, ColumnTitle)

参数

ColumnNumber

类型: 整数

如果省略或大于控件中的当前列数, 则新列将添加到右侧最后一列的旁边.

否则, 指定新插入的列的列号. 在 ColumnNumber 处和其右侧的任何列都会向右移动, 以便为新列腾出空间. 第一列的编号为 1(不是 0).

Options

类型: 字符串

如果省略, 列总是以默认值开始, 例如是否使用整数排序. 否则, 请指定上表中的一个或多个选项.

ColumnTitle

类型: 字符串

如果为空或省略, 则默认为空的标题. 否则, 请指定列的标题.

返回值

类型: 整数

函数返回新列的位置编号.

备注

新插入列的初始内容为空, 然而如果它是首列则会继承原来首列的内容, 同时原来首列的内容变为空白.

ListView 中的最大列数是 200.

DeleteCol

删除指定的列及其中所有内容.

LV.DeleteCol(ColumnNumber)

参数

ColumnNumber

类型: 整数

要删除的列. 当一列被删除后, 位于其右侧的列号将减少 1. 因此, 调用 LV.DeleteCol(2) 两次, 将删除第二列和第三列.

GetCount

返回控件中行数或列数.

Count := LV.GetCount(Mode)

参数

Mode

类型: 字符串

如果为空或省略, 该方法返回控件中的总行数. 否则, 请指定以下字符串之一:

SSelected: 计数仅包括选择的/高亮的行.

ColColumn: 方法返回控件中的列数.

返回值

类型: 整数

此方法返回控件的行数或列数. 该值总是即时返回的, 因为控件会跟踪这些计数.

备注

这个方法通常用于 Loop 的顶行, 在这种情况下, 这个方法只会被调用一次(在首次循环前). 例如:

Loop LV.GetCount()
{
    RetrievedText := LV.GetText(A_Index)
    if InStr(RetrievedText, "some filter text")
        LV.Modify(A_Index, "Select")  ; 选择首个字段包含过滤文本的所有行.
}

要获取 ListView 的列宽 -- 用于例如保存它们到 INI 文件以便在会话期间保持一致 -- 请参照此例:

Loop LV.GetCount("Column")
{
    ColWidth := SendMessage(0x101D, A_Index - 1, 0, LV)  ; 0x101D 是 LVM_GETCOLUMNWIDTH.
    MsgBox("Column " A_Index "'s width is " ColWidth ".")
}

GetNext

返回下一个选择, 选中或具有焦点的行的行号.

RowNumber := LV.GetNext(StartingRowNumber, RowType)

参数

StartingRowNumber

类型: 整数

如果省略或小于 1, 则从列表的顶部开始搜索. 否则, 请指定开始搜索的行号.

RowType

类型: 字符串

如果省略, 则方法搜索下一个选择的/高亮的行(请参阅下面的例子). 否则, 请指定以下字符串之一:

CChecked: 寻找下一个选中的行.

FFocused: 寻找焦点行. 在整个列表中不可能有多个焦点行, 且有时甚至没有.

返回值

类型: 整数

此方法返回下一个选择, 选中或聚焦的行的行号. 如果没有找到, 返回 0.

备注

下面的示例, 报告 ListView 中所有选择的行:

RowNumber := 0  ; 这样使得首次循环从列表的顶部开始搜索.
Loop
{
    RowNumber := LV.GetNext(RowNumber)  ; 在前一次找到的位置后继续搜索.
    if not RowNumber  ; 上面返回零, 所以选择的行已经都找到了.
        break
    Text := LV.GetText(RowNumber)
    MsgBox('The next selected row is #' RowNumber ', whose first field is "' Text '".')
}

判断一个特定的行是否被选中的另一种方法如下:

ItemState := SendMessage(0x102C, RowNumber - 1, 0xF000, LV)  ; 0x102C 是 LVM_GETITEMSTATE. 0xF000 是 LVIS_STATEIMAGEMASK.
IsChecked := (ItemState >> 12) - 1  ; 如果 RowNumber 为选中的则设置 IsChecked 为真, 否则为假.

GetText

检索指定行号和列号的文本.

Text := LV.GetText(RowNumber , ColumnNumber)
RowNumber

类型: 整数

要检索文本的行的编号. 如果为 0, 则获取列标题的文本.

ColumnNumber

类型: 整数

如果省略, 默认为 1(首列的文本). 否则, 请指定 RowNumber 所在的行中的列号.

返回值

类型: 整数

此方法返回检索到的文本. 最多只能检索 8191 个字符.

备注

脚本中看到的列编号不会因为用户所做的拖放而改变. 例如, 最初首列的编号仍是 1, 即使用户把它拖到其他列的右边.

SetImageList

设置或替换用于显示图标的 ImageList(图像列表).

PrevImageListID := LV.SetImageList(ImageListID , IconType)

参数

ImageListID

类型: 整数

从之前调用 IL_Create 返回的 ID 数字.

IconType

类型: 整数

如果省略, 则自动检测 ImageList 中的图标类型为大图标或小图标. 否则, 指定 0 为大图标, 1 为小图标, 或者 2 为状态图标(这还不直接支持, 但可以通过 SendMessage 使用).

返回值

类型: 整数

成功时, 函数返回先前与 ListView 关联的 ImageList ID. 失败时, 返回 0. 任何这样分离的 ImageList 通常应该通过 IL_Destroy 来销毁.

备注

这个方法通常在向 ListView 添加任何行之前被调用. 它设置 ImageList, 其中的图标会显示在 ListView 的行中(可选的, 以及列).

一个 ListView 最多可以有两个图像列表: 小图标和/或大图标. 当脚本允许用户在大图标视图之间进行切换时, 这很有用. 要再关联一个图像列表到 ListView, 请再次调用 SetImageList 方法, 指定第二个列表的 ImageListID. 一个同时拥有大图标和小图标 ImageList 的 ListView 应该确保两个列表中的图标顺序相同. 这是因为相同的 ID 号被用来引用特定图标的大版本和小版本.

尽管除了图标模式和平铺模式外, 传统的所有的视图模式都显示小图标, 但可以通过传递一个大图标列表到 SetImageList 方法, 并且在第二个参数中指定 1(小图标) 覆盖此行为. 这也增加了 ListView 中每行的高度以适合大图标.

事件

以下事件可以通过调用 OnEvent 来注册一个回调函数或方法来检测:

事件当...触发
Click控件被点击.
DoubleClick控件被双击.
ColClick列标题被点击.
ContextMenu当控件拥有键盘焦点时, 用户右键单击控件或按下 MenuShift+F10.
Focus控件获得键盘焦点.
LoseFocus控件失去键盘焦点.
ItemCheck一个项目被选中或取消选中.
ItemEdit一个项目的标签被用户编辑.
ItemFocus焦点项目改变.
ItemSelect一个项目被选中或取消选中.

额外的(很少使用的) 通知可以通过使用 OnNotify 来检测. 这些通知在 Microsoft Docs 上都有文档. Microsoft Docs 没有显示每个通知代码的数值; 这些可以在 Windows SDK 中找到, 或者在互联网上搜索.

ImageList(图像列表)

图像列表是存储在内存中的一组相同大小的图标. 在创建时, 每个图像列表是空的. 脚本重复调用 IL_Add 来添加图标到列表中, 且给每个图标分配一个序号, 序号从 1 开始. 脚本引用此编号在一行中或列标题中显示一个特定的图标. 这是个可运行示例, 其中演示了如何在 ListView 的行中显示图标:

MyGui := Gui()  ; 创建 MyGui 窗口.
LV := MyGui.Add("ListView", "h200 w180", ["Icon & Number","Description"])  ; 创建 ListView.
ImageListID := IL_Create(10)  ; 创建加载 10 个小图标的图像列表.
LV.SetImageList(ImageListID)  ; 把上面的图像列表指定给当前的 ListView.
Loop 10  ; 把 DLL 中的一系列图标装入图像列表.
    IL_Add(ImageListID, "shell32.dll", A_Index) 
Loop 10  ; 在 ListView 中添加行(出于演示的目的, 每个图标一行).
    LV.Add("Icon" . A_Index, A_Index, "n/a")
MyGui.Show

IL_Create

创建一个新的初始为空的图像列表.

ImageListID := IL_Create(InitialCount, GrowCount, LargeIcons)

参数

InitialCount

类型: 整数

如果省略, 默认为 2. 否则, 请指定希望立即放入列表的图标数量.

GrowCount

类型: 整数

如果省略, 默认为 5. 否则, 请指定每次超过当前列表容量时, 列表将增加的图标数量.

LargeIcons

类型: 布尔值

如果省略, 则默认为 false.

如果为 false, ImageList 将包含小图标.

如果为 true, ImageList 将包含大图标.

添加到列表中的图标自动进行缩放, 以符合系统中大小图标的尺寸.

返回值

类型: 整数

成功时, 函数返回新创建的图像列表的唯一 ID. 失败时, 返回 0.

IL_Add

添加图标或图片到指定的图像列表.

IconIndex := IL_Add(ImageListID, IconFileName , IconNumber)
IconIndex := IL_Add(ImageListID, PicFileName, MaskColor, Resize)

参数

ImageListID

类型: 整数

之前调用 IL_Create 返回的 ID 号.

IconFileName

类型: 字符串

图标(.ICO), 光标(.CUR) 或动画光标(.ANI) 文件的名称(动态光标在 ListView 中显示时实际将不会动), 或图标句柄 , 如 "HBITMAP:" handle. 图标的其他来源包含下列类型的文件: EXE, DLL, CPL, SCR, 以及包含图标资源的其他类型.

IconNumber

类型: 整数

如果省略, 默认为 1(第一个图标组). 否则, 请指定请指定文件中要使用的图标组的编号. 如果为负数, 则假定其绝对值表示可执行文件中图标的资源 ID. 在下面的例子中, 将使用第二个图标组中的默认图标: IL_Add(ImageListID, "C:\My Application.exe", 2).

PicFileName

类型: 字符串

非图标图像的名称, 例如 BMP, GIF, JPG, PNG, TIF, Exif, WMF 和 EMF, 或位图句柄, 如 "HBITMAP:" handle.

MaskColor

类型: 整数

屏蔽的/透明的颜色编码. 对于大多数图片, 0xFFFFFF(白色) 可能是最佳的.

Resize

类型: 布尔值

如果为 true, 则图片缩放为单个图标.

如果为 false, 则图片将被分割为多个可以匹配实际宽度的图标.

返回值

类型: 整数

成功时, 函数返回新图标的索引(首个图标索引为 1, 第二个为 2, 依此类推). 失败时, 返回 0.

IL_Destroy

删除指定的图像列表.

IsDestroyed := IL_Destroy(ImageListID)

参数

ImageListID

类型: 整数

之前调用 IL_Create 返回的 ID 号.

返回值

类型: 整数(布尔值)

成功时, 函数返回 1(true). 失败时, 返回 0(false).

备注

通常没必要销毁图像列表, 因为一旦关联到 ListView, 当 ListView 或其父窗口销毁时它们会被自动销毁. 但是, 如果一个 ListView 和其他 ListView 共享图像列表(通过在选项中包含 0x40), 在所有使用此图像列表的 ListView 销毁后脚本应该显式地销毁它. 同样地, 如果脚本使用一个新的图像列表代替 ListView 中原来的, 则应该显式销毁原来的图像列表.

备注

Gui.Submit 对 ListView 控件没有效果.

在对某个列进行排序后 -- 通过用户点击其标题或脚本调用 LV.ModifyCol(1, "Sort") -- 任何随后添加的行将出现在列表的底部而不会遵循排序顺序. 例外情况是 SortSortDesc 样式, 它们会把新添加的行移动正确的位置.

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

MyGui.Add("Button", "Hidden Default", "OK").OnEvent("Click", LV_Enter)
...
LV_Enter(*) {
    global
    if MyGui.FocusedCtrl != LV
        return
    MsgBox("Enter was pressed. The focused row number is " LV.GetNext(0, "Focused"))
}

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

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

尽管 ListView 中可以使用的最大行数仅受系统可用内存的限制, 但参照 Count 选项中描述的方法可以极大地提高添加行的性能.

可以使用图片作为 ListView 周围的背景(即作为 ListView 的框架). 要实现这个效果, 请在 ListView 后创建图片控件并且在图片控件的 Options 中包含 0x4000000(这是 WS_CLIPSIBLINGS).

脚本可以在每个窗口中创建多个 ListView.

最好不要使用 SendMessage 直接插入或删除列. 这是因为程序为每个列维护一个排序参数集, 而这会让它们无法同步. 作为替代, 请使用内置的列的方法.

要对 ListView 执行一些操作(例如调整大小, 隐藏或改变字体), 请参阅 GuiControl 对象.

要从外部 ListView(那些不属于脚本自身的) 中提取文本, 请使用 ListViewGetContent.

TreeView, 其他控件类型, Gui, ContextMenu 事件, Gui 对象, GuiControl 对象, ListView 样式列表

示例

指定行号为 0 来选择或取消选择所有行.

LV.Modify(0, "Select")   ; 选择所有行.
LV.Modify(0, "-Select")  ; 取消选择所有行.
LV.Modify(0, "-Check")  ;取消选中所有复选框.

自动根据内容调整所有列的宽度.

LV.ModifyCol  ; 此模式不需要参数.

下面是一个比页面顶部的脚本更复杂的可工作的脚本. 它显示用户选择的文件夹中的文件, 且每个文件分配一个与其类型关联的图标. 用户在一个文件上双击或在一个或多个文件上右击后, 会显示上下文菜单.

; 创建 GUI 窗口:
MyGui := Gui("+Resize")  ; 允许用户最大化窗口或拖动来改变窗口的大小.

; 创建一些按钮:
B1 := MyGui.Add("Button", "Default", "Load a folder")
B2 := MyGui.Add("Button", "x+20", "Clear List")
B3 := MyGui.Add("Button", "x+20", "Switch View")

; 通过 MyGui.Add 创建 ListView 及其列:
LV := MyGui.Add("ListView", "xm r20 w700", ["Name","In Folder","Size (KB)","Type"])
LV.ModifyCol(3, "Integer")  ; 为了排序, 表示 Size 列中的内容是整数.

; 创建图像列表, 这样 ListView 才可以显示图标:
ImageListID1 := IL_Create(10)
ImageListID2 := IL_Create(10, 10, true)  ; 搭配小图标列表的大图标列表.

; 将图像列表附加到 ListView 上, 这样它就可以在以后显示图标:
LV.SetImageList(ImageListID1)
LV.SetImageList(ImageListID2)

; 应用控件事件:
LV.OnEvent("DoubleClick", RunFile)
LV.OnEvent("ContextMenu", ShowContextMenu)
B1.OnEvent("Click", LoadFolder)
B2.OnEvent("Click", (*) => LV.Delete())
B3.OnEvent("Click", SwitchView)

; 应用窗口事件:
MyGui.OnEvent("Size", Gui_Size)

; 创建弹出式菜单, 作为上下文菜单使用:
ContextMenu := Menu()
ContextMenu.Add("Open", ContextOpenOrProperties)
ContextMenu.Add("Properties", ContextOpenOrProperties)
ContextMenu.Add("Clear from ListView", ContextClearRows)
ContextMenu.Default := "Open"  ; 将 "Open(打开)" 改为粗体字, 以表明这是双击的默认动作.

; 显示窗口:
MyGui.Show()

LoadFolder(*)
{
    static IconMap := Map()
    MyGui.Opt("+OwnDialogs") ; 强制用户在使用主窗口之前关闭下面的对话框.
    Folder := DirSelect(, 3, "Select a folder to read:")
    if not Folder  ; 用户取消对话框.
        return

    ; 检查文件夹名称的最后一个字符是否为反斜杠, 对于根目录则会如此,
    ; 例如 C:\. 如果是, 则移除这个反斜杠以避免之后出现两个反斜杠.
    if SubStr(Folder, -1, 1) = "\"
        Folder := SubStr(Folder, 1, -1)  ; 移除尾部的反斜杠.

    ; 计算 SHFILEINFO 结构需要的缓冲大小.
    sfi_size := A_PtrSize + 688
    sfi := Buffer(sfi_size)

    ; 获取所选择文件夹中的文件名列表并添加到 ListView:
    LV.Opt("-Redraw")  ; 在加载时禁用重绘来提升性能.
    Loop Files, Folder "\*.*"
    {
        FileName := A_LoopFilePath  ; 必须保存到可写的变量中供后面使用.

        ; 建立唯一的扩展 ID 以避免变量名中的非法字符,
        ; 例如破折号. 这种使用唯一 ID 的方法也会执行地更好,
        ; 因为在数组中查找项目不需要进行搜索循环.
        SplitPath(FileName,,, &FileExt)  ; 获取文件扩展名.
        if FileExt ~= "i)\A(EXE|ICO|ANI|CUR)\z"
        {
            ExtID := FileExt  ; 特殊 ID 作为占位符.
            IconNumber := 0  ; 将其标记为未找到, 以便这些类型可以有一个唯一的图标.
        }
        else  ; 其他的扩展名/文件类型, 计算它们的唯一 ID.
        {
            ExtID := 0  ; 进行初始化来处理比其他更短的扩展名.
            Loop 7     ; 限制扩展名为 7 个字符, 这样之后计算的结果才能存放到 64 位值.
            {
                ExtChar := SubStr(FileExt, A_Index, 1)
                if not ExtChar  ; 没有更多字符了.
                    break
                ; 把每个字符与不同的位置进行运算来得到唯一 ID:
                ExtID := ExtID | (Ord(ExtChar) << (8 * (A_Index - 1)))
            }
            ; 检查此文件扩展名的图标是否已经在图像列表中. 如果是,
            ; 可以避免多次调用并极大提高性能,
            ; 尤其对于包含数以百计文件的文件夹而言:
            IconNumber := IconMap.Has(ExtID) ? IconMap[ExtID] : 0
        }
        if not IconNumber  ; 此扩展名还没有相应的图标, 所以进行加载.
        {
            ; 取与此文件扩展名关联的高质量小图标:
            if not DllCall("Shell32\SHGetFileInfoW", "Str", FileName
            , "Uint", 0, "Ptr", sfi, "UInt", sfi_size, "UInt", 0x101)  ; 0x101 是 SHGFI_ICON+SHGFI_SMALLICON
                IconNumber := 9999999  ; 把它设置到范围外来显示空图标.
            else ; 成功加载图标.
            {
                ; 从结构中提取 hIcon 成员:
                hIcon := NumGet(sfi, 0, "Ptr")
                ; 直接添加 HICON 到小图标和大图标列表.
                ; 下面加上 1 来把返回的索引从基于零转换到基于一:
                IconNumber := DllCall("ImageList_ReplaceIcon", "Ptr", ImageListID1, "Int", -1, "Ptr", hIcon) + 1
                DllCall("ImageList_ReplaceIcon", "Ptr", ImageListID2, "Int", -1, "Ptr", hIcon)
                ; 现在已经把它复制到图像列表, 所以应销毁原来的:
                DllCall("DestroyIcon", "Ptr", hIcon)
                ; 缓存图标来节省内存并提升加载性能:
                IconMap[ExtID] := IconNumber
            }
        }

        ; 在 ListView 中创建新行并把它和上面的图标编号进行关联:
        LV.Add("Icon" . IconNumber, A_LoopFileName, A_LoopFileDir, A_LoopFileSizeKB, FileExt)
    }
    LV.Opt("+Redraw")  ; 重新启用重绘(上面把它禁用了).
    LV.ModifyCol()  ; 根据内容自动调整每列的大小.
    LV.ModifyCol(3, 60)  ; 把 Size 列加宽一些以便显示出它的标题.
}

SwitchView(*)
{
    static IconView := false
    if not IconView
        LV.Opt("+Icon")        ; 切换到图标视图.
    else
        LV.Opt("+Report")      ; 切换回详细信息视图.
    IconView := not IconView   ; 进行反转以为下次做准备.
}

RunFile(LV, RowNumber)
{
    FileName := LV.GetText(RowNumber, 1) ; 获取首个字段的文本.
    FileDir := LV.GetText(RowNumber, 2)  ; 获取第二个字段的文本.
    try
        Run(FileDir "\" FileName)
    catch
        MsgBox("Could not open " FileDir "\" FileName ".")
}

ShowContextMenu(LV, Item, IsRightClick, X, Y)  ; 响应右键或 Apps 键.
{
    ; 在所提供的 X 和 Y 坐标处显示菜单.
    ; 这些应该被使用, 因为即使用户按了 Apps 键, 它们也能提供正确的坐标:
    ContextMenu.Show(X, Y)
}

OpenOrProperties(ItemName, *)  ; 用户在上下文菜单中选择了 "Open" 或 "Properties".
{
    ; 简单地说, 只对焦点行进行操作, 而不是对所有选中的行进行操作:
    FocusedRowNumber := LV.GetNext(0, "F")  ; Find the focused row.
    if not FocusedRowNumber  ; 没有聚焦行.
        return
    FileName := LV.GetText(FocusedRowNumber, 1) ; 获取首个字段的文本.
    FileDir := LV.GetText(FocusedRowNumber, 2)  ; 获取第二个字段的文本.
    try
    {
        if (ItemName = "Open")  ; 用户在上下文菜单中选择了 "Open".
            Run(FileDir "\" FileName)
        else
            Run("properties " FileDir "\" FileName)
    }
    catch
        MsgBox("Could not perform requested action on " FileDir "\" FileName ".")
}

ContextClearRows(*)  ; 用户在上下文菜单中选择了 "Clear".
{
    RowNumber := 0  ; 这会使得首次循环从顶部开始搜索.
    Loop
    {
        ; 由于删除了一行使得此行下面的所有行的行号都减小了,
        ; 所以把行号减 1, 这样搜索里包含的行号才会与之前找到的行号相一致
        ; (以防选择了相邻行):
        RowNumber := LV.GetNext(RowNumber - 1)
        if not RowNumber  ; 上面返回零, 所以没有更多选择的行了.
            break
        LV.Delete(RowNumber)  ; 从 ListView 中删除行.
    }
}

Gui_Size(thisGui, MinMax, Width, Height)  ; 扩大或缩小 ListView 来响应用户对窗口大小的改变.
{
    if MinMax = -1  ; 窗口被最小化了. 无需进行操作.
        return
    ; 否则, 窗口的大小被调整过或被最大化了. 调整 ListView 的大小来适应.
    LV.Move(,, Width - 20, Height - 40)
}