[v2] ListView Filter Bar

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
kczx3
Posts: 1648
Joined: 06 Oct 2015, 21:39

[v2] ListView Filter Bar

11 Mar 2019, 11:22

I am trying to make use of the HDS_FILTERBAR style on Header Controls and am having trouble accessing what is entered in the filter field.

The HDN_FILTERCHANGE notification is sent via WM_NOTIFY. See here for details on the notification - https://docs.microsoft.com/en-us/windows/desktop/controls/hdn-filterchange

I can't seem to get things quite right with HDM_GETITEM to properly retrieve the filter text. Are my offsets correct? Am I looking for the string in the proper place?

EDIT: This AutoIt post could be of help but it sure seems like I'm doing the same thing - https://www.autoitscript.com/forum/topic/190357-filter-in-listview-header/

Code: Select all

main := GuiCreate()

lv := main.addListView("w400 h400", "col1|col2|col3")

; get hwnd of Header control
lvHeader := SendMessage(0x101F, 0, 0, , "ahk_id " . lv.hwnd)

; toggle the HDS_FILTERBAR style
WinSetStyle("^0x100", "ahk_id " lvHeader)

; populate ListView
Loop(100) {
    lv.Add("", "Col 1 - Row " A_Index, "Col 2 - Row " A_Index, "Col 3 - Row " A_Index)
}

LV.ModifyCol()

lv.OnNotify(HDN_FILTERBTNCLICK := -313, "LV_OnNotify")

main.show()
return

LV_OnNotify(ctrl, lParam) {
    static HDM_GETITEM := 0x120B
    static NMHDR_Size := A_PtrSize * 3
    static HDItemSize := (4 * 6) + (A_PtrSize * 6)
    static typeOffset := 44
    static pvFilterOffset := typeOffset + 4
    
    hwnd := NumGet(lParam, 0, "Ptr")
    col := NumGet(lParam, NMHDR_Size, "Int")
    
    ; String buffer for HDTEXTFILTER struct
    VarSetCapacity(string, 64 * 2, 0)
    
    ; HDFILTER struct
    VarSetCapacity(HDTEXTFILTER, A_PtrSize + 4)
    NumPut(&string, HDTEXTFILTER, 0, "UPtr") ; add pointer to string buffer variable
    NumPut(64*2, HDTEXTFILTER, 8, "Int") ; buffer size
    
    ; HDITEM struct
    VarSetCapacity(HDItem, HDItemSize)
    NumPut(0x100, HDItem, 0, "UInt") ; Set the Mask to HDI_FILTER := 0x100
    NumPut(0x0, HDItem, typeOffset, "UInt") ; Set the Type to HDFT_ISSTRING := 0x0
    NumPut(&HDTEXTFILTER, HDItem, pvFilterOffset, "Ptr") ; Add pointer to HDTEXTFILTER struct
    
    ; Send message to get the item
    SendMessage(HDM_GETITEM, col, &HDItem, , "ahk_id " hwnd)
    
    ; Assume that the filter text should then be contained in the variable 'string', no?
    ToolTip("Column index - " col "`n`nfilter = " string)
}
User avatar
kczx3
Posts: 1648
Joined: 06 Oct 2015, 21:39

Re: [v2] ListView Filter Bar

11 Mar 2019, 12:02

Ended up using _Struct to verify the offsets... which were wrong, of course.

Type offset is 48 and pvFilter offset is 56.

Additionally, I had the size of HDTEXTFILTER wrong.

With those updates, I appear that I am getting the first letter of the text in the filter. But not all of the text. I'm puzzled.

Code: Select all

#Warn

main := GuiCreate()

lv := main.addListView("w400 h400", "col1|col2|col3")

; get hwnd of Header control
lvHeader := SendMessage(0x101F, 0, 0, , "ahk_id " . lv.hwnd)

; toggle the HDS_FILTERBAR style
WinSetStyle("^0x100", "ahk_id " lvHeader)

