Jump to content
Sign in to follow this  
Achilles

Drag and rearrange listview items

Recommended Posts

Achilles

I'm trying to make a list where you can drag and drop and pretty much arrange your list just by moving items yourself (no sort). However, so far I can't do any of that. Any listview I try creating doesn't allow me to do that.. I looked at all the other _GUICtrlListView_* functions but they don't seem to have the answer. I searched, but closest thing to what I wanted was this page... which is two years old and has a bunch of constants autoit no longer recognizes.

Just try to starting with _GUICtrlListView_Create.. it has the constants I referred to above commented out..


My Programs[list][*]Knight Media Player[*]Multiple Desktops[*]Daily Comics[*]Journal[/list]

Share this post


Link to post
Share on other sites
GEOSoft

I couldn't get that working either so i settled for a couple of buttons (Up and Down) to move an item. I wrote a function Named GUIListViewItem_Swap() to handle it. Here is some example code for it that I submited. Hope it's of some use to you.

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


GUICreate("ListView Item Swap", 300, 200)
Global $hListView = GUICtrlCreateListView("Column1|Column2|Column3", 10, 10, 280, 140)
For $i = 0 to 3
    GUICtrlCreateListViewItem("Item " & $i & "|" & "SubItem " & $i & "-2|Subitem " & $i & "-3", $hListView)
Next
$Btn_Up = GUICtrlCreateButton("Up", 100, 160, 40, 30)
GUICtrlSetState(-1, $GUI_DISABLE)
$Btn_Dn = GUICtrlCreateButton("Down", 160, 160, 40, 30)
GUICtrlSetState(-1, $GUI_DISABLE)

GUISetState()
_GUICtrlListView_RegisterSortCallBack($hListView)
While 1
    $i_Item = Number(_GUICtrlListView_GetSelectedIndices($hListView))

    If _GUICtrlListView_GetSelectedIndices($hListView) = "" Then
        If BitAND(GUICtrlGetState($Btn_Dn), $GUI_ENABLE) = $GUI_ENABLE Then
            GUICtrlSetState($Btn_Dn, $GUI_DISABLE)
            GUICtrlSetState($Btn_Up, $GUI_DISABLE)
        EndIf
    Else
        If BitAND(GUICtrlGetState($Btn_Dn), $GUI_DISABLE) = $GUI_DISABLE Then
            GUICtrlSetState($Btn_Dn, $GUI_ENABLE)
            GUICtrlSetState($Btn_Up, $GUI_ENABLE)
        EndIf
    EndIf
    $msg = GUIGetMsg()
    Switch $msg
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $hListView
            _GUICtrlListView_SortItems($hListView, GUICtrlGetState($hListView))
        Case $Btn_Dn
            If $i_Item = _GUICtrlListView_GetItemCount($hListView) -1 Then ContinueLoop
            _GUICtrlListView_ItemSwap($hListView, $i_Item, $i_Item + 1)
            _GUICtrlListView_SetItemSelected($hListView, $i_Item + 1)
            GUICtrlSetState($hListView, $GUI_FOCUS)
        Case $Btn_Up
            If $i_Item = 0 Then ContinueLoop
            _GUICtrlListView_ItemSwap($hListView, $i_Item, $i_Item - 1)
            _GUICtrlListView_SetItemSelected($hListView, $i_Item - 1)
            GUICtrlSetState($hListView, $GUI_FOCUS)
    EndSwitch
WEnd

_GUICtrlListView_UnRegisterSortCallBack($hListView)
GUIDelete()

Func _GUICtrlListView_ItemSwap($hListView, $i_Index, $i_NewIndex)
    $i_Index = Number($i_Index)
    $i_NewIndex = Number($i_NewIndex)
    Local $aOrig = _GUICtrlListView_GetItemTextArray($hListView, $i_Index)
    Local $aSwap = _GUICtrlListView_GetItemTextArray($hListView, $i_NewIndex)
    For $i = 1 To UBound($aOrig) - 1
        _GUICtrlListView_SetItemText($hListView, $i_NewIndex, $aOrig[$i], $i - 1)
        _GUICtrlListView_SetItemText($hListView, $i_Index, $aSwap[$i], $i - 1)
    Next
EndFunc   ;==>_GUICtrlListView_ItemSwap

