ListView (GUI)

Auto Hotkey

ListView

Table of Contents

Introduction and Simple Example

A List-View is one of the most elaborate controls provided by the operating system. In its most recognizable form, it displays a tabular view of rows and columns, the most common example of which is Explorer's list of files and folders (detail view).

Though it may be elaborate, a ListView's basic features are easy to use. The syntax for creating a ListView is:

LV := Gui.Add("ListView", Options, "ColumnTitle1|ColumnTitle2|...")

Here is a working script that creates and displays a ListView containing a list of files in the user's "My Documents" folder:

; Create the window.
Gui := GuiCreate()

; Create the ListView with two columns, Name and Size:
LV := Gui.Add("ListView", "r20 w700", "Name|Size (KB)")

; Notify the script whenever the user double clicks a row:
LV.OnEvent("DoubleClick", "LV_DoubleClick")

; Gather a list of file names from a folder and put them into the ListView:
LoopFiles("%A_MyDocuments%\*.*")
  LV.Add(, A_LoopFileName, A_LoopFileSizeKB)

LV.ModifyCol()  ; Auto-size each column to fit its contents.
LV.ModifyCol(2, "Integer")  ; For sorting purposes, indicate that column 2 is an integer.

; Display the window.
Gui.Show()

LV_DoubleClick(LV, RowNumber)
{
  RowText := LV.GetText(RowNumber)  ; Get the text from the row's first field.
  ToolTip("You double-clicked row number %RowNumber%. Text: '%RowText%'")
}

Options and Styles for the Options parameter

Background: Specify the word Background followed immediately by a color name (see color chart) or RGB value (the 0x prefix is optional). Examples: BackgroundSilver, BackgroundFFDD99. If this option is not present, the ListView initially defaults to the system's default background color. Specifying BackgroundDefault or -Background applies the system's default background color (usually white). For example, a ListView can be restored to the default color via LV.Opt("+BackgroundDefault").

C: Text color. Specify the letter C followed immediately by a color name (see color chart) or RGB value (the 0x prefix is optional). Examples: cRed, cFF2211, c0xFF2211, cDefault.

Checked: Provides a checkbox at the left side of each row. When adding a row, specify the word Check in its options to have the box to start off checked instead of unchecked. The user may either click the checkbox or press the spacebar to check or uncheck a row.

Count: Specify the word Count followed immediately by the total number of rows that the ListView will ultimately contain. This is not a limit: rows beyond the count can still be added. Instead, this option serves as a hint to the control that allows it to allocate memory only once rather than each time a row is added, which greatly improves row-adding performance (it may also improve sorting performance). To improve performance even more, use LV.Opt("-Redraw") prior to adding a large number of rows. Afterward, use LV.Opt("+Redraw") to re-enable redrawing (which also repaints the control).

Grid: Provides horizontal and vertical lines to visually indicate the boundaries between rows and columns.

Hdr: Specify -Hdr (minus Hdr) to omit the special top row that contains column titles. To make it visible later, use LV.Opt("+Hdr").

LV: Specify the string LV followed immediately by the number of an extended ListView style. These styles are entirely separate from generic extended styles. For example, specifying -E0x200 would remove the generic extended style WS_EX_CLIENTEDGE to eliminate the control's default border. By contrast, specifying -LV0x20 would remove LVS_EX_FULLROWSELECT.

LV0x10: Specify -LV0x10 to prevent the user from dragging column headers to the left or right to reorder them. However, it is usually not necessary to do this because the physical reordering of columns does not affect the column order seen by the script. For example, the first column will always be column 1 from the script's point of view, even if the user has physically moved it to the right of other columns.

LV0x20: Specify -LV0x20 to require that a row be clicked at its first field to select it (normally, a click on any field will select it). The advantage of this is that it makes it easier for the user to drag a rectangle around a group of rows to select them.

Multi: Specify -Multi (minus Multi) to prevent the user from selecting more than one row at a time.

NoSortHdr: Prevents the header from being clickable. It will take on a flat appearance rather than its normal button-like appearance. Unlike most other ListView styles, this one cannot be changed after the ListView is created.

NoSort: Turns off the automatic sorting that occurs when the user clicks a column header. However, the header will still behave visually like a button (unless NoSortHdr has been specified). In addition, the ColClick event is still raised, so the script can respond with a custom sort or other action.