; populate ListView
Loop(100) {
    lv.Add("", "Col 1 - Row " A_Index, "Col 2 - Row " A_Index, "Col 3 - Row " A_Index)
}

LV.ModifyCol()

lv.OnNotify(HDN_FILTERCHANGE := -312, "LV_OnNotify")

main.show()
return

LV_OnNotify(ctrl, lParam) {
    static HDM_GETITEM := 0x120B
    static NMHDR_Size := A_PtrSize * 3
    static HDItemSize := (4 * 6) + (A_PtrSize * 6)
    static typeOffset := 48
    static pvFilterOffset := 56
    
    hwnd := NumGet(lParam, 0, "Ptr")
    col := NumGet(lParam, NMHDR_Size, "Int")
    
    ; String buffer for HDTEXTFILTER struct
    VarSetCapacity(string, 64 * 2, 0)
    
    ; HDFILTER struct
    VarSetCapacity(HDTEXTFILTER, A_PtrSize * 2)
    NumPut(&string, HDTEXTFILTER, 0, "UPtr") ; add pointer to string buffer variable
    NumPut(64*2, HDTEXTFILTER, 8, "Int") ; buffer size
    
    ; HDITEM struct
    VarSetCapacity(HDItem, HDItemSize)
    NumPut(0x100, HDItem, 0, "UInt") ; Set the Mask to HDI_FILTER := 0x100
    NumPut(0x0, HDItem, typeOffset, "UInt") ; Set the Type to HDFT_ISSTRING := 0x0
    NumPut(&HDTEXTFILTER, HDItem, pvFilterOffset, "Ptr") ; Add pointer to HDTEXTFILTER struct
    
    ; Send message to get the item
    SendMessage(HDM_GETITEM, col, &HDItem, , "ahk_id " hwnd)
    
    ; Assume that the filter text should then be contained in the variable 'string', no?
    ToolTip("Column index - " col "`n`nfilter = " string)
}
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: [v2] ListView Filter Bar

11 Mar 2019, 12:05

That could hint at an encoding problem - e.g. when ANSI is expected but UTF-16 text is send
Recommends AHK Studio
User avatar
kczx3
Posts: 1648
Joined: 06 Oct 2015, 21:39

Re: [v2] ListView Filter Bar

11 Mar 2019, 12:14

nnnik wrote:
11 Mar 2019, 12:05
That could hint at an encoding problem - e.g. when ANSI is expected but UTF-16 text is send
Ah ha! That did the trick. Can't just reference the buffer that was passed in the HDTEXTFILTER. You have to get it via StrGet and specify UTF-16 encoding.
If you have any other suggestions, then please let me know. Many thanks as usual.

Final working code:

Code: Select all

#Warn

main := GuiCreate()

lv := main.addListView("w400 h400", "col1|col2|col3")

; get hwnd of Header control
lvHeader := SendMessage(0x101F, 0, 0, , "ahk_id " . lv.hwnd)

; toggle the HDS_FILTERBAR style
WinSetStyle("^0x100", "ahk_id " lvHeader)

; populate ListView
Loop(100) {
    lv.Add("", "Col 1 - Row " A_Index, "Col 2 - Row " A_Index, "Col 3 - Row " A_Index)
}

LV.ModifyCol()

lv.OnNotify(HDN_FILTERCHANGE := -312, "LV_OnNotify")

main.show()
return