George

Question about decompiling code? Read the decompiling FAQ and don't bother posting the question in the forums.

Be sure to read and follow the forum rules. -AKA the AutoIt Reading and Comprehension Skills test.***

The PCRE (Regular Expression) ToolKit for AutoIT - (Updated Oct 20, 2011 ver:3.0.1.13) - Please update your current version before filing any bug reports. The installer now includes both 32 and 64 bit versions. No change in version number.

Visit my Blog .. currently not active but it will soon be resplendent with news and views. Also please remove any links you may have to my website. it is soon to be closed and replaced with something else.

"Old age and treachery will always overcome youth and skill!"

Share this post


Link to post
Share on other sites
Achilles

I couldn't get that working either so i settled for a couple of buttons (Up and Down) to move an item. I wrote a function Named GUIListViewItem_Swap() to handle it. Here is some example code for it that I submited. Hope it's of some use to you.

<code>

Hmm, that will work... I still wish there was a nice simple way to drag them for order... Thanks for putting this together, I'll definitely use it if no knows how to do it using the mouse.. It's a fairly basic part of windows listviews so I figured it wouldn't be too complicated, but I guess I was wrong.. Well, on second thought maybe not... maybe I'm just use to foobar letting me move stuff how I want.

My Programs[list][*]Knight Media Player[*]Multiple Desktops[*]Daily Comics[*]Journal[/list]

Share this post


Link to post
Share on other sites
picaxe

Here's a quick hack of the _GUIImageList_Drag* help file example (with some small tweeks). You would probably want to remove the image list code.

Opt("MustDeclareVars", 1)
#include <GuiConstantsEx.au3>
#include <GuiListView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#Include <SendMessage.au3>

Global $hDragImageList, $h_ListView, $bDragging = False, $LV_Height, $fNoImage
Global $a_index[2] ; from and to

Global Const $DebugIt = 0

Opt("WinTitleMatchMode", 2)

Local Const $image_width = 20
Local Const $image_height = 20
Local $h_images, $main_GUI, $iIndex, $sTitle = "Listview Drag & Drop Rearrange"

Switch MsgBox(262144 + 4, $sTitle, '"Yes" for listview with icons' & @LF & '"No" for standard listview')
 Case 6
 $fNoImage = False
 Case 7
 $fNoImage = True
EndSwitch

$main_GUI = GUICreate($sTitle, 350, 300)
$h_ListView = _GUICtrlListView_Create($main_GUI, "Entry Name|Category", 5, 10, 340, 280, -1, BitOR($WS_EX_CLIENTEDGE, $WS_EX_STATICEDGE))
$LV_Height = 280 - 75
_GUICtrlListView_SetColumnWidth($h_ListView, 0, 160)
_GUICtrlListView_SetColumnWidth($h_ListView, 1, 160)
If $fNoImage Then
 _GUICtrlListView_SetExtendedListViewStyle($h_ListView, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT))
Else
 _GUICtrlListView_SetExtendedListViewStyle($h_ListView, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT, $LVS_EX_CHECKBOXES))
 _GUICtrlListView_SetExtendedListViewStyle($h_ListView, BitOR($LVM_SETEXTENDEDLISTVIEWSTYLE, $LVS_EX_SUBITEMIMAGES))
EndIf
;------------------------------------------------------
; Using subitem images
;------------------------------------------------------

If Not $fNoImage Then
 $h_images = _GUIImageList_Create($image_width, $image_height, 5, 1)
 For $x = 1 To 21
 _GUIImageList_AddIcon($h_images, @SystemDir & "\shell32.dll", $x)
 Next
 _GUICtrlListView_SetImageList($h_ListView, $h_images, $LVSIL_SMALL)
EndIf

;Register WM_NOTIFY events
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUIRegisterMsg($WM_LBUTTONUP, "WM_LBUTTONUP")
GUIRegisterMsg($WM_MOUSEMOVE, "WM_MOUSEMOVE")
;GUIRegisterMsg($WM_SIZE, "WM_SIZE")
Local $y = 1
For $x = 0 To 9
 $iIndex = _GUICtrlListView_AddItem($h_ListView, "Name " & $x + 1, $y) ; handle, string, image index
 _GUICtrlListView_AddSubItem($h_ListView, $iIndex, "Category " & $x + 1, 1, $y + 1) ; handle, index, string, subitem, image index
 $y += 2