ReadOnly: Specify -ReadOnly (minus ReadOnly) to allow editing of the text in the first column of each row. To edit a row, select it then press the F2 key. Alternatively, you can click a row once to select it, wait at least half a second, then click the same row again to edit it.

R: Rows of height (upon creation). Specify the letter R followed immediately by the number of rows for which to make room inside the control. For example, R10 would make the control 10 rows tall. If the ListView is created with a view mode other than report view, the control is sized to fit rows of icons instead of rows of text. Note: adding icons to a ListView's rows will increase the height of each row, which will make this option inaccurate.

Sort: The control is kept alphabetically sorted according to the contents of the first column.

SortDesc: Same as above except in descending order.

WantF2: Specify -WantF2 (minus WantF2) to prevent an F2 keystroke from editing the currently focused row. This setting is ignored unless -ReadOnly is also in effect.

(Unnamed numeric styles): Since styles other than the above are rarely used, they do not have names. See the ListView styles table for a list.

View Modes

A ListView has five viewing modes, of which the most common is report view (which is the default). To use one of the other views, specify its name in the options list. The view can also be changed after the control is created; for example: LV.Opt("+IconSmall").

Icon: Shows a large-icon view. In this view and all the others except Report, the text in columns other than the first is not visible. To display icons in this mode, the ListView must have a large-icon ImageList assigned to it.

Tile: Shows a large-icon view but with ergonomic differences such as displaying each item's text to the right of the icon rather than underneath it. Checkboxes do not function in this view. Also, attempting to show this view on operating systems older than Windows XP has no effect.

IconSmall: Shows a small-icon view.

List: Shows a small-icon view in list format, which displays the icons in columns. The number of columns depends on the width of the control and the width of the widest text item in it.

Report: Switches back to report view, which is the initial default. For example: LV.Opt("+Report").

Built-in Methods for ListViews

Additionally to the default methods/properties of a GUI control, the following methods can be specified for a ListView. If the associated GuiControl object does not exist or does not belongs to a ListView, the ListView methods throw an exception.

When the phrase "row number" is used on this page, it refers to a row's current position within the ListView. The top row is 1, the second row is 2, and so on. After a row is added, its row number tends to change due to sorting, deleting, and inserting of other rows. Therefore, to locate specific row(s) based on their contents, it is usually best to use LV.GetText in a loop.

Row Methods

Add

Adds a new row to the bottom of the list, and returns the new row number, which is not necessarily the last row if the ListView has the Sort or SortDesc style.

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

A string containing zero or more words from the list below (not case sensitive). Separate each word from the next with a space or tab. To remove an option, precede it with a minus sign. To add an option, a plus sign is permitted but not required.

Check: Shows a checkmark in the row (if the ListView has checkboxes). To later uncheck it, use LV.Modify(RowNumber, "-Check").

Col: Specify the word Col followed immediately by the column number at which to begin applying the parameters Col1 and beyond. This is most commonly used with LV.Modify to alter individual fields in a row without affecting those that lie to their left.

Focus: Sets keyboard focus to the row (often used in conjunction with Select). To later de-focus it, use LV.Modify(RowNumber, "-Focus").

Select: Selects the row. To later deselect it, use LV.Modify(RowNumber, "-Select"). When selecting rows, it is usually best to ensure that at least one row always has the focus property because that allows the Apps key to display its context menu (if any) near the focused row. The word Select may optionally be followed immediately by a 0 or 1 to indicate the starting state. In other words, both "Select" and "Select" . VarContainingOne are the same (the period used here is the concatenation operator). This technique also works with Focus and Check above.

Vis: Ensures that the specified row is completely visible by scrolling the ListView, if necessary. This has an effect only for LV.Modify; for example: LV.Modify(RowNumber, "Vis").

Col1, Col2, ...

The columns of the new row, which can be text or numeric (including numeric expression results). To make any field blank, specify "" or the equivalent. If there are too few fields to fill all the columns, the columns at the end are left blank. If there are too many fields, the fields at the end are completely ignored.

Insert

Inserts a new row at the specified row number, and returns the new row number.

NewRowNumber := LV.Insert(RowNumber , Options, Col1, Col2, ...)
RowNumber
The row number for the newly inserted row. Any rows at or beneath RowNumber are shifted downward to make room for the new row. If RowNumber is greater than the number of rows in the list (even as high as 2147483647), the new row is added to the end of the list.
Options
See row options.
Col1, Col2, ...

