I am trying to use a ListView control for a drag and drop event. I want to drag an item from position 1 to somewhere else...say, position 5 (there are no subitems). But when I do that it does nothing. But, actually, when I step through the code the remove
method removes the item. But it places right back in the same place so it looks like it does nothing. I needed to add the APIs according to here because it would always place it in the first position.
I got the code from here before researching and adding the API (which I thought was the issue) and tried to tailor it to my specific need, but I can't get it to work. I am running 32-bit Excel.
Global Constants and Handles
'Windows API Constants
Public Const LOGPIXELSX = 88
Public Const LOGPIXELSY = 90
'Windows API Function Declarations
'Get a handle to the Device Context (a drawing layer) for a window
Public Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
'Get the capabilities of a device, from its Device Context
Public Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, _
ByVal nIndex As Long) As Long
'Release the handle to the Device Context, to tidy up
Public Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, _
ByVal hDC As Long) As Long
Drag and Drop Event
Private Sub lvSortableColumn_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
Dim item As MSComctlLib.ListItem
Dim lngXPixelsPerInch As Long, lngYPixelsPerInch As Long
Dim lngDeviceHandle As Long
'We must determine the Pixels per Inch for the display device.
lngDeviceHandle = GetDC(0)
lngXPixelsPerInch = GetDeviceCaps(lngDeviceHandle, LOGPIXELSX)
lngYPixelsPerInch = GetDeviceCaps(lngDeviceHandle, LOGPIXELSY)
ReleaseDC 0, lngDeviceHandle
LVDragDropSingle lvSortableColumn, x * 1440 / lngXPixelsPerInch, y * 1440 / lngYPixelsPerInch
End Sub
Procedure
Public Sub LVDragDropSingle(ByRef lvList As ListView, ByVal x As Single, ByVal y As Single)
'Item being dropped
Dim objDrag As ListItem
'Item being dropped on
Dim objDrop As ListItem
'Item being readded to the list
Dim objNew As ListItem
'Drop position
Dim intIndex As Integer
'Retrieve the original items
Set objDrop = lvList.HitTest(x, y)
Set objDrag = lvList.SelectedItem
If (objDrop Is Nothing) Or (objDrag Is Nothing) Then
Set lvList.DropHighlight = Nothing
Set objDrop = Nothing
Set objDrag = Nothing
Exit Sub
End If
'Retrieve the drop position
intIndex = objDrop.Index
'Remove the dragged item
lvList.ListItems.Remove objDrag.Index
'Add it back into the dropped position
'Seems to fail on this line*****
Set objNew = lvList.ListItems.Add(intIndex, objDrag.Key, objDrag.Text) ', objDrag.Icon, objDrag.SmallIcon)
'Reselect the item
objNew.Selected = True
'Destroy all objects
Set objNew = Nothing
Set objDrag = Nothing
Set objDrop = Nothing
Set lvList.DropHighlight = Nothing
End Sub
EDIT
Just an additional piece of information that might prove helpful before my bounty runs out. If I place a stop in one of the events I notice that when I drag an item it immediately highlights the first item. I think this could be why it won't work. It does this same thing in other ListViews on other userforms. For example, if the end user clicks an item, that item highlights. But if he checks the checkbox directly without clicking the actual item it highlights a random item (usually the same one). There is some very strange behavior with the ListView control in VBA (as noted by a few people online).
@Brian I made your code to work in some crude way
1st of all changing Set objNew = lvList.ListItems.Add(intIndex, objDrag.Key, objDrag.Text)
to lvList.ListItems.Add intIndex, objDrag.Key, objDrag.Text
made it work. Also LvList.refresh
added at the end.
Then multpliying X & Y with 15 make drophighlight
to work in some crude way.
Further I used (20 as twips to point)
Xp = Application.ActiveWindow.PointsToScreenPixelsX(X * 20)
Yp = Application.ActiveWindow.PointsToScreenPixelsY(Y * 20)
and used Xp & Yp for HitTest. It gives more close result (but still not exactly). Xp & Yp are not declared and used as variant only. Declaring Xp Yp single will stop the conversion result to 0 as hittest X Y is single and PointstoScreen
is Long. Csng()
not working. My monitor is 1366 X 768.
Following are my observations (still not used in the program)
I used Private Declare Function GetSystemMetrics Lib "user32" (ByVal whichMetric As Long) As Long
successfully to get monitor width etc. Could not get gdi32 to work.
Xw = Application.ActiveWindow.UsableWidth
Yh = Application.ActiveWindow.UsableHeight
bringing in 1009.5 & 399. don't know what is the unit
Edit2: I forget to mention, I used your procedure code directly in OLEDragDrop event. I have also used OLEDragOver Event
Xp = Application.ActiveWindow.PointsToScreenPixelsX(X * 20)
Yp = Application.ActiveWindow.PointsToScreenPixelsY(Y * 20)
Set lvList.DropHighlight = lvList.HitTest(Xp, Yp)
If lvList.DropHighlight Is Nothing Then
Set lvList.DropHighlight = lvList.ListItems(lvList.ListItems.Count)
End If
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With