@Helgef: Thanks this seems to be working quite well.
You mentioned that this is not advised, are you referring to that it is better to not to change the standard ahk gui object, but create a new class, or do you recommend to use a subclass for the selection.
Here is my current code.
I also added a cool feature that lets you loop over the listview, then it returns the rows one by one and the content of the rows.
It is working already great, but it is missing a lot of functionalities like looping over the selection, creating groups,..
lv.table => get or set the content of the listview as a table (tab and linefeeds)
lv.selection.table => returns the selection of the listview as a table (tab and linefeeds)
lv.selection.rows => returns the selection of the listview as a table (tab and linefeeds)
lv.rows => returns the number of rows
lv.colunms=> returns the number of colunms
lv.Checked(row) => returns the checked status
lv.headerArray := ["test","test2"] => quickly set or get the headers
lv.SetExplorerTheme() => change the theme
lv.GetObject(Options := "", aHeaders:="",includeIcons:=0) => retrieve the content as an array of objects (aHeaders can be used to change the properties names, as no spaces are allowed. set IncludeIcons to 1 to also include the iconnumber and checked status.
lv.GetObject(aRows, aProps := "") => set the content of the listview as an object
for row, rowcontent in lv => loop over the content
I really like this one
lv.Filter(SearchString, colNumber:="") => quickly filter the list
Code: Select all
#SingleInstance force
#Include "C:\Users\Dimitri\Documents\Autohotkey v2\Projects\Gui\V2GuiCreator\Lib\ObjectGui.ah2"
class ListView_Ext extends Gui.ListView { ; Technically no need to extend classes unless
Static __New() { ; you are attaching new base on control creation.
For prop in this.Prototype.OwnProps()
super.Prototype.%prop% := this.Prototype.%prop%
super.Prototype.DefineProp("hwndHeader", { get: (this) => (hwndHeader := SendMessage(LVM_GETHEADER := 0x101F, 0, 0, , "ahk_id " . This.hwnd), (hwndHeader = "FAIL" ? False : hwndHeader)) })
super.Prototype.DefineProp("columns", { get: (this) => (this.GetCount("Col"))})
super.Prototype.DefineProp("rows", { get: (this) => (this.GetCount(""))})
super.Prototype.DefineProp("table", { get: (this) => (this.GetTable(this)), Set: (this, value) => (this.SetTable(value))})
super.Prototype.DefineProp("headerArray", { get: (this) => (this.GetHeaderArray()), Set: (this, value) => (this.SetHeaderArray(value))})
super.Prototype.defineprop("selection", { get: sel_get } )
sel_get(this) {
sel := {}
sel.DefineProp("table", { get: (sel) => (this.GetTable("selected")) })
sel.DefineProp("rows", { get: (sel) => (this.GetCount("S")) })
sel.DefineProp("row", { get: (sel) => (this.GetNext()) })
return sel
}
}
Checked(rowNumber) => (SendMessage(VM_GETITEMSTATE := 4140, rowNumber - 1, 0xF000, , "ahk_id " this.hwnd) >> 12) - 1 ; VM_GETITEMSTATE = 4140 / LVIS_STATEIMAGEMASK = 0xF000
GetIconIndex(rowNumber, col := 1) { ; from "just me" LV_EX ; Link: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=69262&p=298308#p299057
LVITEM := Buffer((A_PtrSize = 8) ? 56 : 40, 0) ; create variable/structure
NumPut("UInt", 0x2, "Int", rowNumber - 1, "Int", col - 1, LVITEM.ptr, 0) ; LVIF_IMAGE := 0x2 / iItem (row) / column num
NumPut("Int", 0, LVITEM.ptr, (A_PtrSize = 8) ? 36 : 28) ; iImage
SendMessage(StrLen(Chr(0xFFFF)) ? 0x104B : 0x1005, 0, LVITEM.ptr, , "ahk_id " this.hwnd) ; LVM_GETITEMA/W := 0x1005 / 0x104B
return NumGet(LVITEM.ptr, (A_PtrSize = 8) ? 36 : 28, "Int") + 1 ;iImage
}
GetColWidth(colNumber) => (SendMessage(LVM_GETCOLUMNWIDTH := 0x101D, colNumber - 1, 0, this.hwnd))
GetTable(Options := "") {
Result := ""
Loop this.GetCount("Column") {
Result .= (A_Index = 1) ? "" : "`t"
Result .= this.GetText(0, A_Index)
}
Result .= "`n" ListViewGetContent(Options, this)
return Result
}
SetTable(Table,headerIncl:=true) {
this.Opt("-Redraw")
; Remove content
this.Delete()
if (headerIncl){
; Clear all columns
Loop this.GetCount("Column") {
this.DeleteCol(1)
}
}
loop parse Table, "`n", "`r" {
aRow := StrSplit(A_LoopField, "`t")
if (A_index = 1 and headerIncl) {
for Index, Header in aRow {
this.InsertCol(Index, , Header)
}
} else {
this.Add("", aRow*)
}
}
this.ModifyCol
this.Opt("+Redraw")
return
}
GetHeaderArray() {
Result := ""
Loop This.GetCount("Column") {
Result .= (A_Index = 1) ? "" : "`t"
Result .= This.GetText(0, A_Index)
}
return Result
}
SetHeaderArray(aHeaders) {
this.Opt("-Redraw")
for index, HeaderText in aHeaders {
this.ModifyCol(Index,,HeaderText)
}
this.Opt("+Redraw")
}
GetObject(Options := "", aHeaders:="",includeIcons:=0) {
aResult := Array()
if (aHeaders=""){
aHeaders := Array()
Loop this.GetCount("Column") {
aHeaders.Push(StrReplace(this.GetText(0, A_Index), " ", "_"))
}
}
Loop parse, ListViewGetContent(Options, lv), "`n", "`r" {
oRow := Object()
Loop parse, A_LoopField, "`t" {
; oRow.DefineProp(aHeaders[A_index], { get: ((result, this) => (result)).bind(A_LoopField) })
propertyName := aHeaders[A_index]
oRow.%propertyName% := A_LoopField
}
aResult.Push(oRow)
}
if (includeIcons){
rowNumber := 0
loop aResult.Length {
if (InStr(Options,"Selected")){
rowNumber := LV.GetNext(rowNumber)
} else{
rowNumber++
}
aResult[A_Index].IconIndex := this.GetIconIndex(rowNumber)
aResult[A_Index].Checked := this.Checked(rowNumber)
}
}
return aResult
}
; aRows := An array of objects with the data
; aProps := An array of the properties:
; - an array of strings => uses the strings as properties
; - or an array of objects with properties name and text => uses text as header and name as properties
; - empty => uses all the properties of the first object of aRows
SetObject(aRows, aProps := "") {
this.Opt("-Redraw")
this.Delete()
Loop this.GetCount("Column") {
this.DeleteCol(1)
}
aHeaders := Type(aProps)="Array" ? aProps : aRows[1].OwnProps()
if (aProps = ""){
aHeaders := Array()
For PropName, PropValue in aRows[1].OwnProps() {
if (PropName="IconNumber" or PropName="Checked"){
continue
}
this.InsertCol(A_Index, , PropName)
aHeaders.Push(PropName)
}
}
else if (aHeaders[1].HasProp("name")) { ; an array of objects with the name and text property
aHeaders2 := Array()
For index, Property in aHeaders {
this.InsertCol(A_Index, , Property.HasProp("text")? Property.text : Property.name)
aHeaders2.Push(Property.name)
}
aHeaders := aHeaders2
} else { ; an array of strings
For index, Property in aHeaders {
this.InsertCol(A_Index, , Property)
}
}
for Index, oRow in aRows
{
aRow := Array()
For index, Property in aHeaders {
aRow.Push(oRow.%Property%)
}
this.Add((oRow.HasProp("Iconnumber") ? "Icon" oRow.IconNumber : "") . ((oRow.HasProp("Checked") and oRow.Checked=1) ? " Check" : ""), aRow*)
}
this.ModifyCol
this.Opt("+Redraw")
return
}
__Enum(NumberOfVars){
; Lexical scope bound.
; Serves as an index counter.
i := 1
aContent := StrSplit(ListViewGetContent(, this),"`n","`r")
imax := aContent.Length
EnumerateElements(&element){
; Stop enumerating when there are no more elements
; left in the array.
if (i > imax)
return false
; Assign to the 'for' loop and decrement.
element := aContent[i]
i += 1
; Continue enumeration.
return true
}
EnumerateIndexWithElements(&index, &element){
if (i > imax)
return false
index := i
element := aContent[i]
i += 1
return true
}
; Decide, based on the 'for' loop type, which
; enumeration function is the appropriate one.
return (NumberOfVars = 1)
? EnumerateElements
: EnumerateIndexWithElements
}
Filter(SearchString, colNumber:=""){
TableNew := ""
this.Opt("-Redraw")
Index := 0
Loop parse, this.table, "`n","`r"
{
Haystack := colNumber = "" ? A_LoopField : StrSplit(A_LoopField,"`t")[colNumber]
if (A_index!=1) and !Instr(A_LoopField,SearchString){
This.Delete(Index)
Index--
}
Index++
}
this.Opt("+Redraw")
}
SetExplorerTheme(){
if (DllCall("kernel32\GetVersion", "uchar") > 5) {
VarSetStrCapacity(&ClassName, 1024)
if (DllCall("user32\GetClassName", "ptr", this.hwnd, "str", ClassName, "int", 512, "int")) {
return !DllCall("uxtheme\SetWindowTheme", "ptr", this.hwnd, "str", "Explorer", "ptr", 0)
}
}
return false
}
}
; Create the window:
MyGui := Gui()
; Create the ListView with two columns, Name and Size:
LV := MyGui.Add("ListView", "checked r20 w700", ["Name","Size"])
ImageListID := IL_Create(10) ; Create an ImageList to hold 10 small icons.
LV.SetImageList(ImageListID) ; Assign the above ImageList to the current ListView.
Loop 20 ; Load the ImageList with a series of icons from the DLL.
IL_Add(ImageListID, "shell32.dll", A_Index)
; Gather a list of file names from a folder and put them into the ListView:
Loop Files, A_MyDocuments "\*.*"
LV.Add("Icon" . A_Index, A_LoopFileName, A_LoopFileSizeKB)
LV.ModifyCol ; Auto-size each column to fit its contents.
; Display the window:
MyGui.Show
aRows := [{ test: "eeee", name: "www", iconnumber:2, checked:1 }, { test: "ddd", name: "eeeee" }]
lv.SetObject(aRows,[{ name: "test", text: "tast" },{ name: "name", text: "Name" }])
ObjectGui(LV.GetObject(, , 1))
ObjectGui(aRows)
; lv.Filter("a")
; for index, rowvalue in lv{
; MsgBox(index " " rowvalue)
; }
sleep 5000
; lv.headerArray := ["test","test2"]
MsgBox(lv.selection.table)
MsgBox(lv.rows)
LV.SetExplorerTheme()
table := "test`tvalue`ndddddddddddd`tG`ntest2`t4564"
; LV.table := table
aRows := [{test:"ssfsddf",name:"sdfsd"},{ test: "ffff", name: "ssssss" }]
lv.SetObject(aRows)