The columns of the new row, which can be text or numeric (including numeric expression results). To make any field blank, specify "" or the equivalent. If there are too few fields to fill all the columns, the columns at the end are left blank. If there are too many fields, the fields at the end are completely ignored.

Modify

Modifies the attributes and/or text of a row, and returns 1 upon success and 0 upon failure.

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

Note: When only the first two parameters are present, only the row's attributes and not its text are changed.

RowNumber
The row to modify. If RowNumber is 0, all rows in the control are modified (in this case the return value is 1 on complete success and 0 if any part of the operation failed).
Options
The ColN option may be used to update specific columns without affecting the others. For other options, see row options.
NewCol1, NewCol2, ...
The new columns of the specified row, which can be text or numeric (including numeric expression results). To make any field blank, specify "" or the equivalent. If there are too few parameters to cover all the columns, the columns at the end are not changed. If there are too many fields, the fields at the end are completely ignored.

Delete

Deletes the specified row, and returns 1 upon success and 0 upon failure.

LV.Delete(RowNumber)
RowNumber
The row to delete. If this parameter is omitted, all rows in the ListView are deleted.

Column Methods

ModifyCol

Modifies the attributes and/or text of the specified column and its header, and returns 1 upon success and 0 upon failure.

LV.ModifyCol(ColumnNumber, Options, ColumnTitle)

Note: If all parameters are omitted, the width of every column is adjusted to fit the contents of the rows. If only the first parameter is present, only the specified column is auto-sized. Auto-sizing has no effect when not in Report (Details) view.

ColumnNumber
The column to modify. The first column is number 1 (not 0).
Options

A string containing zero or more words from the list below (not case sensitive). Separate each word from the next with a space or tab. To remove an option, precede it with a minus sign. To add an option, a plus sign is permitted but not required.

Column Options: General

N: Specify for N the new width of the column, in pixels. This number can be unquoted if is the only option. For example, the following are both valid: LV.ModifyCol(1, 50) and LV.ModifyCol(1, "50 Integer").

Auto: Adjusts the column's width to fit its contents. This has no effect when not in Report (Details) view.

AutoHdr: Adjusts the column's width to fit its contents and the column's header text, whichever is wider. If applied to the last column, it will be made at least as wide as all the remaining space in the ListView. It is usually best to apply this setting only after the rows have been added because that allows any newly-arrived vertical scroll bar to be taken into account when sizing the last column. This option has no effect when not in Report (Details) view.

Icon: Specify the word Icon followed immediately by the number of the ImageList's icon to display next to the column header's text. Specify -Icon (minus icon) to remove any existing icon.

IconRight: Puts the icon on the right side of the column rather than the left.

Column Options: Data Type

Float: For sorting purposes, indicates that this column contains floating point numbers (hexadecimal format is not supported). Sorting performance for Float and Text columns is up to 25 times slower than it is for integers.

Integer: For sorting purposes, indicates that this column contains integers. To be sorted properly, each integer must be 32-bit; that is, within the range -2147483648 to 2147483647. If any of the values are not integers, they will be considered zero when sorting (unless they start with a number, in which case that number is used). Numbers may appear in either decimal or hexadecimal format (e.g. 0xF9E0).

Text: Changes the column back to text-mode sorting, which is the initial default for every column. Only the first 8190 characters of text are significant for sorting purposes (except for the Logical option, in which case the limit is 4094).

Column Options: Alignment / Justification

Center: Centers the text in the column. To center an Integer or Float column, specify the word Center after the word Integer or Float.

Left: Left-justifies the column's text, which is the initial default for every column. On older operating systems, the first column might have a forced left-justification.

Right: Right-justifies the column's text. This attribute need not be specified for Integer and Float columns because they are right-justified by default. That default can be overridden by specifying something such as "Integer Left" or "Float Center".

Column Options: Sorting

Case: The sorting of the column is case sensitive (affects only text columns). If the options Case, CaseLocale, and Logical are all omitted, the uppercase letters A-Z are considered identical to their lowercase counterparts for the purpose of the sort.

CaseLocale: The sorting of the column is case insensitive based on the current user's locale (affects only text columns). For example, most English and Western European locales treat the letters A-Z and ANSI letters like Ä and Ü as identical to their lowercase counterparts. This method also uses a "word sort", which treats hyphens and apostrophes in such a way that words like "coop" and "co-op" stay together.

Desc: Descending order. The column starts off in descending order the first time the user sorts it.