LV_OnNotify(ctrl, lParam) {
    static HDM_GETITEM := 0x120B
    static NMHDR_Size := A_PtrSize * 3
    static HDItemSize := (4 * 6) + (A_PtrSize * 6)
    static typeOffset := 48
    static pvFilterOffset := 56
    filterTextLength := 64 * 2
    
    hwnd := NumGet(lParam, 0, "Ptr")
    col := NumGet(lParam, NMHDR_Size, "Int")
    
    ; String buffer for HDTEXTFILTER struct
    VarSetCapacity(filterText, filterTextLength)
    
    ; HDFILTER struct
    VarSetCapacity(HDTEXTFILTER, A_PtrSize * 2)
    NumPut(&filterText, HDTEXTFILTER, 0, "UPtr") ; add pointer to string buffer variable
    NumPut(filterTextLength, HDTEXTFILTER, 8, "Int") ; buffer size
    
    ; HDITEM struct
    VarSetCapacity(HDItem, HDItemSize)
    NumPut(0x100, HDItem, 0, "UInt") ; Set the Mask to HDI_FILTER := 0x100
    NumPut(0x0, HDItem, typeOffset, "UInt") ; Set the Type to HDFT_ISSTRING := 0x0
    NumPut(&HDTEXTFILTER, HDItem, pvFilterOffset, "Ptr") ; Add pointer to HDTEXTFILTER struct
    
    ; Send message to get the item
    SendMessage(HDM_GETITEM, col, &HDItem, , "ahk_id " hwnd)
    
    ; Assume that the filter text should then be contained in the variable 'string', no?
    ToolTip("Column index - " col "`n`nfilter = " StrGet(&filterText, filterTextLength, "UTF-16"))
}
User avatar
emmanuel d
Posts: 90
Joined: 17 Nov 2013, 04:45

Re: [v2] ListView Filter Bar

22 Sep 2023, 11:33

This is no longer compatible, i tried to fix it like

Code: Select all

#Requires Autohotkey v2
#Warn
; Msgbox("Offset pvfilters:" (7*4)+(A_PtrSize*3))
; Msgbox("SizeItem:" (4*7)+(A_PtrSize*6)
; . "`n" (4 * 6) + (A_PtrSize * 6))
main := Gui()
lv := main.addListView("w400 h400", ["col1","col2","col3"])
lvHeader := SendMessage(0x101F,0,0,lv)            ; get hwnd of Header control
WinSetStyle("^0x100",lvHeader)                    ; toggle the HDS_FILTERBAR style
Loop(100)                                         ; populate ListView
    lv.Add("", "Col 1 - Row " A_Index, "Col 2 - Row " A_Index, "Col 3 - Row " A_Index)