Next
GUISetState()
While 1
 Switch GUIGetMsg()
 ;This case statement exits and updates code if needed
 Case $GUI_EVENT_CLOSE
     ExitLoop
     ;put all the misc. stuff here
 Case Else
     ;;;
 EndSwitch
WEnd
_GUIImageList_Destroy($h_images)
GUIDelete()

Func _LVInsertItem($i_FromItem, $i_ToItem)
 Local $struct_LVITEM = DllStructCreate($tagLVITEM)
 If @error Then Return SetError(-1, -1, -1)
 Local $struct_String = DllStructCreate("char Buffer[4096]")
 If @error Then Return SetError(-1, -1, -1)
 Local $sBuffer_pointer = DllStructGetPtr($struct_String)
 DllStructSetData($struct_LVITEM, "Mask", BitOR($LVIF_STATE, $LVIF_IMAGE, $LVIF_INDENT, $LVIF_PARAM, $LVIF_TEXT))
 DllStructSetData($struct_LVITEM, "StateMask", $LVIS_STATEIMAGEMASK)
 DllStructSetData($struct_LVITEM, "Item", $i_FromItem)
 DllStructSetData($struct_LVITEM, "SubItem", 0)
 DllStructSetData($struct_LVITEM, "TextMax", 4096)
 DllStructSetData($struct_LVITEM, "Text", $sBuffer_pointer)
 _GUICtrlListView_GetItemEx($h_ListView, $struct_LVITEM)
 If @error Then Return SetError(-1, -1, -1)
 Local $item_state = DllStructGetData($struct_LVITEM, "State")
 DllStructSetData($struct_LVITEM, "Item", $i_ToItem)
;~ Local $aItemText = _GUICtrlListView_GetItem($h_ListView, $i_FromItem) ; DllStructGetData($struct_String, "Buffer") only gets first letter possible bug ?
 Local $i_newIndex = _GUICtrlListView_InsertItem($h_ListView, _GUICtrlListView_GetItemText($h_ListView, $i_FromItem), $i_ToItem, DllStructGetData($struct_LVITEM, "Image"))
 If @error Then Return SetError(-1, -1, -1)
 If $DebugIt Then _DebugPrint("$i_newIndex = " & $i_newIndex)
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_STATE)
 DllStructSetData($struct_LVITEM, "Item", $i_newIndex)
 DllStructSetData($struct_LVITEM, "State", $item_state)
 DllStructSetData($struct_LVITEM, "StateMask", $LVIS_STATEIMAGEMASK)
 _GUICtrlListView_SetItemState($h_ListView, $i_newIndex, $item_state, $LVIS_STATEIMAGEMASK)
 If @error Then Return SetError(-1, -1, -1)
 Return $i_newIndex
EndFunc ;==>_LVInsertItem

Func _LVCopyItem($i_FromItem, $i_ToItem, $i_SubItem = 0)
 Local $struct_LVITEM = DllStructCreate($tagLVITEM)
 Local $struct_String = DllStructCreate("char Buffer[4096]")
 Local $sBuffer_pointer = DllStructGetPtr($struct_String)
 ; get from item info
 DllStructSetData($struct_LVITEM, "Mask", BitOR($LVIF_STATE, $LVIF_IMAGE, $LVIF_INDENT, $LVIF_PARAM, $LVIF_TEXT))
 DllStructSetData($struct_LVITEM, "StateMask", $LVIS_STATEIMAGEMASK)
 DllStructSetData($struct_LVITEM, "Item", $i_FromItem)
 DllStructSetData($struct_LVITEM, "SubItem", $i_SubItem)
 DllStructSetData($struct_LVITEM, "TextMax", 4096)
 DllStructSetData($struct_LVITEM, "Text", $sBuffer_pointer)
 _GUICtrlListView_GetItemEx($h_ListView, $struct_LVITEM)

 ; set to
 DllStructSetData($struct_LVITEM, "Item", $i_ToItem)
 ; set text
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_TEXT)
 DllStructSetData($struct_LVITEM, "Text", $sBuffer_pointer)
 DllStructSetData($struct_LVITEM, "TextMax", 4096)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
 If @error Then Return SetError(@error, @error, @error)
 ; set status
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_STATE)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
 ; set image
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_IMAGE)
 DllStructSetData($struct_LVITEM, "State", $LVIF_IMAGE)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
 ; set state
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_STATE)
 DllStructSetData($struct_LVITEM, "State", $LVIF_STATE)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
 ; set indent
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_INDENT)
 DllStructSetData($struct_LVITEM, "State", $LVIF_INDENT)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
 ; set Param
 DllStructSetData($struct_LVITEM, "Mask", $LVIF_PARAM)
 DllStructSetData($struct_LVITEM, "State", $LVIF_PARAM)
 _GUICtrlListView_SetItemEx($h_ListView, $struct_LVITEM)