Logical: Same as CaseLocale except that any sequences of digits in the text are treated as true numbers rather than mere characters. For example, the string "T33" would be considered greater than "T4". Logical requires Windows XP or later (on older OSes, CaseLocale is automatically used instead). In addition, Logical and Case are currently mutually exclusive: only the one most recently specified will be in effect.

NoSort: Prevents a user's click on this column from having any automatic sorting effect. However, the ColClick event is still raised, so the script can respond with a custom sort or other action. To disable sorting for all columns rather than only a subset, include NoSort in the ListView's options.

Sort: Immediately sorts the column in ascending order (even if it has the Desc option).

SortDesc: Immediately sorts the column in descending order.

Uni: Unidirectional sort. This prevents a second click on the same column from reversing the sort direction.

ColumnTitle
The new column header. Omit this parameter to left it unchanged.

InsertCol

Inserts a new column at the specified column number, and returns the new column's position number.

NewColumnNumber := LV.InsertCol(ColumnNumber , Options, ColumnTitle)
ColumnNumber
The column number of the newly inserted column. Any column at or on the right side of ColumnNumber are shifted to the right to make room for the new column. The first column is 1 (not 0). The maximum number of columns in a ListView is 200. If ColumnNumber is larger than the number of columns currently in the control, the new column is added next to the last column on the right side. The newly inserted column starts off with empty contents beneath it unless it is the first column, in which case it inherits the old first column's contents and the old first column acquires blank contents.
Options
The new column's attributes -- such as whether or not it uses integer sorting -- always start off at their defaults unless changed via Options.
ColumnTitle
The new column header.

DeleteCol

Deletes the specified column and all of the contents beneath it, and returns 1 upon success and 0 upon failure.

LV.DeleteCol(ColumnNumber)
ColumnNumber
The column to delete. Once a column is deleted, the column numbers of any that lie to its right are reduced by 1. Consequently, calling LV.DeleteCol(2) twice would delete the second and third columns. On operating systems older than Windows XP, attempting to delete the original first column might fail and return 0.

Getting Data Out of a ListView

GetCount

Returns the number of rows or columns in the control.

CountNumber := LV.GetCount(Mode)
Mode
When this parameter is omitted, it returns the total number of rows in the control. When this parameter is "S" or "Selected", the count includes only the selected/highlighted rows. When this parameter is "Col" or "Column", it returns the number of columns in the control. The return value will be retrieved instantly, because the control keeps track of these counts.

This method is often used in the top line of a Loop, in which case the method would get called only once (prior to the first iteration). For example:

Loop(LV.GetCount())
{
    RetrievedText := LV.GetText(A_Index)
    if InStr(RetrievedText, "some filter text")
        LV.Modify(A_Index, "Select")  ; Select each row whose first field contains the filter-text.
}

To retrieve the widths of a ListView's columns -- for uses such as saving them to an INI file to be remembered between sessions -- follow this example:

Gui.Opt("+LastFound")
Loop(LV.GetCount("Column"))
{
    ColWidth := SendMessage(4125, A_Index - 1, 0, "SysListView321")  ; 4125 is LVM_GETCOLUMNWIDTH.
    MsgBox("Column %A_Index%'s width is %ColWidth%.")
}

GetNext

Returns the row number of the next selected, checked, or focused row, otherwise zero.

RowNumber := LV.GetNext(StartingRowNumber, RowType)
StartingRowNumber
If omitted or less than 1, the search begins at the top of the list. Otherwise, the search begins at the row after StartingRowNumber.
RowType
If omitted, it searches for the next selected/highlighted row. Otherwise, specify "C" or "Checked" to find the next checked row; or "F" or "Focused" to find the focused row (there is never more than one focused row in the entire list, and sometimes there is none at all).

The following example reports all selected rows in the ListView:

RowNumber := 0  ; This causes the first loop iteration to start the search at the top of the list.
Loop
{
    RowNumber := LV.GetNext(RowNumber)  ; Resume the search at the row after that found by the previous iteration.
    if not RowNumber  ; The above returned zero, so there are no more selected rows.
        break
    Text := LV.GetText(RowNumber)
    MsgBox('The next selected row is #%RowNumber%, whose first field is "%Text%".')
}

An alternate method to find out if a particular row number is checked is the following:

