Detect Enter key presses on Listview and Treeview controls

Put simple Tips and Tricks that are not entire Tutorials in this forum
just me
Posts: 9579
Joined: 02 Oct 2013, 08:51
Location: Germany

Detect Enter key presses on Listview and Treeview controls

27 May 2024, 06:41

Method #1:

Like documented:

ListView:
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:

Code: Select all

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"))
}
TreeView:
To detect when the user has pressed Enter while a TreeView has focus, use a default button (which can be hidden if desired). For example:

Code: Select all

MyGui.Add("Button", "Hidden Default", "OK").OnEvent("Click", ButtonOK)
...
ButtonOK(*) {
    global
    if MyGui.FocusedCtrl != TV
        return
    MsgBox("Enter was pressed. The selected item ID is " TV.GetSelection())
}
just me
Posts: 9579
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Detect Enter key presses on Listview and Treeview controls

27 May 2024, 06:42

Method #2:

By processing the WM_KEYDOWN message. (This will be much more convenient with AHK v2.1 which will add control specific OnMessage processing).

Code: Select all

#Requires AutoHotkey v2.0
Win := Gui( , "LV WantReturn")
Win.OnEvent("Close", (*) => ExitApp())
Win.AddText("xm", "ListView:")
LV := Win.AddListView("xm  y+5 w400 r5 -ReadOnly", ["Column 1" , "Column 2"])
LV.OnNotify(-4, LV_NM_RETURN)
LV_TV_WantReturn.Register(LV)
Loop 5
   LV.Add("", "Row " . A_Index . " Column 1", "Row " . A_Index . " Column 1")
LV.ModifyCol()
Win.AddText("xm", "TreeView:")
TV := Win.AddTreeView("xm y+5 w400 r6 -ReadOnly")
Win.AddText("xm", "Edit:")
EDT := Win.AddEdit("xm y+5 w400 r3")
Win.AddText("xm", "Listbox:")
LB  := Win.AddListBox("xm y+5 w400 r3", [1, 2, 3])
BTN := Win.AddButton("w400 +Default", "Default Button")
BTN.OnEvent("Click", (*) => MsgBox("Button clicked!"))
Win.Show()

LV_NM_RETURN(LV, *) {
   MsgBox("Enter was pressed on registered ListView!")
}