EndFunc ;==>_LVCopyItem

Func ListView_Click()
 ;----------------------------------------------------------------------------------------------
 If $DebugIt Then _DebugPrint("$NM_CLICK")
 ;----------------------------------------------------------------------------------------------
EndFunc ;==>ListView_Click

Func ListView_DoubleClick()
 ;----------------------------------------------------------------------------------------------
 If $DebugIt Then _DebugPrint("$NM_DBLCLK")
 ;----------------------------------------------------------------------------------------------
EndFunc ;==>ListView_DoubleClick

Func WM_MOUSEMOVE($hWndGUI, $MsgID, $wParam, $lParam)
 #forceref $MsgID, $wParam
 If $bDragging = False Then Return $GUI_RUNDEFMSG
 Local $lpos = ControlGetPos($hWndGUI, "", $h_ListView)
 Local $x = BitAND($lParam, 0xFFFF) - $lpos[0]
 Local $y = BitShift($lParam, 16) - $lpos[1]
 If $y > $LV_Height - 20 Then
 _GUICtrlListView_Scroll($h_ListView, 0, $y)
 ElseIf $y < 20 Then
 _GUICtrlListView_Scroll($h_ListView, 0, $y * - 1)
 EndIf
 _GUIImageList_DragMove($x, $y)
 Return $GUI_RUNDEFMSG
EndFunc ;==>WM_MOUSEMOVE

Func WM_LBUTTONUP($hWndGUI, $MsgID, $wParam, $lParam)
 #forceref $MsgID, $wParam
 $bDragging = False
 Local $lpos = ControlGetPos($hWndGUI, "", $h_ListView)
 Local $x = BitAND($lParam, 0xFFFF) - $lpos[0]
 Local $y = BitShift($lParam, 16) - $lpos[1]
 If $DebugIt Then _DebugPrint("$x = " & $x)
 If $DebugIt Then _DebugPrint("$y = " & $y)
 _GUIImageList_DragLeave($h_ListView)
 _GUIImageList_EndDrag()
 ;_GUIImageList_Destroy($hDragImageList[0])
 If IsArray($hDragImageList) Then _GUIImageList_Destroy($hDragImageList[0]) ; gw
 _WinAPI_ReleaseCapture()
 Local $struct_LVHITTESTINFO = DllStructCreate($tagLVHITTESTINFO)

 DllStructSetData($struct_LVHITTESTINFO, "X", $x)
 DllStructSetData($struct_LVHITTESTINFO, "Y", $y)
 $a_index[1] = _SendMessage($h_ListView, $LVM_HITTEST, 0, DllStructGetPtr($struct_LVHITTESTINFO), 0, "wparam", "ptr")
 Local $flags = DllStructGetData($struct_LVHITTESTINFO, "Flags")
 If $DebugIt Then _DebugPrint("$flags: " & $flags)
;~ // Out of the ListView?
 If $a_index[1] = -1 Then Return $GUI_RUNDEFMSG