LV.ModifyCol()
lv.OnNotify(HDN_FILTERCHANGE := -312, LV_OnNotify)
main.show()
return
LV_OnNotify(ctrl, lParam) {
    static HDM_GETITEM := 0x120B
    static NMHDR_Size := A_PtrSize * 3
    static HDItemSize := 76 ; (4 * 6) + (A_PtrSize * 6)
    static typeOffset := 48
    ; static pvFilterOffset := 56
    static pvFilterOffset := typeOffset+4 ; 52
    filterTextLength := 64 * 2
    hwnd := NumGet(lParam, 0, "Ptr")
    col := NumGet(lParam, NMHDR_Size, "Int")
    ; String buffer for HDTEXTFILTER struct
    ; VarSetCapacity(filterText, filterTextLength)
    VarSetStrCapacity(&filterText, filterTextLength)
    ; filterText := Buffer(filterTextLength)
    ; HDFILTER struct
    ; VarSetCapacity(HDTEXTFILTER, A_PtrSize * 2)
    HDTEXTFILTER := Buffer(A_PtrSize * 2)
    ; NumPut(&filterText, HDTEXTFILTER, 0, "UPtr") ; add pointer to string buffer variable
    ; NumPut(filterTextLength, HDTEXTFILTER, 8, "Int") ; buffer size
    NumPut("Ptr",StrPtr(filterText), HDTEXTFILTER, 0) ; add pointer to string buffer variable
    NumPut("Int",filterTextLength, HDTEXTFILTER, 8) ; buffer size
    ; HDITEM struct
    ; VarSetCapacity(HDItem, HDItemSize)
    HDItem := Buffer(HDItemSize,0)
    ; NumPut(0x100, HDItem, 0, "UInt") ; Set the Mask to HDI_FILTER := 0x100
    ; NumPut(0x0, HDItem, typeOffset, "UInt") ; Set the Type to HDFT_ISSTRING := 0x0
    ; NumPut(&HDTEXTFILTER, HDItem, pvFilterOffset, "Ptr") ; Add pointer to HDTEXTFILTER struct
    NumPut("UInt",0x100       , HDItem, 0) ; Set the Mask to HDI_FILTER := 0x100
    NumPut("UInt",0x0         , HDItem, typeOffset) ; Set the Type to HDFT_ISSTRING := 0x0
    NumPut("Ptr" ,HDTEXTFILTER.Ptr, HDItem, pvFilterOffset) ; Add pointer to HDTEXTFILTER struct


    ; NumPut("UInt",0x0          ; Set the Type to HDFT_ISSTRING := 0x0
          ; ,"Ptr" ,StrPtr(filterText) ; add pointer to string buffer variable
          ; ,"Int" ,filterTextLength, HDItem,typeOffset) ; buffer size
   
    ; Send message to get the item
    ; SendMessage(HDM_GETITEM, col, &HDItem, , "ahk_id " hwnd)
    SendMessage(HDM_GETITEM, col, HDItem, , "ahk_id " hwnd)
    
    ; Assume that the filter text should then be contained in the variable 'string', no?
    L_Str := StrGet(filterText, filterTextLength, "UTF-16")
    ToolTip("Column index - " col "`n`nfilter = " L_Str )
}
/* typedef struct _HD_TEXTFILTERA {
  LPSTR pszText;
  INT   cchTextMax;
} HD_TEXTFILTERA, *LPHD_TEXTFILTERA;
*/
/* typedef struct _HD_ITEMA {
  UINT    mask;                                   ; 4
  int     cxy;                                    ; 4
  LPSTR   pszText;                                ; A_PtrSize
  HBITMAP hbm;                                    ; A_PtrSize
  int     cchTextMax;                             ; 4
  int     fmt;                                    ; 4
  LPARAM  lParam;                                 ; A_PtrSize
  int     iImage;                                 ; 4
  int     iOrder;                                 ; 4
  UINT    type;                                   ; A_PtrSize due to next one
  void    *pvFilter;                              ; A_PtrSize + A_PtrSize
  UINT    state;                                  ; 4
} HDITEMA, *LPHDITEMA;
just me
Posts: 9532
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [v2] ListView Filter Bar

23 Sep 2023, 11:30

Didn't check the whole code, but at least:

Code: Select all

    ...
    static HDItemSize := (4 * 6) + (A_PtrSize * 6)
    static typeOffset := (4 * 6) + (A_PtrSize * 3)
    ...
    static pvFilterOffset := typeOffset + A_PtrSize
    ...
User avatar
emmanuel d
Posts: 90
Joined: 17 Nov 2013, 04:45

Re: [v2] ListView Filter Bar

23 Sep 2023, 12:41

Thank you sir, I am sure that is correct what you are saying, adjusted it, but still. :(
Got the error, that my first parameter is incorrect. I tried buffer and Varsetstrcapacity and &varref and so but no luck

Code: Select all

    L_Str := StrGet(filterText, filterTextLength, "UTF-16")
i am just flying blind here :shifty: ;)
just me
Posts: 9532
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [v2] ListView Filter Bar

24 Sep 2023, 07:54

Two more changes should make it working:

Code: Select all

--- 
    NumPut("Int", filterTextLength,  HDTEXTFILTER, A_PtrSize) ; buffer size    <<< changed  8 to A_PtrSize
...
    L_Str := StrGet(StrPtr(filterText), filterTextLength, "UTF-16")  ; added StrPtr()
...
Good luck!
just me
Posts: 9532
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [v2] ListView Filter Bar

25 Sep 2023, 03:27

Working skript:

Code: Select all

#Requires Autohotkey v2.0.9
#Warn
Win := Gui()
LV := Win.AddListView("w400 h400", ["Col1","Col2","Col3"])
LV.Header := SendMessage(0x101F, 0, 0, LV.Hwnd) ; LVM_GETHEADER - get hwnd of Header control
DllCall("UxTheme.dll\SetWindowTheme", "Ptr", LV.Hwnd, "WStr", "Explorer", "Ptr", 0) ; Set 'Explorer' theme
ControlSetStyle("^0x100", LV.Header) ; toggle the HDS_FILTERBAR style
Loop(100) ; populate ListView
    LV.Add("", "Col 1 - Row " A_Index, "Col 2 - Row " A_Index, "Col 3 - Row " A_Index)
LV.ModifyCol()
LV.OnNotify(-312, LV_FilterChange) ; HDN_FILTERCHANGE = -312
Win.Show()
; ----------------------------------------------------------------------------------------------------------------------
LV_FilterChange(LV, LParam) {
   Static NMHDR_Size := A_PtrSize * 3,
          HDItemSize := (4 * 6) + (A_PtrSize * 6),
          TypeOffset := (4 * 6) + (A_PtrSize * 3),
          FilterOffset := TypeOffset + A_PtrSize
   Local  FilterText := "",
          FilterTextLength := 256,                             ; max 256 characters
          Col := NumGet(LParam, NMHDR_Size, "Int")             ; get the current column index
   VarSetStrCapacity(&FilterText, FilterTextLength)            ; String buffer for HDTEXTFILTER struct
   Local HDTEXTFILTER := Buffer(A_PtrSize * 2, 0)              ; HDTEXTFILTER struct -----------------------------------
   NumPut("Ptr", StrPtr(FilterText), HDTEXTFILTER, 0)          ; add pointer to string buffer variable
   NumPut("Int", FilterTextLength  , HDTEXTFILTER, A_PtrSize)  ; buffer size
   Local HDItem := Buffer(HDItemSize, 0)                       ; HDITEM struct -----------------------------------------
   NumPut("UInt", 0x100           , HDItem, 0)                 ; Set the Mask to HDI_FILTER := 0x100
   NumPut("UInt", 0x0             , HDItem, TypeOffset)        ; Set the Type to HDFT_ISSTRING := 0x0
   NumPut("Ptr" , HDTEXTFILTER.Ptr, HDItem, FilterOffset)      ; Add pointer to HDTEXTFILTER struct
   ; Send message to get the item
   SendMessage(0x120B, Col, HDItem.Ptr, LV.Header)             ; HDM_GETITEM = 0x120B
   VarSetStrCapacity(&FilterText, -1)                          ; update the internally-stored string length
   ToolTip("Column index - " Col "`n`nFilter = " FilterText)
}

/* typedef struct _HD_TEXTFILTERW {
  LPWSTR  pszText;                                ; A_PtrSize
  INT     cchTextMax;                             ; A_PtrSize due to previous one
} HD_TEXTFILTERA, *LPHD_TEXTFILTERA;
*/
/* typedef struct _HD_ITEMW {
  UINT    mask;                                   ; 4
  int     cxy;                                    ; 4
  LPWSTR  pszText;                                ; A_PtrSize
  HBITMAP hbm;                                    ; A_PtrSize
  int     cchTextMax;                             ; 4
  int     fmt;                                    ; 4
  LPARAM  lParam;                                 ; A_PtrSize
  int     iImage;                                 ; 4
  int     iOrder;                                 ; 4
  UINT    type;                                   ; A_PtrSize (4 + (A_PtrSize - 4)) due to alignment
  void    *pvFilter;                              ; A_PtrSize
  UINT    state;                                  ; A_PtrSize (4 + (A_PtrSize - 4)) due to alignment
} HDITEMA, *LPHDITEMA;
*/

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: diegg1739, kunkel321, teadrinker and 51 guests