Gui.Opt("+LastFound")
ItemState := SendMessage(4140, RowNumber - 1, 0xF000, "SysListView321")  ; 4140 is LVM_GETITEMSTATE. 0xF000 is LVIS_STATEIMAGEMASK.
IsChecked := (ItemState >> 12) - 1  ; This sets IsChecked to true if RowNumber is checked or false otherwise.

GetText

Retrieves the text at the specified row and column number.

RetrievedText := LV.GetText(RowNumber , ColumnNumber)
RowNumber
The number of the row, whose text to be retrieved. If this parameter is 0, the column header text is retrieved. RetrievedText has a maximum length of 8191.
ColumnNumber
The number of the column, where the specified row is located. If omitted, it defaults to 1 (the text in the first column). Column numbers seen by the script are not altered by any dragging and dropping of columns the user may have done. For example, the original first column is still number 1 even if the user drags it to the right of other columns.

Setting Icons

SetImageList

Sets or replaces the ImageList, and returns the ImageListID that was previously associated with this control (or 0 if none).

PrevImageListID := LV.SetImageList(ImageListID , IconType)
ImageListID
The number returned from a previous call to IL_Create.
IconType
If the second parameter is omitted, the type of icons in the ImageList is detected automatically as large or small. Otherwise, specify 0 for large icons, 1 for small icons, and 2 for state icons (state icons are not yet directly supported, but they could be used via SendMessage).

This method is normally called prior to adding any rows to the ListView.

A ListView may have up to two ImageLists: small-icon and/or large-icon. This is useful when the script allows the user to switch to and from the large-icon view. To add more than one ImageList to a ListView, call LV.SetImageList a second time, specifying the ImageListID of the second list. A ListView with both a large-icon and small-icon ImageList should ensure that both lists contain the icons in the same order. This is because the same ID number is used to reference both the large and small versions of a particular icon.

Although it is traditional for all viewing modes except Icon and Tile to show small icons, this can be overridden by passing a large-icon list to LV.SetImageList and specifying 1 (small-icon) for the second parameter. This also increases the height of each row in the ListView to fit the large icon.

Any such detached ImageList should normally be destroyed via IL_Destroy.

Events

The following events can be detected by calling OnEvent to register a callback function or method:

EventRaised when...
ClickThe control is clicked.
DoubleClickThe control is double-clicked.
ColClickA column header is clicked.
ContextMenuThe user right-clicks the control or presses the Apps key or Shift-F10 while the control has the keyboard focus.
FocusThe control gains the keyboard focus.
LoseFocusThe control loses the keyboard focus.
ItemCheckAn item is checked or unchecked.
ItemEditAn item's label is edited by the user.
ItemFocusThe focused item changes.
ItemSelectAn item is selected or deselected.

Additional (rarely-used) notifications can be detected by using OnNotify. These notifications are documented at MSDN. MSDN does not show the numeric value of each notification code; those can be found in the Windows SDK or by searching the Internet.

ImageList (the means by which icons are added to a ListView)

An Image-List is a group of identically sized icons stored in memory. Upon creation, each ImageList is empty. The script calls IL_Add repeatedly to add icons to the list, and each icon is assigned a sequential number starting at 1. This is the number to which the script refers to display a particular icon in a row or column header. Here is a working example that demonstrates how to put icons into a ListView's rows:

Gui := GuiCreate()  ; Create a GUI window.
LV := Gui.Add("ListView", "h200 w180", "Icon & Number|Description")  ; Create a ListView.
ImageListID := IL_Create(10)  ; Create an ImageList to hold 10 small icons.
LV.SetImageList(ImageListID)  ; Assign the above ImageList to the current ListView.
Loop(10)  ; Load the ImageList with a series of icons from the DLL.
    IL_Add(ImageListID, "shell32.dll", A_Index) 
Loop(10)  ; Add rows to the ListView (for demonstration purposes, one for each icon).
    LV.Add("Icon" . A_Index, A_Index, "n/a")
LV.ModifyCol("Hdr")  ; Auto-adjust the column widths.
Gui.Show()

IL_Create

Creates a new ImageList, initially empty, and returns the unique ID of the ImageList (or 0 upon failure).

UniqueID := IL_Create(InitialCount := 2, GrowCount := 5, LargeIcons := false)
InitialCount
The number of icons you expect to put into the list immediately (if omitted, it defaults to 2).
GrowCount
The number of icons by which the list will grow each time it exceeds the current list capacity (if omitted, it defaults to 5).
LargeIcons
If this parameter is true, the ImageList will contain large icons. If it is false, it will contain small icons (this is the default when omitted). Icons added to the list are scaled automatically to conform to the system's dimensions for small and large icons.

