DragListBox

  • Moin,

    weil in der letzten Zeit so viel über Drag&Drop in ListViews diskutiert wurde, kommt hier eine "entsprechende Funktion" für ListBoxen. Vergleichbare Lösungen gibt es bereits im "großen" Forum, allerdings nicht so kompakt. Ich hoffe, das Beispiel und die Kommentare in der Funktion sind Erklärung genug:

    Spoiler anzeigen
    [autoit]

    ; *********************************************************
    ; Das untenstehende Beispiel funktioniert nur im 32Bit-Mode
    ; *********************************************************
    #AutoIt3Wrapper_UseX64=n
    ; *********************************************************

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

    #include <GUIConstantsEx.au3>

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

    $hGUI = GUICreate("ListBox Drag&Drop", 300, 400)
    $idList = GUICtrlCreateList("", 20, 20, 260, 360)
    GUICtrlSetData(-1, "List1|List2|List3|List4|List5|List6")

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

    _DragListBox($idList, "Init", 0, 0)

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

    GUISetState()

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

    While True
    Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
    Exit
    EndSwitch
    WEnd

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

    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _DragListBox
    ; Description ...: ListBox-Einträge per Drag&Drop verschieben.
    ; Syntax.........: _DragListBox($hWnd, $iMsg, $wParam, $lParam)
    ; Parameters ....: $hWnd - HWND des Fensters - bei direktem Aufruf mit $iMsg = "Init" ID des List-Controls
    ; $iMsg - Nachrichtennummer - bei direktem Aufruf "Init"
    ; $wParam - WPARAM - bei direktem Aufruf 0 (wird ignoriert)
    ; $lParam - LPARAM - bei direktem Aufruf 0 (wird ignoriert)
    ; Return values .: Erfolg: - True
    ; Fehler: - False
    ; Author ........: Großvater (http://www.autoit.de)
    ; Modified.......:
    ; Remarks .......: Für jedes List-Control muss die Funktion einmal direkt mit den Parametern
    ; $hWnd = ID des List-Controls aus GuiCtrlCreateList()
    ; $iMsg = "Init"
    ; $wParam = egal
    ; $lParam = egal
    ; aufgerufen werden. Beim ersten direkten Aufruf wird die Funktion mit GUIRegisterMsg()
    ; als Messagehandler für die speziellen Drag&Drop-Nachrichten registriert.
    ; Related .......:
    ; Link ..........: http://msdn.microsoft.com/en-us/library/…28VS.85%29.aspx
    ; Example .......:
    ; ===============================================================================================================================
    Func _DragListBox($hWnd, $iMsg, $wParam, $lParam)
    Local Static $MSGINIT = "Init"
    Local Static $DRAGLISTMSGSTRING = "commctrl_DragListMsg"
    Local Static $DL_CURSORSET = 0
    Local Static $DL_STOPCURSOR = 1
    Local Static $DL_COPYCURSOR = 2
    Local Static $DL_MOVECURSOR = 3
    Local Static $DL_BEGINDRAG = 0x485
    Local Static $DL_DRAGGING = 0x486
    Local Static $DL_DROPPED = 0x487
    Local Static $DL_CANCELDRAG = 0x488
    Local Static $LB_INSERTSTRING = 0x181
    Local Static $LB_DELETESTRING = 0x182
    Local Static $LB_SETCURSEL = 0x186
    Local Static $LB_GETTEXT = 0x189
    Local Static $LB_GETTEXTLEN = 0x18A
    Local Static $DLMSG = 0
    Local Static $CTRLLIST = ""
    Local Static $LBITEMB = -1
    Local Static $LBITEME = -1
    Local $aResult, $idCtrl, $hCtrl, $CURSOR, $DRAGLISTINFO, $LBITEM, $LBTEXT, $Len, $Pos, $Notification, $X, $Y
    ; INIT -----------------------------------------------------------------------------------------------------------------------
    If $iMSG = $MSGINIT Then
    $idCtrl = $hWnd
    $hCtrl = GUICtrlGetHandle($hWnd)
    $CTRLLIST &= $idCtrl & "|" & $hCtrl & @LF
    $aResult = DllCall("Comctl32.dll", "BOOL", "MakeDragList", "HWND", $hCtrl)
    If @error Or $aResult[0] = 0 Then Return False
    If Not $DLMSG Then
    $aResult = DllCall("User32.dll", "UINT", "RegisterWindowMessageW", "WStr", $DRAGLISTMSGSTRING)
    If @error Or $aResult[0] = 0 Then Return False
    $DLMSG = $aResult[0]
    GUIRegisterMsg($DLMSG, "_DragListBox")
    EndIf
    Return True
    EndIf
    ; DRAGLISTINFO (lParam) -----------------------------------------------------------------------------------------------------
    $DRAGLISTINFO = DllStructCreate("UINT uNotification; HWND hWnd; LONG ptX; LONG ptY", $lParam)
    $Notification = DllStructGetData($DRAGLISTINFO, "uNotification")
    $hCtrl = HWnd(DllStructGetData($DRAGLISTINFO, "hWnd"))
    $X = DllStructGetData($DRAGLISTINFO, "ptX")
    $Y = DllStructGetData($DRAGLISTINFO, "ptY")
    $aResult = StringRegExp($CTRLLIST, "(?i)(?m)^([0-9a-fx]+)\|(" & $hCtrl & ")$", 1)
    If @error Then Return False
    $idCtrl = $aResult[0]
    $hCtrl = $aResult[1]
    $aResult = DllCall("Comctl32.dll", "INT", "LBItemFromPt","HWND", $hCtrl, "LONG", $X, "LONG", $Y, "BOOL", 1)
    If @error Then Return False
    $LBITEM = $aResult[0]
    ; Notification --------------------------------------------------------------------------------------------------------------
    Switch $Notification
    Case $DL_BEGINDRAG
    If $LBITEM = -1 Then Return False
    $LBITEMB = $LBITEM
    Return True
    Case $DL_DRAGGING
    $LBITEME = $LBITEM
    DllCall("Comctl32.dll", "None", "DrawInsert", "HWND", $hWnd, "HWND", $hCtrl, "INT", $LBITEME)
    If $LBITEME = -1 Then Return $DL_STOPCURSOR
    Return $DL_MOVECURSOR
    Case $DL_DROPPED
    DllCall("Comctl32.dll", "None", "DrawInsert", "HWND", $hWnd, "HWND", $hCtrl, "INT", -1)
    $LBITEME = $LBITEM
    If $LBITEME = -1 Or $LBITEMB = $LBITEME Then Return
    $Len = GUICtrlSendMsg($idCtrl, $LB_GETTEXTLEN, $LBITEMB, 0)
    $LBTEXT = DllStructCreate("WChar [" & $Len+1 & "]")
    GUICtrlSendMsg($idCtrl, $LB_GETTEXT, $LBITEMB, DllStructGetPtr($LBTEXT))
    GUICtrlSendMsg($idCtrl, $LB_DELETESTRING, $LBITEMB, 0)
    If $LBITEMB < $LBITEME Then $LBITEME -= 1
    $Pos = GUICtrlSendMsg($idCtrl, $LB_INSERTSTRING, $LBITEME, DllStructGetPtr($LBTEXT))
    GUICtrlSendMsg($idCtrl, $LB_SETCURSEL, $Pos, 0)
    Case $DL_CANCELDRAG
    DllCall("Comctl32.dll", "None", "DrawInsert", "HWND", $hWnd, "HWND", $hCtrl, "INT", -1)
    EndSwitch
    EndFunc

    [/autoit]

    2 Mal editiert, zuletzt von Großvater (7. Februar 2011 um 17:03)

    • Offizieller Beitrag

    @Großvater, 1A Funktion :thumbup:

    Deine Funktion hat aber noch einen kleinen Schönheitsfehler/Bug.
    Wenn man z.B. den ersten Eintrag per Drag n Drop an das Ende der Liste verschienen will klappt das nicht.
    Man kann immer nur bis zum vorletzen Eintrag verschieben. ;)

  • Oscar :
    Ich habe versucht, ein paar Parametertypen 64-Bit-fit zu machen. Kannst Du bitte nochmal probieren, ob es jetzt läuft?

    Raupi :
    Danke, der "Schönheitsfehler" ist von Herrn Gates so gewollt. Man kann den "netten" Einfügezeiger (DrawInsert) nicht hinter dem letzten Eintrag anzeigen lassen.

    • Offizieller Beitrag
    Zitat von Großvater

    Raupi :
    Danke, der "Schönheitsfehler" ist von Herrn Gates so gewollt. Man kann den "netten" Einfügezeiger (DrawInsert) nicht hinter dem letzten Eintrag anzeigen lassen.

    @Großvater, mit dem Einfügen am Ende kann man tricksen ;)

    Es ist deine Funktion, deshalb wurstel ich nicht daran herum. Schau dir mal das Beispiel an:

    Spoiler anzeigen
    [autoit]

    #include <Constants.au3>
    #include <GUIListBox.au3>
    #include <WinAPI.au3>
    #include <WindowsConstants.au3>

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

    Global $gnDRAGLISTMSGSTRING = _WinAPI_RegisterWindowMessage("commctrl_DragListMsg")
    Global $DL_BEGINDRAG = $WM_USER + 133
    Global $DL_DRAGGING = $WM_USER + 134
    Global $DL_DROPPED = $WM_USER + 135
    Global $DL_CANCELDRAG = $WM_USER + 136
    Global Enum $DL_STOPCURSOR = 1, $DL_COPYCURSOR, $DL_MOVECURSOR
    Global $gtDRAGLISTINFO = "long uNotification;long hWnd;long x;long y"
    Global $gfItemAdded = False

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

    Global $hMain = GUICreate("DragList", 200, 400)
    Global $cListbox = GUICtrlCreateList("", 16, 16, 168, 368, $WS_BORDER + $WS_VSCROLL)
    GUICtrlSetFont($cListbox, 10, Default, Default, "Tahoma")
    Global $hListbox = GUICtrlGetHandle($cListbox)
    GUICtrlSetData($cListbox, "Apples|Oranges|Bananas|Pears|Grapefruits|Limes|Lemons|Strawberries|Plums|Melons|Grapes|")
    GUISetState()

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

    _ComCtl32_MakeDragList($hListbox)

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

    Global $wProcNew = DllCallbackRegister("_MyWndProc", "int", "hwnd;int;wparam;lparam")
    Global $wProcOld = _WinAPI_SetWindowLong($hMain, $GWL_WNDPROC, DllCallbackGetPtr($wProcNew))

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

    While GUIGetMsg() <> -3
    Sleep(10)
    Wend

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

    Exit

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

    Func _MyWndProc($hWnd, $nMsg, $wParam, $lParam)
    Local $aRet, $nOldIndex, $sItemText
    If $nMsg = $gnDRAGLISTMSGSTRING Then
    Local $tDRAGLISTINFO = DllStructCreate($gtDRAGLISTINFO, $lParam)
    Local $uNotification = DllStructGetData($tDRAGLISTINFO, "uNotification")
    Local $x = DllStructGetData($tDRAGLISTINFO, "x"), $y = DllStructGetData($tDRAGLISTINFO, "y")
    Local $nItem = _ComCtl32_LBItemFromPt($hListbox, $x, $y)
    Switch $uNotification
    Case $DL_BEGINDRAG
    If $nItem < (_GUICtrlListBox_GetCount($hListbox) - 1) Then
    _GUICtrlListBox_AddString($hListbox, "")
    $gfItemAdded = True
    EndIf
    Return 1
    Case $DL_DRAGGING
    _ComCtl32_DrawInsert($hMain, $hListbox, $nItem)
    If $nItem = _GUICtrlListBox_GetCurSel($hListbox) Then Return $DL_STOPCURSOR
    Return $DL_MOVECURSOR
    Case $DL_DROPPED
    If $nItem > -1 Then
    $nOldIndex = _GUICtrlListBox_GetCurSel($hListbox)
    If $nItem <> $nOldIndex Then
    $sItemText = _GUICtrlListBox_GetText($hListbox, $nOldIndex)
    If $nItem < $nOldIndex Then $nOldIndex += 1
    _GUICtrlListBox_InsertString($hListbox, $sItemText, $nItem)
    _GUICtrlListBox_DeleteString($hListbox, $nOldIndex)
    If $nItem > $nOldIndex Then $nItem -= 1
    _GUICtrlListBox_SetCurSel($hListbox, $nItem)
    EndIf
    EndIf
    If $gfItemAdded Then
    _GUICtrlListBox_DeleteString($hListbox, _GUICtrlListBox_GetCount($hListbox) - 1)
    $gfItemAdded = False
    EndIF
    _ComCtl32_DrawInsert($hMain, $hListbox, -1)
    Return 0
    Case $DL_CANCELDRAG
    If $gfItemAdded Then
    _GUICtrlListBox_DeleteString($hListbox, _GUICtrlListBox_GetCount($hListbox) - 1)
    $gfItemAdded = False
    EndIF
    _ComCtl32_DrawInsert($hMain, $hListbox, -1)
    Return 0
    EndSwitch
    EndIf
    Return _WinAPI_CallWindowProc($wProcOld, $hWnd, $nMsg, $wParam, $lParam)
    EndFunc

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

    Func _ComCtl32_MakeDragList($hWnd)
    Local $aRet = DllCall("comctl32.dll", "int", "MakeDragList", "hwnd", $hWnd)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
    EndFunc

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

    Func _ComCtl32_LBItemFromPt($hWnd, $x, $y)
    Local $aRet = DllCall("comctl32.dll", "long", "LBItemFromPt", "hwnd", $hWnd, "long", $x, "long", $y, "long", 1)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
    EndFunc

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

    Func _ComCtl32_DrawInsert($hWndParent, $hWnd, $nItem)
    DllCall("comctl32.dll", "none", "DrawInsert", "hwnd", $hWndParent, "hwnd", $hWnd, "long", $nItem)
    If @error Then Return SetError(@error, @extended, 0)
    Return
    EndFunc

    [/autoit]


    Ich denke, da findest du die Lösung :thumbup:

    Edit: Ich glaub das DragList Control funzt generell nicht unter x64. Habe zumindest nichts bei Google gefunden, um es x64 fähig zu bekommen.

    Edit2: Das obrige Beispiel ist von hier: http://www.autoitscript.com/forum/topic/97…__1#entry701125

  • @Großvater, mit dem Einfügen am Ende kann man tricksen ;)


    Da hast Du wohl recht, ich verspüre aber manchmal keine Lust dazu, Mr. Gates nachzubessern. Wenn man sich z.B. die unterschiedlichen Notifications für das Drag&Drop bei ListBox und ListView ansieht, habe ich den Eindruck, dass es Mr. Gates nicht wirklich wichtig war, seine "Billigcontrols für die Masse" konsistent bedienbar zu machen.

    Oscar : Danke für den (erfolglosen) 64-Bit-Test. Ich habe es oben eingefügt. Ist es dem Wrapper eigentlich egal, wo im Skript seine Direktiven abgelegt sind?