;~ // Not in an item?
 If BitAND($flags, $LVHT_ONITEMLABEL) = 0 And BitAND($flags, $LVHT_ONITEMSTATEICON) = 0 And BitAND($flags, $LVHT_ONITEMICON) = 0 Then Return $GUI_RUNDEFMSG
 ;If $a_index[0] < $a_index[1] - 1 Or $a_index[0] > $a_index[1] + 1 Then ; make sure insert is at least 2 items above or below, don't want to create a duplicate
 If $a_index[0] < $a_index[1] Or $a_index[0] > $a_index[1] Then
 If $DebugIt Then _DebugPrint("To = " & $a_index[1])
 Local $i_newIndex = _LVInsertItem($a_index[0], $a_index[1])
 If @error Then Return SetError(-1, -1, $GUI_RUNDEFMSG)
 Local $From_index = $a_index[0]
 If $a_index[0] > $a_index[1] Then $From_index = $a_index[0] + 1
 For $x = 1 To _GUICtrlListView_GetColumnCount($h_ListView) - 1
     _LVCopyItem($From_index, $i_newIndex, $x)
     If @error Then Return SetError(-1, -1, $GUI_RUNDEFMSG)
 Next
 _GUICtrlListView_DeleteItem($h_ListView, $From_index)
 EndIf
 Return $GUI_RUNDEFMSG
EndFunc ;==>WM_LBUTTONUP

; WM_NOTIFY event handler
Func WM_NOTIFY($hWndGUI, $MsgID, $wParam, $lParam)
 #forceref $hWndGUI, $MsgID, $wParam
 Local $tNMHDR, $code, $x, $y, $tNMLISTVIEW, $hwndFrom, $tDraw, $dwDrawStage, $dwItemSpec
 $tNMHDR = DllStructCreate($tagNMHDR, $lParam) ;NMHDR (hwndFrom, idFrom, code)
 If @error Then Return
 $code = DllStructGetData($tNMHDR, "Code")
 $hwndFrom = DllStructGetData($tNMHDR, "hWndFrom")
 Switch $hwndFrom
 Case $h_ListView
     Switch $code
         Case $LVN_BEGINDRAG
             If $DebugIt Then _DebugPrint("$LVN_BEGINDRAG")
             $x = BitAND($lParam, 0xFFFF)
             $y = BitShift($lParam, 16)
             $tNMLISTVIEW = DllStructCreate($tagNMLISTVIEW, $lParam)
             $a_index[0] = DllStructGetData($tNMLISTVIEW, "Item")
             $hDragImageList = _GUICtrlListView_CreateDragImage($h_ListView, $a_index[0])
             If @error Then Return SetError(-1, -1, $GUI_RUNDEFMSG)
             _GUIImageList_BeginDrag($hDragImageList[0], 0, 0, 0)
             If @error Then Return SetError(-1, -1, $GUI_RUNDEFMSG)
             If $DebugIt Then _DebugPrint("From = " & $a_index[0])
             ;_GUIImageList_DragEnter($h_ListView, $x, $y) ; commenting out this shows dragged image ?
             _WinAPI_SetCapture($hWndGUI)
             $bDragging = True
         Case $NM_CUSTOMDRAW
             If $DebugIt Then _DebugPrint("$NM_CUSTOMDRAW")
             $tDraw = DllStructCreate($tagNMLVCUSTOMDRAW, $lParam)
             $dwDrawStage = DllStructGetData($tDraw, "dwDrawStage")
             $dwItemSpec = DllStructGetData($tDraw, "dwItemSpec")
             Switch $dwDrawStage
                 Case $CDDS_PREPAINT
                     If $DebugIt Then _DebugPrint("$CDDS_PREPAINT")
                     Return $CDRF_NOTIFYITEMDRAW
                 Case $CDDS_ITEMPREPAINT
                     If $DebugIt Then _DebugPrint("$CDDS_ITEMPREPAINT")
;~  If BitAND($dwItemSpec, 1) = 1 Then
;~  DllStructSetData($tDraw, "clrTextBk", $CLR_AQUA)
;~  Else
;~  DllStructSetData($tDraw, "clrTextBk", $CLR_WHITE)
;~  EndIf
                     Return $CDRF_NEWFONT
             EndSwitch
     EndSwitch
 EndSwitch
 Return $GUI_RUNDEFMSG
EndFunc ;==>WM_NOTIFY

Func _DebugPrint($s_text)
 $s_text = StringReplace($s_text, @LF, @LF & "-->")
 ConsoleWrite("!===========================================================" & @LF & _
     "+===========================================================" & @LF & _
     "-->" & $s_text & @LF & _
     "+===========================================================" & @LF)
EndFunc ;==>_DebugPrint

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.