LVM_HITTEST ohne Erfolg

    • Offizieller Beitrag

    Hi,

    ich suche nach einer Möglichkeit direkt mit dem Klick (oder De-/Aktivieren per Spacebar) festzustellen ob eine Checkbox im Listview-Item gecheckt ist oder nicht. Dazu ist der Index des Listview-Item erforderlich.
    Wenn das Item selektiert ist, ist das kein Problem - aber ein Klick auf eine Checkbox eines nicht selektierten Item führt nicht zu dessen Selektion.
    Laut MSDN soll mit Senden der "LVM_HITTEST" Message der Index des Items, in dessen Bereich geklickt wurde ermittelt werden können. Ich habe das in folgendem Bsp.Skript mal nachvollzogen - aber als Index erhalte ich immer 0.

    Bitte keine evtl. Lösungsvorschläge, wie man das anders lösen könnte (es gibt keine wirklich sinnvollen ;) - nur riesig aufgeblähte Workarounds über sechs Ecken).

    Spoiler anzeigen
    [autoit]

    #include <GUIConstantsEx.au3>
    #include <GuiListView.au3>
    #include <ListViewConstants.au3>
    #include <StructureConstants.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $hGui = GUICreate('test')
    $hListView = GUICtrlCreateListView('Spalte1|Spalte2', 10, 10, 300, 200, BitOR($LVS_SHOWSELALWAYS,$LVS_REPORT), BitOR($LVS_EX_CHECKBOXES,$LVS_EX_FULLROWSELECT))
    _GUICtrlListView_SetColumnWidth($hListView, 0, 146)
    _GUICtrlListView_SetColumnWidth($hListView, 1, $LVSCW_AUTOSIZE_USEHEADER)
    For $i = 1 To 10
    GUICtrlCreateListViewItem('Zeile ' & $i & ' Spalte 1|Zeile ' & $i & ' Spalte 2', $hListView)
    Next
    GUISetState()
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    If $hWndFrom = $hWndListView Then
    Switch $iCode
    Case $LVN_ITEMCHANGED ; == Item geändert
    Local $old = Opt('MouseCoordMode', 2), $aPos = MouseGetPos(), $tPOINT = DllStructCreate($tagPOINT)
    Opt('MouseCoordMode', $old)
    DllStructSetData($tPOINT, 1, $aPos[0])
    DllStructSetData($tPOINT, 2, $aPos[1])
    Local $tITEM = DllStructCreate('struct; int; endstruct'), $tLVHITTESTINFO = DllStructCreate($tagLVHITTESTINFO)
    DllStructSetData($tLVHITTESTINFO, 1, DllStructGetPtr($tPOINT)) ; == Mauskoordinaten im Client
    DllStructSetData($tLVHITTESTINFO, 'Flags', $LVHT_ONITEMSTATEICON) ; == Flag für "The position is over the state image of a list-view item"
    DllStructSetData($tLVHITTESTINFO, 'Item', DllStructGetPtr($tITEM)) ; == Rückgabewert Listview Item-Index
    ; == LVM_HITTEST Message senden ( http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx )
    Local $ret = DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hGui, "uint", $LVM_HITTEST, "wparam", 0, "lparam", DllStructGetPtr($tLVHITTESTINFO))
    ;~ ConsoleWrite($ret[0] & ' err: ' & @error & @LF)
    ConsoleWrite('Index changed = ' & DllStructGetData($tITEM, 1) & @LF)
    EndSwitch
    EndIf
    Return $GUI_RUNDEFMSG
    EndFunc ;==>WM_NOTIFY

    [/autoit]
    • Offizieller Beitrag

    Warum benutzt du nicht

    [autoit]

    $Hit=_GUICtrlListView_HitTest($hWndListView, -1, -1)

    [/autoit]


    Das liefert dir das Item an der Mausposition zurück? Bei erfolg steht das item in $Hit[0]

  • Ich habe deine Version mal verbessert. Die LVHITTESTINFO Struct hat nur einen "in" Wert den du selbst festlegen musst, und zwar die Mausposition. Die musst du allerdings nicht in Form eines Pointers zu einer POINT Struct übergeben, sondern als Werte für die Elemente "X" und "Y" in der Hittest Struct selbst. Wenn du dir die Definition von $tagLVHITTESTINFO anschaust, dann siehst du, dass "X" und "Y" als ganz normale Elemente für die Mausposition angegeben sind.
    Alle anderen Elemente in LVHITTESTINFO kannst du vor dem Call ignorieren, weil die Werte zurückgeben und nicht annehmen. "Flags" gibst du nicht selbst an, sondern du wertest diesen Parameter aus, nachdem die Nachricht gesendet wurde.
    Weiterhin muss die Mausposition nicht relativ zur Client Area der GUI, sondern zu der Client Area des Listview Controls angegeben werden. Das geht am besten mit ScreenToClient.
    Auch die Nachricht selbst muss an das Handle es Listview Controls geschickt werden.
    Der Index des Items ist der Rückgabewert des DllCalls zu SendMessage und nicht der Wert des "Item" Elements im Hittest Struct.

    So funktioniert es ;):

    Spoiler anzeigen
    [autoit]

    #include <GUIConstantsEx.au3>
    #include <GuiListView.au3>
    #include <ListViewConstants.au3>
    #include <StructureConstants.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $hGui = GUICreate('test')
    $hListView = GUICtrlCreateListView('Spalte1|Spalte2', 10, 10, 300, 200, BitOR($LVS_SHOWSELALWAYS,$LVS_REPORT), BitOR($LVS_EX_CHECKBOXES,$LVS_EX_FULLROWSELECT))
    _GUICtrlListView_SetColumnWidth($hListView, 0, 146)
    _GUICtrlListView_SetColumnWidth($hListView, 1, $LVSCW_AUTOSIZE_USEHEADER)
    For $i = 1 To 10
    GUICtrlCreateListViewItem('Zeile ' & $i & ' Spalte 1|Zeile ' & $i & ' Spalte 2', $hListView)
    Next
    GUISetState()
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    If $hWndFrom = $hWndListView Then
    Switch $iCode
    Case $LVN_ITEMCHANGED ; == Item geändert
    Local $old = Opt('MouseCoordMode', 1), $aPos = MouseGetPos(), $tPOINT = DllStructCreate($tagPOINT)
    Opt('MouseCoordMode', $old)
    DllStructSetData($tPOINT, "X", $aPos[0])
    DllStructSetData($tPOINT, "Y", $aPos[1])
    DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWndListView, "struct*", $tPOINT)

    [/autoit] [autoit][/autoit] [autoit]

    Local $tLVHITTESTINFO = DllStructCreate($tagLVHITTESTINFO)
    DllStructSetData($tLVHITTESTINFO, "X", DllStructGetData($tPOINT, "X")) ; == Mauskoordinaten im Client
    DllStructSetData($tLVHITTESTINFO, "Y", DllStructGetData($tPOINT, "Y"))
    ; == LVM_HITTEST Message senden ( http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx )
    Local $ret = DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWndListView, "uint", $LVM_HITTEST, "wparam", 0, "struct*", $tLVHITTESTINFO)
    If BitAND(DllStructGetData($tLVHITTESTINFO, "Flags"), $LVHT_ONITEMSTATEICON) Then ConsoleWrite('Index changed = ' & $ret[0] & @LF)
    EndSwitch
    EndIf
    Return $GUI_RUNDEFMSG
    EndFunc ;==>WM_NOTIFY

    [/autoit]
    • Offizieller Beitrag

    Aus mir unerfindlichen Gründen läuft das Script von name22 bei mir nicht. Vielleicht weil ich immernoch 3.3.6.1 benutze :rolleyes:
    Hier meine Version:

    Spoiler anzeigen
    [autoit]

    #region - Timestamp
    ; 2013-04-17 21:47:43
    #endregion - Timestamp

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #include <GUIConstantsEx.au3>
    #include <GuiListView.au3>
    #include <ListViewConstants.au3>
    #include <StructureConstants.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $hGui = GUICreate('test')
    $hListView = GUICtrlCreateListView('Spalte1|Spalte2', 10, 10, 300, 200, BitOR($LVS_SHOWSELALWAYS, $LVS_REPORT), BitOR($LVS_EX_CHECKBOXES, $LVS_EX_FULLROWSELECT))
    _GUICtrlListView_SetColumnWidth($hListView, 0, 146)
    _GUICtrlListView_SetColumnWidth($hListView, 1, $LVSCW_AUTOSIZE_USEHEADER)
    For $i = 1 To 10
    GUICtrlCreateListViewItem('Zeile ' & $i & ' Spalte 1|Zeile ' & $i & ' Spalte 2', $hListView)
    Next
    GUISetState()
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    If $hWndFrom = $hWndListView Then
    Switch $iCode
    Case $LVN_ITEMCHANGED ; == Item geändert
    Local $tPoint = _WinAPI_GetMousePos(True, $hWndListView);Mausposition innerhalb des Clients
    Local $tLVHITTESTINFO = DllStructCreate($tagLVHITTESTINFO)
    DllStructSetData($tLVHITTESTINFO, "X", DllStructGetData($tPoint, "X"))
    DllStructSetData($tLVHITTESTINFO, "Y", DllStructGetData($tPoint, "Y"))
    Local $iRet = _SendMessage($hWndFrom, $LVM_HITTEST, 0, DllStructGetPtr($tLVHITTESTINFO))
    If @error Or $iRet = -1 Then Return $GUI_RUNDEFMSG ; -1 = not on lv item, otherwise returns index
    If BitAND(DllStructGetData($tLVHITTESTINFO, "Flags"), $LVHT_ONITEMSTATEICON) Then ConsoleWrite('Index changed = ' & DllStructGetData($tLVHITTESTINFO, "Item") & @LF)
    EndSwitch
    EndIf
    Return $GUI_RUNDEFMSG
    EndFunc ;==>WM_NOTIFY

    [/autoit]

    Btw, mit _WinAPI_GetMousePos spart man sich den Aufruf von Opt, MouseGetPos und die Umwandlung in Clientkoordinaten ;)

    Zitat von name22

    Der Index des Items ist der Rückgabewert des DllCalls zu SendMessage und nicht der Wert des "Item" Elements im Hittest Struct.


    Sagt wer? Das steht auch in Item ;)

    • Offizieller Beitrag

    Ich sollte mal langsam auf einem Recher ne neuere Version installieren :whistling:

    Meine Version ist dann was für Nostalgiker :D

    • Offizieller Beitrag

    Raupi, deinen Post dazwischen hatte ich glatt übersehen. :whistling:
    Habe aber jetzt mal deinen Tipp umgesetzt und zusätzlich erweitert auf Prüfung De-/Aktivierung einer Checkbox per Spacebar.
    Nun ist es so, wie ich es wollte - fein. 8o

    Spoiler anzeigen
    [autoit]

    #include <GUIConstantsEx.au3>
    #include <GuiListView.au3>
    #include <ListViewConstants.au3>
    #include <StructureConstants.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $hGui = GUICreate('test')
    $hListView = GUICtrlCreateListView('Spalte1|Spalte2', 10, 10, 300, 200, BitOR($LVS_SHOWSELALWAYS,$LVS_REPORT), BitOR($LVS_EX_CHECKBOXES,$LVS_EX_FULLROWSELECT))
    _GUICtrlListView_SetColumnWidth($hListView, 0, 146)
    _GUICtrlListView_SetColumnWidth($hListView, 1, $LVSCW_AUTOSIZE_USEHEADER)
    For $i = 1 To 10
    GUICtrlCreateListViewItem('Zeile ' & $i & ' Spalte 1|Zeile ' & $i & ' Spalte 2', $hListView)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    GUISetState()
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

    [/autoit] [autoit][/autoit] [autoit]

    Do
    Until GUIGetMsg() = -3

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView
    $hWndListView = $hListView
    If Not IsHWnd($hListView) Then $hWndListView = GUICtrlGetHandle($hListView)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iCode = DllStructGetData($tNMHDR, "Code")
    If $hWndFrom = $hWndListView Then
    Switch $iCode
    Case $LVN_ITEMCHANGED ; == Item geändert
    Local $aHit = _GUICtrlListView_HitTest($hWndListView)
    If $aHit[0] > -1 And $aHit[4] Then
    ConsoleWrite('Klick-Index ' & $aHit[0] & ' Check: ' & _GUICtrlListView_GetItemChecked($hWndListView, $aHit[0]) & @LF)
    EndIf
    Case $LVN_KEYDOWN ; == Checkbox per Spacebar De-/Aktiviert
    Local $tInfo = DllStructCreate($tagNMLVKEYDOWN, $ilParam)
    If DllStructGetData($tInfo, "VKey") = 32 Then
    Local $iIndex = _GUICtrlListView_GetSelectedIndices($hWndListView)
    ConsoleWrite('Space-Index ' & $iIndex & ' Check: ' & Not _GUICtrlListView_GetItemChecked($hWndListView, $iIndex) & @LF)
    EndIf
    EndSwitch
    EndIf
    Return $GUI_RUNDEFMSG
    EndFunc ;==>WM_NOTIFY

    [/autoit]
    • Offizieller Beitrag

    Jaja, ich werd mal wieder übersehen. X(;(

    Hab mal dein Script in meine Sammlung aufgenommen, kann ich irgendwann brauchen :thumbup:

  • Raupi Ich hab deinen Edit erst jetzt gesehen ^^. Ja stimmt, das steht auch in Item. Ich glaube ich habe entweder bei MSDN falsch gelesen, oder das DllStruct beim testen falsch ausgelesen. Am Anfang hat das nämlich nicht geklappt, da muss ich wohl irgendwas übersehen haben.