IL_Add

Adds an icon or picture to the specified ImageList, and returns the new icon's index (1 is the first icon, 2 is the second, and so on).

IconIndex := IL_Add(ImageListID, Filename , IconNumber := 1, ResizeNonIcon)
ImageListID
The ID of a ImageList created by IL_Create.
Filename
The name of an icon (.ICO), cursor (.CUR), animated cursor (.ANI) file (animated cursors will not actually be animated when displayed in a ListView), or a bitmap or icon handle like HBITMAP:%handle%. Other sources of icons include the following types of files: EXE, DLL, CPL, SCR, and other types that contain icon resources.
IconNumber
To use an icon group other than the first one in the file, specify its number for IconNumber. If IconNumber is negative, its absolute value is assumed to be the resource ID of an icon within an executable file. In the following example, the default icon from the second icon group would be used: IL_Add(ImageListID, "C:\My Application.exe", 2).
ResizeNonIcon

Non-icon images such as BMP, GIF and JPG may also be loaded. However, in this case the last two parameters should be specified to ensure correct behavior: IconNumber should be the mask/transparency color number (0xFFFFFF [the color white] might be best for most pictures); and ResizeNonIcon should be non-zero to cause the picture to be scaled to become a single icon, or zero to divide up the image into however many icons can fit into its actual width.

All operating systems support GIF, JPG, BMP, ICO, CUR, and ANI images. On Windows XP or later, additional image formats such as PNG, TIF, Exif, WMF, and EMF are supported. Operating systems older than XP can be given support by copying Microsoft's free GDI+ DLL into the AutoHotkey.exe folder (but in the case of a compiled script, copy the DLL into the script's folder). To download the DLL, search for the following phrase at www.microsoft.com: gdi redistributable

IL_Destroy

Deletes the specified ImageList, and returns 1 upon success and 0 upon failure.

Success := IL_Destroy(ImageListID)
ImageListID
The ID of a ImageList created by IL_Create.

Note: It is normally not necessary to destroy ImageLists because once attached to a ListView, they are destroyed automatically when the ListView or its parent window is destroyed. However, if the ListView shares ImageLists with other ListViews (by having 0x40 in its options), the script should explicitly destroy the ImageList after destroying all the ListViews that use it. Similarly, if the script replaces one of a ListView's old ImageLists with a new one, it should explicitly destroy the old one.

ListView Remarks

Gui.Submit has no effect on a ListView control.

After a column is sorted -- either by means of the user clicking its header or the script calling LV.ModifyCol(1, "Sort") -- any subsequently added rows will appear at the bottom of the list rather than obeying the sort order. The exception to this is the Sort and SortDesc styles, which move newly added rows into the correct positions.

To detect when the user has pressed Enter while a ListView has focus, use a default button (which can be hidden if desired). For example:

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

In addition to navigating from row to row with the keyboard, the user may also perform incremental search by typing the first few characters of an item in the first column. This causes the selection to jump to the nearest matching row.

Although any length of text can be stored in each field of a ListView, only the first 260 characters are displayed.

Although the maximum number of rows in a ListView is limited only by available system memory, row-adding performance can be greatly improved as described in the Count option.

A picture may be used as a background around a ListView (that is, to frame the ListView). To do this, create the picture control after the ListView and include 0x4000000 (which is WS_CLIPSIBLINGS) in the picture's Options.

A script may create more than one ListView per window.

It is best not to insert or delete columns directly with SendMessage. This is because the program maintains a collection of sorting preferences for each column, which would then get out of sync. Instead, use the built-in column methods.

To perform actions such as resizing, hiding, or changing the font of a ListView, see GuiControl object.

To extract text from external ListViews (those not owned by the script), use ControlGetList.

Related

TreeView, Other Control Types, GuiCreate, ContextMenu event, Gui object, GuiControl object, ListView styles table

Examples

; Select or de-select all rows by specifying 0 as the row number:
LV.Modify(0, "Select")   ; Select all.
LV.Modify(0, "-Select")  ; De-select all.
LV.Modify(0, "-Check")  ; Uncheck all the checkboxes.

; Auto-size all columns to fit their contents:
LV.ModifyCol()  ; There are no parameters in this mode.

 