; ======================================================================================================================
; LV_TV_WantReturn (OnMessage)
;     'Fakes' Return key processing for ListView and Treeview controls which otherwise won't process it.
;     If enabled, the control will receive a NM_RETURN (-4) notification whenever the Return key is pressed while
;     the control has the focus.
; Usage:
;     To register a control call the functions once and pass the Gui.Control object as the first and only parameter.
;     To deregister it, call the function again with the same Gui.Control object as the first and only parameter.
; ----------------------------------------------------------------------------------------------------------------------
; NM_RETURN    -> https://learn.microsoft.com/en-us/windows/win32/controls/nm-return-list-view-
;              -> https://learn.microsoft.com/en-us/windows/win32/controls/nm-return-tree-view-
; WM_KEYDOWN   -> https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
; ======================================================================================================================
Class LV_TV_WantReturn {
   Static Ctrls := Map()
   Static OnMsg := False
   Static Callback := ObjBindMethod(LV_TV_WantReturn, "OnKeyDown")
   ; -------------------------------------------------------------------------------------------------------------------
   Static Register(CtrlObj) {
      If (Type(CtrlObj) = "Gui.ListView") || (Type(CtrlObj) = "Gui.TreeView") {
         If This.Ctrls.Has(CtrlObj.Hwnd) { ; the control is already registered, remove it
            This.Ctrls.Delete(CtrlObj.Hwnd)
            If ((This.Ctrls.Count = 0) && This.OnMsg) {
               OnMessage(0x0100, This.Callback, 0)
               This.OnMsg := False
            }
            Return True
         }
         This.Ctrls[CtrlObj.Hwnd] := {CID: DllCall("GetDlgCtrlID", "Ptr", CtrlObj.Hwnd, "Int"), HGUI: CtrlObj.Gui.Hwnd}
         If !(This.OnMsg) {
            OnMessage(0x0100, This.Callback, -1)
            This.OnMsg := True
         }
         Return True
      }
      Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   Static OnKeyDown(wParam, lParam, Msg, HWND) {
      Static NMHDR := Buffer(24, 0) ; NMHDR structure 64-bit
      Static NM_RETURN := -4
      If (wParam = 13) && This.Ctrls.Has(HWND) {
         If !(lParam & 0x40000000) { ; don't send notifications for auto-repeated keydown events
            NumPut("Ptr", HWND, "Ptr", This.Ctrls[HWND].CID, "Ptr", NM_RETURN, NMHDR)
            PostMessage(0x004E, This.Ctrls[HWND].CID, NMHDR.Ptr, This.Ctrls[HWND].HGUI)
         }
         Return 0
      }
   }
}
Last edited by just me on 27 May 2024, 10:04, edited 2 times in total.
just me
Posts: 9579
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Detect Enter key presses on Listview and Treeview controls

27 May 2024, 06:42

Method #3:

By subclassing. Using this example, you can use a default subclassproc or pass an own subclassproc when registering the control.

Code: Select all

#Requires AutoHotkey v2.0
Win := Gui( , "LV WantReturn")
Win.OnEvent("Close", (*) => ExitApp())
Win.AddText("xm", "ListView:")
LV := Win.AddListView("xm  y+5 w400 r5 -ReadOnly", ["Column 1" , "Column 2"])
LV.OnNotify(-4, LV_NM_RETURN)
LV_TV_WantReturnSC.Register(LV) ; register the LV
Loop 5
   LV.Add("", "Row " . A_Index . " Column 1", "Row " . A_Index . " Column 1")
LV.ModifyCol()
Win.AddText("xm", "TreeView:")
TV := Win.AddTreeView("xm y+5 w400 r6 -ReadOnly")
TV.OnNotify(-4, TV_NM_RETURN)
LV_TV_WantReturnSC.Register(TV, TV_SubClassProc) ; register an own subclassproc for the TV
Win.AddText("xm", "Edit:")
EDT := Win.AddEdit("xm y+5 w400 r3")
Win.AddText("xm", "Listbox:")
LB  := Win.AddListBox("xm y+5 w400 r3", [1, 2, 3])
BTN := Win.AddButton("w400 +Default", "Default Button")
BTN.OnEvent("Click", (*) => MsgBox("Button clicked!"))
Win.Show()
; ----------------------------------------------------------------------------------------------------------------------
LV_NM_RETURN(LV, *) {
   MsgBox("Enter was pressed on registered ListView!")
}
; ----------------------------------------------------------------------------------------------------------------------
TV_NM_RETURN(LV, *) {
   MsgBox("Enter was pressed on registered TreeView!")
}
; ----------------------------------------------------------------------------------------------------------------------
TV_SubClassProc(HWND, Msg, wParam, lParam, ID, Data) {
   Static NMHDR := Buffer(24, 0) ; NMHDR structure 64-bit
   Static NM_RETURN := -4
   Switch Msg {
      Case 135: ; WM_GETDLGCODE (0x0087)
         If (wParam = 13) ; VK_RETURN
            Return 4 ; DLGC_WANTALLKEYS
      Case 256: ; WM_KEYDOWN (0x0100)
         If (wParam = 13) { ; VK_RETURN
            If !(lParam & 0x40000000) { ; not auto-repetition
               Local Ctl := GuiCtrlFromHwnd(Hwnd)
               Local CID := GetDlgCtrlID(Hwnd)
               NumPut("Ptr", HWND, "Ptr", CID, "Ptr", NM_RETURN, NMHDR)
               PostMessage(0x004E, CID, NMHDR.Ptr, Ctl.Gui.Hwnd) ; WM_NOTIFY
            }
            Return 0
         }
      Case 2:   ; WM_DESTROY (0x0002)
         DllCall("RemoveWindowSubclass", "Ptr", HWND, "Ptr", Data, "UPtr", Hwnd)
   }
   Return DllCall("DefSubclassProc", "Ptr", HWND, "UInt", Msg, "Ptr", wParam, "Ptr", lParam, "Ptr")
   ; -------------------------------------------------------------------------------------------------------------------
   GetDlgCtrlID(Hwnd) => DllCall("GetDlgCtrlID", "Ptr", Hwnd, "Int")
}
; ======================================================================================================================
; LV_TV_WantReturnSC (SubClassing)
;     'Fakes' Return key processing for ListView and Treeview controls which otherwise won't process it.
;     If enabled, the control will receive a NM_RETURN (-4) notification whenever the Return key is pressed while
;     the control has the focus.
; Usage:
;     To register a control call the functions once and pass the Gui.Control object as the first parameter.
;     To deregister it, call the function again with the same Gui.Control object as the first parameter.
;     You may pass a function object in the second parameterto be used as subclassproc for the specified control.
;     This function must accept 6 parameters. Look at the built-in SubClassProc method for further details.
; ----------------------------------------------------------------------------------------------------------------------
; NM_RETURN    -> https://learn.microsoft.com/en-us/windows/win32/controls/nm-return-list-view-
;              -> https://learn.microsoft.com/en-us/windows/win32/controls/nm-return-tree-view-
; WM_KEYDOWN   -> https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
; ======================================================================================================================
Class LV_TV_WantReturnSC {
   Static Ctrls := Map()
   Static SCProc := CallbackCreate(ObjBindMethod(LV_TV_WantReturnSC, "SubClassProc"), , 6)
   ; -------------------------------------------------------------------------------------------------------------------
   Static Register(CtrlObj, SubClassProc?) {
      If (Type(CtrlObj) = "Gui.ListView") || (Type(CtrlObj) = "Gui.TreeView") {
         Local Hwnd := CtrlObj.Hwnd, SCP := 0
         If This.Ctrls.Has(Hwnd) { ; the control is already registered, remove it
            DllCall("RemoveWindowSubclass", "Ptr", Hwnd, "Ptr", This.Ctrls[Hwnd].SCP, "UPtr", Hwnd, "Int")
            This.Ctrls.Delete(Hwnd)
            Return True
         }
         SCP  := IsSet(SubClassProc) && IsObject(SubClassProc) ? CallbackCreate(SubClassProc, , 6) : This.SCProc
         This.Ctrls[CtrlObj.Hwnd] := {CID: This.GetDlgCtrlID(Hwnd), HGUI: CtrlObj.Gui.Hwnd, SCP: SCP}
         Return DllCall("SetWindowSubclass", "Ptr", Hwnd, "Ptr", SCP, "Ptr", Hwnd, "Ptr", SCP, "UInt")
      }
      Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   Static SubClassProc(HWND, Msg, wParam, lParam, ID, Data) {
      Static NMHDR := Buffer(24, 0) ; NMHDR structure 64-bit
      Static NM_RETURN := -4
      Switch Msg {
         Case 135: ; WM_GETDLGCODE (0x0087)
            If (wParam = 13) ; VK_RETURN
               Return 4 ; DLGC_WANTALLKEYS
         Case 256: ; WM_KEYDOWN (0x0100)
            If (wParam = 13) { ; VK_RETURN
               Local Ctl := This.Ctrls[Hwnd]
               If !(lParam & 0x40000000) { ; not auto-repetition
                  NumPut("Ptr", HWND, "Ptr", Ctl.CID, "Ptr", NM_RETURN, NMHDR)
                  PostMessage(0x004E, Ctl.CID, NMHDR.Ptr, Ctl.HGUI) ; WM_NOTIFY
               }
               Return 0
            }
         Case 2:   ; WM_DESTROY (0x0002)
            DllCall("RemoveWindowSubclass", "Ptr", HWND, "Ptr", Data, "UPtr", Hwnd)
      }
      Return DllCall("DefSubclassProc", "Ptr", HWND, "UInt", Msg, "Ptr", wParam, "Ptr", lParam, "Ptr")
   }
   ; -------------------------------------------------------------------------------------------------------------------
   Static GetDlgCtrlID(Hwnd) => DllCall("GetDlgCtrlID", "Ptr", Hwnd, "Int")
}
Last edited by just me on 28 May 2024, 03:18, edited 1 time in total.
just me
Posts: 9579
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Detect Enter key presses on Listview and Treeview controls

28 May 2024, 03:19

Added code for subclass method #3.
User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: Detect Enter key presses on Listview and Treeview controls

29 May 2024, 12:18

@just me FYI that method #2's example is missing the line to register the TreeView control.
just me
Posts: 9579
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Detect Enter key presses on Listview and Treeview controls

30 May 2024, 00:57

@kczx3, that's intended (just to show the difference). ;)

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 3 guests