Dynamic variadic CSV data array to ListView

Helpful script writing tricks and HowTo's
DrReflex

Dynamic variadic CSV data array to ListView

15 Feb 2017, 02:58

I have had several projects that required passing data from an array to a function as a variable length parameter list.

My best example is: The need to dynamically populate a ListView with elements from a 2 dimensional comma separated variable (CSV) file.

Looking at the AHK ListView documentation this does not appear to be possible. My meager attempts to program around or through this apparent limitation failed. The solution was in a post in the Archived Forums (https://autohotkey.com/board/topic/9730 ... omsearch=1). There are several other posts that might get you to the solution.

It turns out that LV_Add() is a "variadic" function. Although LV_Add() is documented as LV_Add([Options, Field1, Field2, ...]) the correct documentation is LV_Add([Options, Field*]) where the asterisk after Field denotes Field to be a variadic parameter and LV_Add() to be a variadic function. In this case Field becomes a variable array of sequential ListView row entries (e.g Field1, Field2, ...). It appears that LV_Insert and LV_Modify are also variadic functions.

In the above post, Jackie Sztuk-Blackholyman provided a simple script to show how to use a variadic parameter to add rows of elements to a ListView. (Thanks Jackie.) I have slightly modified that script to make the code sequence easier to read while adding an extra row and an extra column.

Code: Select all

Gui,Add,ListView,,ColTitle1|ColTitle2|ColTitle3|ColTitle4

args := {}
;BUILD AND ADD DATA ROW 1
args[1] := "A"
args[2] := "B"
args[3] := "C"
args[4] := "D"
LV_Add("",args*)

args := {}
;BUILD AND ADD DATA ROW 2
args[1] := "E"
args[2] := "F"
args[3] := "G"
args[4] := "H"
LV_Add("",args*)
args=

Gui,Show
Return

Esc::
GuiEscape:
GuiClose:
ExitApp
In the following code:
1. I pull the elements into an Array[row,col]=element.
2. I use 1 Loop to build the Column/TitleString (CTString) from the first row of elements. CTString is used by GUI,Add,Listview,,CTString to build ListView.
3. I use 2 nested Loops to read elements by row and column from the Array[row,col]=element into the variadic parameter args* and then use LV_Add("",args*) to add the list of elements one line at a time into ListView.

Code: Select all

Array := {}
Array[1,1] := "ColTitle1"
Array[1,2] := "ColTitle2"
Array[1,3] := "ColTitle3"
Array[1,4] := "ColTitle4"
Array[2,1] := "A"
Array[2,2] := "B"
Array[2,3] := "C"
Array[2,4] := "D"
Array[3,1] := "E"
Array[3,2] := "F"
Array[3,3] := "G"
Array[3,4] := "H"

MaxRow := 3
MaxCol  := 4

;BUILD CTSring (COLUMN TITLE STRING) FROM Array[1,..]
CTString := ""
Loop, %MaxCol%
{
	ColIndex := A_Index
	CTString .= Array[1,ColIndex] . "|"
}
CTString := SubStr(CTString, 1, (StrLen(CTString)-1))   		; REMOVE LAST "|"
;USE CTSString TO BUILD ListView
Gui,Add,ListView,,%CTString%

;BUILD args[ColIndex]  (i.e. args*) FROM Array[Row2..,Col1..]
MaxRow := MaxRow -1  								;REDUCE MaxRow BY 1 TO ACCOUNT FOR COLTITLE ROW
Loop, %MaxRow% 
{
	args := {}
	RowIndex := A_Index + 1  							;INCREASE RowIndex BY 1 TO ACCOUNT FOR COLTITLE ROW
	Loop, %MaxCol%
	{
		ColIndex := A_Index
		args[ColIndex] := Array[RowIndex,ColIndex]
	}
	LV_Add("",args*)
	args = 
}
Gui,Show
Return

Esc::
GuiEscape:
GuiClose:
ExitApp
In the last interation, I use a CSV DataString rather than a CSV datafile so the code is self contained to build the Array[row,col] = element. A the same time I gather the RowMax and ColMax values that will be used to populate the ListView.

Code: Select all

DataString = 
(
ColTitle1, ColTitle2, ColTitle3, ColTitle4
A,B,C,D
E,F,G,H
)

;BUILD ARRAY[Row,Column] FROM DataString
Array     := {}
MaxRow := 0
MaxCol  := 0
Loop, parse, DataString, `n  					;PARSE DataString AT NEW LINES TO GET RowString
{
	RowIndex := A_Index
	RowString := A_LoopField
	Loop, parse, RowString, CSV 				;PARSE RowString AT COMMAS TO GET Elements
	{
		ColIndex := A_Index
		Element  := A_LoopField
		Array[RowIndex,ColIndex] := Element 	;BUILD Array[row,col] FROM Elements
	}
	If (ColIndex > MaxCol)					;IF ColIndex > MaxCol THEN UPDATE MaxCol
		MaxCol := ColIndex
}
MaxRow := RowIndex

;BUILD CTSring (COLUMN TITLE STRING) FROM Array[1,..]
CTString := ""
Loop, %MaxCol%
{
	ColIndex := A_Index
	CTString .= Array[1,ColIndex] . "|"
}
CTString := SubStr(CTString, 1, (StrLen(CTString)-1))	;REMOVE LAST "|"

;USE CTSString TO BUILD ListView
Gui,Add,ListView,,%CTString%
CTString =

;BUILD args[ColIndex] FROM Array[Row2..,Col1..]
MaxRow := MaxRow -1  						;REDUCE MaxRow BY 1 TO ACCOUNT FOR COLTITLE ROW
Loop, %MaxRow% 
{
	args := {}
	RowIndex := A_Index + 1  					; INCREASE RowIndex BY 1 TO ACCOUNT FOR COLTITLE ROW
	Loop, %MaxCol%
	{
		ColIndex := A_Index
		args[ColIndex] := Array[RowIndex,ColIndex]
	}
	;ADD ROW TO ListView USING args*
	LV_Add("",args*)
}
args =
Array =
Gui,Show
Return

Esc::
GuiEscape:
GuiClose:
ExitApp
The code above can easily be changed to input the elements from a CSV file using the "Loop, Read, File" function.

The documentation regarding variadic functions is remarkably sparse given the power of the feature. What documentation there is can be found under Variadic Functions and Variadic Function Calls (https://autohotkey.com/docs/Functions.htm#Variadic). It consists of 2 paragraphs, 10 Notes, and 1 crude example.
If the last parameter in a function definition is followed by an asterisk (*) the function is considered to be variadic and may receive a variable number of parameters.

I hope you find these examples helpful.

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 3 guests