; MAIN EXAMPLE
; The following is a working script that is more elaborate than the one near the top of this page.
; It displays the files in a folder chosen by the user, with each file assigned the icon associated with
; its type. The user can double-click a file, or right-click one or more files to display a context menu.

; Create a GUI window:
Gui := GuiCreate("+Resize")  ; Allow the user to maximize or drag-resize the window.

; Create some buttons:
B_1 := Gui.Add("Button", "Default", "Load a folder")
B_2 := Gui.Add("Button", "x+20", "Clear List")
B_3 := Gui.Add("Button", "x+20", "Switch View")

; Create the ListView and its columns:
LV := Gui.Add("ListView", "xm r20 w700", "Name|In Folder|Size (KB)|Type")
LV.ModifyCol(3, "Integer")  ; For sorting, indicate that the Size column is an integer.

; Apply ListView events:
LV.OnEvent("DoubleClick", "RunFile")
LV.OnEvent("ContextMenu", "ShowContextMenu")

; Create an ImageList so that the ListView can display some icons:
ImageListID1 := IL_Create(10)
ImageListID2 := IL_Create(10, 10, true)  ; A list of large icons to go with the small ones.

; Attach the ImageLists to the ListView so that it can later display the icons:
LV.SetImageList(ImageListID1)
LV.SetImageList(ImageListID2)

; Apply control events:
LV.OnEvent("DoubleClick", "RunFile")
LV.OnEvent("ContextMenu", "ShowContextMenu")
B_1.OnEvent("Click", Func("LoadFolder").bind(Gui, LV, ImageListID1, ImageListID2))
B_2.OnEvent("Click", Func("ClearList").bind(LV))
B_3.OnEvent("Click", Func("SwitchView").bind(LV))

; Apply window events:
Gui.OnEvent("Size", Func("Gui_Size").bind(LV))

; Display the window:
Gui.Show()

LoadFolder(Gui, LV, ImageListID1, ImageListID2)
{
  static IconArray := []
  Gui.Opt("+OwnDialogs")  ; Forces user to dismiss the following dialog before using main window.
  Folder := DirSelect(, 3, "Select a folder to read:")
  if not Folder  ; The user canceled the dialog.
    return

  ; Check if the last character of the folder name is a backslash, which happens for root
  ; directories such as C:\. If it is, remove it to prevent a double-backslash later on.
  if SubStr(Folder, -1, 1) = "\"
    Folder := SubStr(Folder, 1, -1)  ; Remove the trailing backslash.

  ; Calculate buffer size required for SHFILEINFO structure.
  sfi_size := A_PtrSize + 688
  VarSetCapacity(sfi, sfi_size)

  ; Gather a list of file names from the selected folder and append them to the ListView:
  LV.Opt("-Redraw")  ; Improve performance by disabling redrawing during load.
  LoopFiles("%Folder%\*.*")
  {
    FileName := A_LoopFilePath  ; Must save it to a writable variable for use below.

    ; Build a unique extension ID to avoid characters that are illegal in variable names,
    ; such as dashes. This unique ID method also performs better because finding an item
    ; in the array does not require search-loop.
    SplitPath(FileName,,, FileExt)  ; Get the file's extension.
    if FileExt ~= "^(EXE|ICO|ANI|CUR)$"
    {
      ExtID := FileExt  ; Special ID as a placeholder.
      IconNumber := 0  ; Flag it as not found so that these types can each have a unique icon.
    }
    else  ; Some other extension/file-type, so calculate its unique ID.
    {
      ExtID := 0  ; Initialize to handle extensions that are shorter than others.
      Loop(7)     ; Limit the extension to 7 characters so that it fits in a 64-bit value.
      {
        ExtChar := SubStr(FileExt, A_Index, 1)
        if not ExtChar  ; No more characters.
          break
        ; Derive a Unique ID by assigning a different bit position to each character:
        ExtID := ExtID | (Ord(ExtChar) << (8 * (A_Index - 1)))
      }
      ; Check if this file extension already has an icon in the ImageLists. If it does,
      ; several calls can be avoided and loading performance is greatly improved,
      ; especially for a folder containing hundreds of files:
      IconNumber := IconArray[ExtID]
    }
    if not IconNumber  ; There is not yet any icon for this extension, so load it.
    {
      ; Get the high-quality small-icon associated with this file extension:
      if not DllCall("Shell32\SHGetFileInfoW", "str", FileName
      , "uint", 0, "ptr", &sfi;, "uint", sfi_size, "uint", 0x101)  ; 0x101 is SHGFI_ICON+SHGFI_SMALLICON
        IconNumber := 9999999  ; Set it out of bounds to display a blank icon.
      else ; Icon successfully loaded.
      {
        ; Extract the hIcon member from the structure:
        hIcon := NumGet(sfi, 0)
        ; Add the HICON directly to the small-icon and large-icon lists.
        ; Below uses +1 to convert the returned index from zero-based to one-based:
        IconNumber := DllCall("ImageList_ReplaceIcon", "ptr", ImageListID1, "int", -1, "ptr", hIcon) + 1
        DllCall("ImageList_ReplaceIcon", "ptr", ImageListID2, "int", -1, "ptr", hIcon)
        ; Now that it's been copied into the ImageLists, the original should be destroyed:
        DllCall("DestroyIcon", "ptr", hIcon)
        ; Cache the icon to save memory and improve loading performance:
        IconArray[ExtID] := IconNumber
      }
    }

    ; Create the new row in the ListView and assign it the icon number determined above:
    LV.Add("Icon" . IconNumber, A_LoopFileName, A_LoopFileDir, A_LoopFileSizeKB, FileExt)
  }
  LV.Opt("+Redraw")  ; Re-enable redrawing (it was disabled above).
  LV.ModifyCol()  ; Auto-size each column to fit its contents.
  LV.ModifyCol(3, 60) ; Make the Size column at little wider to reveal its header.
}

ClearList(LV)
{
  LV.Delete()  ; Clear the ListView, but keep icon cache intact for simplicity.
}

SwitchView(LV)
{
  static IconView
  if not IconView
    LV.Opt("+Icon")         ; Switch to icon view.
  else
    LV.Opt("+Report")       ; Switch back to details view.
  IconView := not IconView  ; Invert in preparation for next time.
}

RunFile(LV, RowNumber, Verb := "")
{
  FileName := LV.GetText(RowNumber, 1) ; Get the text of the first field.
  FileDir := LV.GetText(RowNumber, 2)  ; Get the text of the second field.
  FilePath := FileDir "\" FileName
  Verb .= Verb ? " " : ""
  try
    Run(Verb FilePath)
  catch Error
    MsgBox(Error.Extra "`n" FilePath)
}

ShowContextMenu(LV, Item, IsRightClick, X, Y)  ; In response to right-click or Apps key.
{
  ; Create function references for the menu events:
  ContextOpen := Func("ContextOpen").bind(LV, Item)
  ContextProperties := Func("ContextProperties").bind(LV, Item)
  ContextClearRows := Func("ContextClearRows").bind(LV)
 
  ; Create or update the popup menu to be used as the context menu:
  Menu("MyContextMenu", "Add", "Open", ContextOpen)
  Menu("MyContextMenu", "Add", "Properties", ContextProperties)
  Menu("MyContextMenu", "Add", "Clear from ListView", ContextClearRows)
  Menu("MyContextMenu", "Default", "Open")  ; Make "Open" a bold font to indicate that double-click does the same thing.
  ; Show the menu at the provided coordinates, X and Y.  These should be used
  ; because they provide correct coordinates even if the user pressed the Apps key:
  Menu("MyContextMenu", "Show", X, Y)
}

ContextOpen(LV, RowNumber)  ; The user selected "Open" in the context menu.
{
  RunFile(LV, RowNumber)
}

ContextProperties(LV, RowNumber)  ; The user selected "Properties" in the context menu.
{
  RunFile(LV, RowNumber, "properties")
}

ContextClearRows(LV)  ; The user selected "Clear" in the context menu.
{
  RowNumber := 0  ; This causes the first iteration to start the search at the top.
  Loop
  {
    ; Since deleting a row reduces the RowNumber of all other rows beneath it,
    ; subtract 1 so that the search includes the same row number that was previously
    ; found (in case adjacent rows are selected):
    RowNumber := LV.GetNext(RowNumber - 1)
    if not RowNumber  ; The above returned zero, so there are no more selected rows.
      break
    LV.Delete(RowNumber)  ; Clear the row from the ListView.
  }
}

Gui_Size(LV, Gui, MinMax, Width, Height)  ; Expand/Shrink ListView in response to the user's resizing.
{
  if MinMax = -1  ; The window has been minimized. No action needed.
    return
  ; Otherwise, the window has been resized or maximized. Resize the ListView to match.
  LV.Move("W" Width-20 " H" Height-40)
}